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

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