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

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