vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php line 232

  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\ORM\Mapping;
  4. use Doctrine\Common\EventManager;
  5. use Doctrine\DBAL\Platforms;
  6. use Doctrine\DBAL\Platforms\AbstractPlatform;
  7. use Doctrine\DBAL\Platforms\MySQLPlatform;
  8. use Doctrine\DBAL\Platforms\SqlitePlatform;
  9. use Doctrine\DBAL\Platforms\SQLServerPlatform;
  10. use Doctrine\Deprecations\Deprecation;
  11. use Doctrine\ORM\EntityManagerInterface;
  12. use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
  13. use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
  14. use Doctrine\ORM\Events;
  15. use Doctrine\ORM\Exception\ORMException;
  16. use Doctrine\ORM\Id\AssignedGenerator;
  17. use Doctrine\ORM\Id\BigIntegerIdentityGenerator;
  18. use Doctrine\ORM\Id\IdentityGenerator;
  19. use Doctrine\ORM\Id\SequenceGenerator;
  20. use Doctrine\ORM\Id\UuidGenerator;
  21. use Doctrine\ORM\Mapping\Exception\CannotGenerateIds;
  22. use Doctrine\ORM\Mapping\Exception\InvalidCustomGenerator;
  23. use Doctrine\ORM\Mapping\Exception\UnknownGeneratorType;
  24. use Doctrine\Persistence\Mapping\AbstractClassMetadataFactory;
  25. use Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface;
  26. use Doctrine\Persistence\Mapping\Driver\MappingDriver;
  27. use Doctrine\Persistence\Mapping\ReflectionService;
  28. use ReflectionClass;
  29. use ReflectionException;
  30. use function assert;
  31. use function class_exists;
  32. use function count;
  33. use function end;
  34. use function explode;
  35. use function get_class;
  36. use function in_array;
  37. use function is_subclass_of;
  38. use function str_contains;
  39. use function strlen;
  40. use function strtolower;
  41. use function substr;
  42. /**
  43.  * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
  44.  * metadata mapping information of a class which describes how a class should be mapped
  45.  * to a relational database.
  46.  *
  47.  * @extends AbstractClassMetadataFactory<ClassMetadata>
  48.  * @psalm-import-type AssociationMapping from ClassMetadata
  49.  * @psalm-import-type EmbeddedClassMapping from ClassMetadata
  50.  * @psalm-import-type FieldMapping from ClassMetadata
  51.  */
  52. class ClassMetadataFactory extends AbstractClassMetadataFactory
  53. {
  54.     /** @var EntityManagerInterface|null */
  55.     private $em;
  56.     /** @var AbstractPlatform|null */
  57.     private $targetPlatform;
  58.     /** @var MappingDriver */
  59.     private $driver;
  60.     /** @var EventManager */
  61.     private $evm;
  62.     /** @var mixed[] */
  63.     private $embeddablesActiveNesting = [];
  64.     /** @return void */
  65.     public function setEntityManager(EntityManagerInterface $em)
  66.     {
  67.         $this->em $em;
  68.     }
  69.     /**
  70.      * {@inheritDoc}
  71.      */
  72.     protected function initialize()
  73.     {
  74.         $this->driver      $this->em->getConfiguration()->getMetadataDriverImpl();
  75.         $this->evm         $this->em->getEventManager();
  76.         $this->initialized true;
  77.     }
  78.     /**
  79.      * {@inheritDoc}
  80.      */
  81.     protected function onNotFoundMetadata($className)
  82.     {
  83.         if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
  84.             return null;
  85.         }
  86.         $eventArgs = new OnClassMetadataNotFoundEventArgs($className$this->em);
  87.         $this->evm->dispatchEvent(Events::onClassMetadataNotFound$eventArgs);
  88.         $classMetadata $eventArgs->getFoundMetadata();
  89.         assert($classMetadata instanceof ClassMetadata || $classMetadata === null);
  90.         return $classMetadata;
  91.     }
  92.     /**
  93.      * {@inheritDoc}
  94.      */
  95.     protected function doLoadMetadata($class$parent$rootEntityFound, array $nonSuperclassParents)
  96.     {
  97.         if ($parent) {
  98.             $class->setInheritanceType($parent->inheritanceType);
  99.             $class->setDiscriminatorColumn($parent->discriminatorColumn);
  100.             $class->setIdGeneratorType($parent->generatorType);
  101.             $this->addInheritedFields($class$parent);
  102.             $this->addInheritedRelations($class$parent);
  103.             $this->addInheritedEmbeddedClasses($class$parent);
  104.             $class->setIdentifier($parent->identifier);
  105.             $class->setVersioned($parent->isVersioned);
  106.             $class->setVersionField($parent->versionField);
  107.             $class->setDiscriminatorMap($parent->discriminatorMap);
  108.             $class->addSubClasses($parent->subClasses);
  109.             $class->setLifecycleCallbacks($parent->lifecycleCallbacks);
  110.             $class->setChangeTrackingPolicy($parent->changeTrackingPolicy);
  111.             if (! empty($parent->customGeneratorDefinition)) {
  112.                 $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition);
  113.             }
  114.             if ($parent->isMappedSuperclass) {
  115.                 $class->setCustomRepositoryClass($parent->customRepositoryClassName);
  116.             }
  117.         }
  118.         // Invoke driver
  119.         try {
  120.             $this->driver->loadMetadataForClass($class->getName(), $class);
  121.         } catch (ReflectionException $e) {
  122.             throw MappingException::reflectionFailure($class->getName(), $e);
  123.         }
  124.         // If this class has a parent the id generator strategy is inherited.
  125.         // However this is only true if the hierarchy of parents contains the root entity,
  126.         // if it consists of mapped superclasses these don't necessarily include the id field.
  127.         if ($parent && $rootEntityFound) {
  128.             $this->inheritIdGeneratorMapping($class$parent);
  129.         } else {
  130.             $this->completeIdGeneratorMapping($class);
  131.         }
  132.         if (! $class->isMappedSuperclass) {
  133.             if ($rootEntityFound && $class->isInheritanceTypeNone()) {
  134.                 Deprecation::trigger(
  135.                     'doctrine/orm',
  136.                     'https://github.com/doctrine/orm/pull/10431',
  137.                     "Entity class '%s' is a subclass of the root entity class '%s', but no inheritance mapping type was declared. This is a misconfiguration and will be an error in Doctrine ORM 3.0.",
  138.                     $class->name,
  139.                     end($nonSuperclassParents)
  140.                 );
  141.             }
  142.             foreach ($class->embeddedClasses as $property => $embeddableClass) {
  143.                 if (isset($embeddableClass['inherited'])) {
  144.                     continue;
  145.                 }
  146.                 if (isset($this->embeddablesActiveNesting[$embeddableClass['class']])) {
  147.                     throw MappingException::infiniteEmbeddableNesting($class->name$property);
  148.                 }
  149.                 $this->embeddablesActiveNesting[$class->name] = true;
  150.                 $embeddableMetadata $this->getMetadataFor($embeddableClass['class']);
  151.                 if ($embeddableMetadata->isEmbeddedClass) {
  152.                     $this->addNestedEmbeddedClasses($embeddableMetadata$class$property);
  153.                 }
  154.                 $identifier $embeddableMetadata->getIdentifier();
  155.                 if (! empty($identifier)) {
  156.                     $this->inheritIdGeneratorMapping($class$embeddableMetadata);
  157.                 }
  158.                 $class->inlineEmbeddable($property$embeddableMetadata);
  159.                 unset($this->embeddablesActiveNesting[$class->name]);
  160.             }
  161.         }
  162.         if ($parent) {
  163.             if ($parent->isInheritanceTypeSingleTable()) {
  164.                 $class->setPrimaryTable($parent->table);
  165.             }
  166.             $this->addInheritedIndexes($class$parent);
  167.             if ($parent->cache) {
  168.                 $class->cache $parent->cache;
  169.             }
  170.             if ($parent->containsForeignIdentifier) {
  171.                 $class->containsForeignIdentifier true;
  172.             }
  173.             if ($parent->containsEnumIdentifier) {
  174.                 $class->containsEnumIdentifier true;
  175.             }
  176.             if (! empty($parent->namedQueries)) {
  177.                 $this->addInheritedNamedQueries($class$parent);
  178.             }
  179.             if (! empty($parent->namedNativeQueries)) {
  180.                 $this->addInheritedNamedNativeQueries($class$parent);
  181.             }
  182.             if (! empty($parent->sqlResultSetMappings)) {
  183.                 $this->addInheritedSqlResultSetMappings($class$parent);
  184.             }
  185.             if (! empty($parent->entityListeners) && empty($class->entityListeners)) {
  186.                 $class->entityListeners $parent->entityListeners;
  187.             }
  188.         }
  189.         $class->setParentClasses($nonSuperclassParents);
  190.         if ($class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {
  191.             $this->addDefaultDiscriminatorMap($class);
  192.         }
  193.         // During the following event, there may also be updates to the discriminator map as per GH-1257/GH-8402.
  194.         // So, we must not discover the missing subclasses before that.
  195.         if ($this->evm->hasListeners(Events::loadClassMetadata)) {
  196.             $eventArgs = new LoadClassMetadataEventArgs($class$this->em);
  197.             $this->evm->dispatchEvent(Events::loadClassMetadata$eventArgs);
  198.         }
  199.         $this->findAbstractEntityClassesNotListedInDiscriminatorMap($class);
  200.         if ($class->changeTrackingPolicy === ClassMetadata::CHANGETRACKING_NOTIFY) {
  201.             Deprecation::trigger(
  202.                 'doctrine/orm',
  203.                 'https://github.com/doctrine/orm/issues/8383',
  204.                 'NOTIFY Change Tracking policy used in "%s" is deprecated, use deferred explicit instead.',
  205.                 $class->name
  206.             );
  207.         }
  208.         $this->validateRuntimeMetadata($class$parent);
  209.     }
  210.     /**
  211.      * Validate runtime metadata is correctly defined.
  212.      *
  213.      * @param ClassMetadata               $class
  214.      * @param ClassMetadataInterface|null $parent
  215.      *
  216.      * @return void
  217.      *
  218.      * @throws MappingException
  219.      */
  220.     protected function validateRuntimeMetadata($class$parent)
  221.     {
  222.         if (! $class->reflClass) {
  223.             // only validate if there is a reflection class instance
  224.             return;
  225.         }
  226.         $class->validateIdentifier();
  227.         $class->validateAssociations();
  228.         $class->validateLifecycleCallbacks($this->getReflectionService());
  229.         // verify inheritance
  230.         if (! $class->isMappedSuperclass && ! $class->isInheritanceTypeNone()) {
  231.             if (! $parent) {
  232.                 if (count($class->discriminatorMap) === 0) {
  233.                     throw MappingException::missingDiscriminatorMap($class->name);
  234.                 }
  235.                 if (! $class->discriminatorColumn) {
  236.                     throw MappingException::missingDiscriminatorColumn($class->name);
  237.                 }
  238.                 foreach ($class->subClasses as $subClass) {
  239.                     if ((new ReflectionClass($subClass))->name !== $subClass) {
  240.                         throw MappingException::invalidClassInDiscriminatorMap($subClass$class->name);
  241.                     }
  242.                 }
  243.             } else {
  244.                 assert($parent instanceof ClassMetadataInfo); // https://github.com/doctrine/orm/issues/8746
  245.                 if (
  246.                     ! $class->reflClass->isAbstract()
  247.                     && ! in_array($class->name$class->discriminatorMaptrue)
  248.                 ) {
  249.                     throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name$class->rootEntityName);
  250.                 }
  251.             }
  252.         } elseif ($class->isMappedSuperclass && $class->name === $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) {
  253.             // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
  254.             throw MappingException::noInheritanceOnMappedSuperClass($class->name);
  255.         }
  256.     }
  257.     /**
  258.      * {@inheritDoc}
  259.      */
  260.     protected function newClassMetadataInstance($className)
  261.     {
  262.         return new ClassMetadata(
  263.             $className,
  264.             $this->em->getConfiguration()->getNamingStrategy(),
  265.             $this->em->getConfiguration()->getTypedFieldMapper()
  266.         );
  267.     }
  268.     /**
  269.      * Adds a default discriminator map if no one is given
  270.      *
  271.      * If an entity is of any inheritance type and does not contain a
  272.      * discriminator map, then the map is generated automatically. This process
  273.      * is expensive computation wise.
  274.      *
  275.      * The automatically generated discriminator map contains the lowercase short name of
  276.      * each class as key.
  277.      *
  278.      * @throws MappingException
  279.      */
  280.     private function addDefaultDiscriminatorMap(ClassMetadata $class): void
  281.     {
  282.         $allClasses $this->driver->getAllClassNames();
  283.         $fqcn       $class->getName();
  284.         $map        = [$this->getShortName($class->name) => $fqcn];
  285.         $duplicates = [];
  286.         foreach ($allClasses as $subClassCandidate) {
  287.             if (is_subclass_of($subClassCandidate$fqcn)) {
  288.                 $shortName $this->getShortName($subClassCandidate);
  289.                 if (isset($map[$shortName])) {
  290.                     $duplicates[] = $shortName;
  291.                 }
  292.                 $map[$shortName] = $subClassCandidate;
  293.             }
  294.         }
  295.         if ($duplicates) {
  296.             throw MappingException::duplicateDiscriminatorEntry($class->name$duplicates$map);
  297.         }
  298.         $class->setDiscriminatorMap($map);
  299.     }
  300.     private function findAbstractEntityClassesNotListedInDiscriminatorMap(ClassMetadata $rootEntityClass): void
  301.     {
  302.         // Only root classes in inheritance hierarchies need contain a discriminator map,
  303.         // so skip for other classes.
  304.         if (! $rootEntityClass->isRootEntity() || $rootEntityClass->isInheritanceTypeNone()) {
  305.             return;
  306.         }
  307.         $processedClasses = [$rootEntityClass->name => true];
  308.         foreach ($rootEntityClass->subClasses as $knownSubClass) {
  309.             $processedClasses[$knownSubClass] = true;
  310.         }
  311.         foreach ($rootEntityClass->discriminatorMap as $declaredClassName) {
  312.             // This fetches non-transient parent classes only
  313.             $parentClasses $this->getParentClasses($declaredClassName);
  314.             foreach ($parentClasses as $parentClass) {
  315.                 if (isset($processedClasses[$parentClass])) {
  316.                     continue;
  317.                 }
  318.                 $processedClasses[$parentClass] = true;
  319.                 // All non-abstract entity classes must be listed in the discriminator map, and
  320.                 // this will be validated/enforced at runtime (possibly at a later time, when the
  321.                 // subclass is loaded, but anyways). Also, subclasses is about entity classes only.
  322.                 // That means we can ignore non-abstract classes here. The (expensive) driver
  323.                 // check for mapped superclasses need only be run for abstract candidate classes.
  324.                 if (! (new ReflectionClass($parentClass))->isAbstract() || $this->peekIfIsMappedSuperclass($parentClass)) {
  325.                     continue;
  326.                 }
  327.                 // We have found a non-transient, non-mapped-superclass = an entity class (possibly abstract, but that does not matter)
  328.                 $rootEntityClass->addSubClass($parentClass);
  329.             }
  330.         }
  331.     }
  332.     /** @param class-string $className */
  333.     private function peekIfIsMappedSuperclass(string $className): bool
  334.     {
  335.         $reflService $this->getReflectionService();
  336.         $class       $this->newClassMetadataInstance($className);
  337.         $this->initializeReflection($class$reflService);
  338.         $this->driver->loadMetadataForClass($className$class);
  339.         return $class->isMappedSuperclass;
  340.     }
  341.     /**
  342.      * Gets the lower-case short name of a class.
  343.      *
  344.      * @psalm-param class-string $className
  345.      */
  346.     private function getShortName(string $className): string
  347.     {
  348.         if (! str_contains($className'\\')) {
  349.             return strtolower($className);
  350.         }
  351.         $parts explode('\\'$className);
  352.         return strtolower(end($parts));
  353.     }
  354.     /**
  355.      * Puts the `inherited` and `declared` values into mapping information for fields, associations
  356.      * and embedded classes.
  357.      *
  358.      * @param AssociationMapping|EmbeddedClassMapping|FieldMapping $mapping
  359.      */
  360.     private function addMappingInheritanceInformation(array &$mappingClassMetadata $parentClass): void
  361.     {
  362.         if (! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
  363.             $mapping['inherited'] = $parentClass->name;
  364.         }
  365.         if (! isset($mapping['declared'])) {
  366.             $mapping['declared'] = $parentClass->name;
  367.         }
  368.     }
  369.     /**
  370.      * Adds inherited fields to the subclass mapping.
  371.      */
  372.     private function addInheritedFields(ClassMetadata $subClassClassMetadata $parentClass): void
  373.     {
  374.         foreach ($parentClass->fieldMappings as $mapping) {
  375.             $this->addMappingInheritanceInformation($mapping$parentClass);
  376.             $subClass->addInheritedFieldMapping($mapping);
  377.         }
  378.         foreach ($parentClass->reflFields as $name => $field) {
  379.             $subClass->reflFields[$name] = $field;
  380.         }
  381.     }
  382.     /**
  383.      * Adds inherited association mappings to the subclass mapping.
  384.      *
  385.      * @throws MappingException
  386.      */
  387.     private function addInheritedRelations(ClassMetadata $subClassClassMetadata $parentClass): void
  388.     {
  389.         foreach ($parentClass->associationMappings as $field => $mapping) {
  390.             $this->addMappingInheritanceInformation($mapping$parentClass);
  391.             // When the class inheriting the relation ($subClass) is the first entity class since the
  392.             // relation has been defined in a mapped superclass (or in a chain
  393.             // of mapped superclasses) above, then declare this current entity class as the source of
  394.             // the relationship.
  395.             // According to the definitions given in https://github.com/doctrine/orm/pull/10396/,
  396.             // this is the case <=> ! isset($mapping['inherited']).
  397.             if (! isset($mapping['inherited'])) {
  398.                 $mapping['sourceEntity'] = $subClass->name;
  399.             }
  400.             $subClass->addInheritedAssociationMapping($mapping);
  401.         }
  402.     }
  403.     private function addInheritedEmbeddedClasses(ClassMetadata $subClassClassMetadata $parentClass): void
  404.     {
  405.         foreach ($parentClass->embeddedClasses as $field => $embeddedClass) {
  406.             $this->addMappingInheritanceInformation($embeddedClass$parentClass);
  407.             $subClass->embeddedClasses[$field] = $embeddedClass;
  408.         }
  409.     }
  410.     /**
  411.      * Adds nested embedded classes metadata to a parent class.
  412.      *
  413.      * @param ClassMetadata $subClass    Sub embedded class metadata to add nested embedded classes metadata from.
  414.      * @param ClassMetadata $parentClass Parent class to add nested embedded classes metadata to.
  415.      * @param string        $prefix      Embedded classes' prefix to use for nested embedded classes field names.
  416.      */
  417.     private function addNestedEmbeddedClasses(
  418.         ClassMetadata $subClass,
  419.         ClassMetadata $parentClass,
  420.         string $prefix
  421.     ): void {
  422.         foreach ($subClass->embeddedClasses as $property => $embeddableClass) {
  423.             if (isset($embeddableClass['inherited'])) {
  424.                 continue;
  425.             }
  426.             $embeddableMetadata $this->getMetadataFor($embeddableClass['class']);
  427.             $parentClass->mapEmbedded(
  428.                 [
  429.                     'fieldName' => $prefix '.' $property,
  430.                     'class' => $embeddableMetadata->name,
  431.                     'columnPrefix' => $embeddableClass['columnPrefix'],
  432.                     'declaredField' => $embeddableClass['declaredField']
  433.                             ? $prefix '.' $embeddableClass['declaredField']
  434.                             : $prefix,
  435.                     'originalField' => $embeddableClass['originalField'] ?: $property,
  436.                 ]
  437.             );
  438.         }
  439.     }
  440.     /**
  441.      * Copy the table indices from the parent class superclass to the child class
  442.      */
  443.     private function addInheritedIndexes(ClassMetadata $subClassClassMetadata $parentClass): void
  444.     {
  445.         if (! $parentClass->isMappedSuperclass) {
  446.             return;
  447.         }
  448.         foreach (['uniqueConstraints''indexes'] as $indexType) {
  449.             if (isset($parentClass->table[$indexType])) {
  450.                 foreach ($parentClass->table[$indexType] as $indexName => $index) {
  451.                     if (isset($subClass->table[$indexType][$indexName])) {
  452.                         continue; // Let the inheriting table override indices
  453.                     }
  454.                     $subClass->table[$indexType][$indexName] = $index;
  455.                 }
  456.             }
  457.         }
  458.     }
  459.     /**
  460.      * Adds inherited named queries to the subclass mapping.
  461.      */
  462.     private function addInheritedNamedQueries(ClassMetadata $subClassClassMetadata $parentClass): void
  463.     {
  464.         foreach ($parentClass->namedQueries as $name => $query) {
  465.             if (! isset($subClass->namedQueries[$name])) {
  466.                 $subClass->addNamedQuery(
  467.                     [
  468.                         'name'  => $query['name'],
  469.                         'query' => $query['query'],
  470.                     ]
  471.                 );
  472.             }
  473.         }
  474.     }
  475.     /**
  476.      * Adds inherited named native queries to the subclass mapping.
  477.      */
  478.     private function addInheritedNamedNativeQueries(ClassMetadata $subClassClassMetadata $parentClass): void
  479.     {
  480.         foreach ($parentClass->namedNativeQueries as $name => $query) {
  481.             if (! isset($subClass->namedNativeQueries[$name])) {
  482.                 $subClass->addNamedNativeQuery(
  483.                     [
  484.                         'name'              => $query['name'],
  485.                         'query'             => $query['query'],
  486.                         'isSelfClass'       => $query['isSelfClass'],
  487.                         'resultSetMapping'  => $query['resultSetMapping'],
  488.                         'resultClass'       => $query['isSelfClass'] ? $subClass->name $query['resultClass'],
  489.                     ]
  490.                 );
  491.             }
  492.         }
  493.     }
  494.     /**
  495.      * Adds inherited sql result set mappings to the subclass mapping.
  496.      */
  497.     private function addInheritedSqlResultSetMappings(ClassMetadata $subClassClassMetadata $parentClass): void
  498.     {
  499.         foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
  500.             if (! isset($subClass->sqlResultSetMappings[$name])) {
  501.                 $entities = [];
  502.                 foreach ($mapping['entities'] as $entity) {
  503.                     $entities[] = [
  504.                         'fields'                => $entity['fields'],
  505.                         'isSelfClass'           => $entity['isSelfClass'],
  506.                         'discriminatorColumn'   => $entity['discriminatorColumn'],
  507.                         'entityClass'           => $entity['isSelfClass'] ? $subClass->name $entity['entityClass'],
  508.                     ];
  509.                 }
  510.                 $subClass->addSqlResultSetMapping(
  511.                     [
  512.                         'name'          => $mapping['name'],
  513.                         'columns'       => $mapping['columns'],
  514.                         'entities'      => $entities,
  515.                     ]
  516.                 );
  517.             }
  518.         }
  519.     }
  520.     /**
  521.      * Completes the ID generator mapping. If "auto" is specified we choose the generator
  522.      * most appropriate for the targeted database platform.
  523.      *
  524.      * @throws ORMException
  525.      */
  526.     private function completeIdGeneratorMapping(ClassMetadataInfo $class): void
  527.     {
  528.         $idGenType $class->generatorType;
  529.         if ($idGenType === ClassMetadata::GENERATOR_TYPE_AUTO) {
  530.             $class->setIdGeneratorType($this->determineIdGeneratorStrategy($this->getTargetPlatform()));
  531.         }
  532.         // Create & assign an appropriate ID generator instance
  533.         switch ($class->generatorType) {
  534.             case ClassMetadata::GENERATOR_TYPE_IDENTITY:
  535.                 $sequenceName null;
  536.                 $fieldName    $class->identifier $class->getSingleIdentifierFieldName() : null;
  537.                 $platform     $this->getTargetPlatform();
  538.                 // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
  539.                 /** @psalm-suppress UndefinedClass, InvalidClass */
  540.                 if (! $platform instanceof MySQLPlatform && ! $platform instanceof SqlitePlatform && ! $platform instanceof SQLServerPlatform && $platform->usesSequenceEmulatedIdentityColumns()) {
  541.                     Deprecation::trigger(
  542.                         'doctrine/orm',
  543.                         'https://github.com/doctrine/orm/issues/8850',
  544.                         <<<'DEPRECATION'
  545. Context: Loading metadata for class %s
  546. Problem: Using the IDENTITY generator strategy with platform "%s" is deprecated and will not be possible in Doctrine ORM 3.0.
  547. Solution: Use the SEQUENCE generator strategy instead.
  548. DEPRECATION
  549.                             ,
  550.                         $class->name,
  551.                         get_class($this->getTargetPlatform())
  552.                     );
  553.                     $columnName     $class->getSingleIdentifierColumnName();
  554.                     $quoted         = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
  555.                     $sequencePrefix $class->getSequencePrefix($this->getTargetPlatform());
  556.                     $sequenceName   $this->getTargetPlatform()->getIdentitySequenceName($sequencePrefix$columnName);
  557.                     $definition     = [
  558.                         'sequenceName' => $this->truncateSequenceName($sequenceName),
  559.                     ];
  560.                     if ($quoted) {
  561.                         $definition['quoted'] = true;
  562.                     }
  563.                     $sequenceName $this
  564.                         ->em
  565.                         ->getConfiguration()
  566.                         ->getQuoteStrategy()
  567.                         ->getSequenceName($definition$class$this->getTargetPlatform());
  568.                 }
  569.                 $generator $fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint'
  570.                     ? new BigIntegerIdentityGenerator($sequenceName)
  571.                     : new IdentityGenerator($sequenceName);
  572.                 $class->setIdGenerator($generator);
  573.                 break;
  574.             case ClassMetadata::GENERATOR_TYPE_SEQUENCE:
  575.                 // If there is no sequence definition yet, create a default definition
  576.                 $definition $class->sequenceGeneratorDefinition;
  577.                 if (! $definition) {
  578.                     $fieldName    $class->getSingleIdentifierFieldName();
  579.                     $sequenceName $class->getSequenceName($this->getTargetPlatform());
  580.                     $quoted       = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
  581.                     $definition = [
  582.                         'sequenceName'      => $this->truncateSequenceName($sequenceName),
  583.                         'allocationSize'    => 1,
  584.                         'initialValue'      => 1,
  585.                     ];
  586.                     if ($quoted) {
  587.                         $definition['quoted'] = true;
  588.                     }
  589.                     $class->setSequenceGeneratorDefinition($definition);
  590.                 }
  591.                 $sequenceGenerator = new SequenceGenerator(
  592.                     $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition$class$this->getTargetPlatform()),
  593.                     (int) $definition['allocationSize']
  594.                 );
  595.                 $class->setIdGenerator($sequenceGenerator);
  596.                 break;
  597.             case ClassMetadata::GENERATOR_TYPE_NONE:
  598.                 $class->setIdGenerator(new AssignedGenerator());
  599.                 break;
  600.             case ClassMetadata::GENERATOR_TYPE_UUID:
  601.                 Deprecation::trigger(
  602.                     'doctrine/orm',
  603.                     'https://github.com/doctrine/orm/issues/7312',
  604.                     'Mapping for %s: the "UUID" id generator strategy is deprecated with no replacement',
  605.                     $class->name
  606.                 );
  607.                 $class->setIdGenerator(new UuidGenerator());
  608.                 break;
  609.             case ClassMetadata::GENERATOR_TYPE_CUSTOM:
  610.                 $definition $class->customGeneratorDefinition;
  611.                 if ($definition === null) {
  612.                     throw InvalidCustomGenerator::onClassNotConfigured();
  613.                 }
  614.                 if (! class_exists($definition['class'])) {
  615.                     throw InvalidCustomGenerator::onMissingClass($definition);
  616.                 }
  617.                 $class->setIdGenerator(new $definition['class']());
  618.                 break;
  619.             default:
  620.                 throw UnknownGeneratorType::create($class->generatorType);
  621.         }
  622.     }
  623.     /** @psalm-return ClassMetadata::GENERATOR_TYPE_SEQUENCE|ClassMetadata::GENERATOR_TYPE_IDENTITY */
  624.     private function determineIdGeneratorStrategy(AbstractPlatform $platform): int
  625.     {
  626.         if (
  627.             $platform instanceof Platforms\OraclePlatform
  628.             || $platform instanceof Platforms\PostgreSQLPlatform
  629.         ) {
  630.             return ClassMetadata::GENERATOR_TYPE_SEQUENCE;
  631.         }
  632.         if ($platform->supportsIdentityColumns()) {
  633.             return ClassMetadata::GENERATOR_TYPE_IDENTITY;
  634.         }
  635.         if ($platform->supportsSequences()) {
  636.             return ClassMetadata::GENERATOR_TYPE_SEQUENCE;
  637.         }
  638.         throw CannotGenerateIds::withPlatform($platform);
  639.     }
  640.     private function truncateSequenceName(string $schemaElementName): string
  641.     {
  642.         $platform $this->getTargetPlatform();
  643.         if (! $platform instanceof Platforms\OraclePlatform && ! $platform instanceof Platforms\SQLAnywherePlatform) {
  644.             return $schemaElementName;
  645.         }
  646.         $maxIdentifierLength $platform->getMaxIdentifierLength();
  647.         if (strlen($schemaElementName) > $maxIdentifierLength) {
  648.             return substr($schemaElementName0$maxIdentifierLength);
  649.         }
  650.         return $schemaElementName;
  651.     }
  652.     /**
  653.      * Inherits the ID generator mapping from a parent class.
  654.      */
  655.     private function inheritIdGeneratorMapping(ClassMetadataInfo $classClassMetadataInfo $parent): void
  656.     {
  657.         if ($parent->isIdGeneratorSequence()) {
  658.             $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition);
  659.         }
  660.         if ($parent->generatorType) {
  661.             $class->setIdGeneratorType($parent->generatorType);
  662.         }
  663.         if ($parent->idGenerator) {
  664.             $class->setIdGenerator($parent->idGenerator);
  665.         }
  666.     }
  667.     /**
  668.      * {@inheritDoc}
  669.      */
  670.     protected function wakeupReflection(ClassMetadataInterface $classReflectionService $reflService)
  671.     {
  672.         assert($class instanceof ClassMetadata);
  673.         $class->wakeupReflection($reflService);
  674.     }
  675.     /**
  676.      * {@inheritDoc}
  677.      */
  678.     protected function initializeReflection(ClassMetadataInterface $classReflectionService $reflService)
  679.     {
  680.         assert($class instanceof ClassMetadata);
  681.         $class->initializeReflection($reflService);
  682.     }
  683.     /**
  684.      * @deprecated This method will be removed in ORM 3.0.
  685.      *
  686.      * @return class-string
  687.      */
  688.     protected function getFqcnFromAlias($namespaceAlias$simpleClassName)
  689.     {
  690.         /** @psalm-var class-string */
  691.         return $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' $simpleClassName;
  692.     }
  693.     /**
  694.      * {@inheritDoc}
  695.      */
  696.     protected function getDriver()
  697.     {
  698.         return $this->driver;
  699.     }
  700.     /**
  701.      * {@inheritDoc}
  702.      */
  703.     protected function isEntity(ClassMetadataInterface $class)
  704.     {
  705.         return ! $class->isMappedSuperclass;
  706.     }
  707.     private function getTargetPlatform(): Platforms\AbstractPlatform
  708.     {
  709.         if (! $this->targetPlatform) {
  710.             $this->targetPlatform $this->em->getConnection()->getDatabasePlatform();
  711.         }
  712.         return $this->targetPlatform;
  713.     }
  714. }