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

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