Builder
9 months ago
Driver
9 months ago
Exception
9 months ago
Reflection
9 months ago
Annotation.php
9 months ago
AnsiQuoteStrategy.php
9 months ago
AssociationOverride.php
9 months ago
AssociationOverrides.php
9 months ago
AttributeOverride.php
9 months ago
AttributeOverrides.php
9 months ago
Cache.php
9 months ago
ChainTypedFieldMapper.php
9 months ago
ChangeTrackingPolicy.php
9 months ago
ClassMetadata.php
9 months ago
ClassMetadataFactory.php
9 months ago
ClassMetadataInfo.php
9 months ago
Column.php
9 months ago
ColumnResult.php
9 months ago
CustomIdGenerator.php
9 months ago
DefaultEntityListenerResolver.php
9 months ago
DefaultNamingStrategy.php
9 months ago
DefaultQuoteStrategy.php
9 months ago
DefaultTypedFieldMapper.php
9 months ago
DiscriminatorColumn.php
9 months ago
DiscriminatorMap.php
9 months ago
Embeddable.php
9 months ago
Embedded.php
9 months ago
Entity.php
9 months ago
EntityListenerResolver.php
9 months ago
EntityListeners.php
9 months ago
EntityResult.php
9 months ago
FieldResult.php
9 months ago
GeneratedValue.php
9 months ago
HasLifecycleCallbacks.php
9 months ago
Id.php
9 months ago
Index.php
9 months ago
InheritanceType.php
9 months ago
InverseJoinColumn.php
9 months ago
JoinColumn.php
9 months ago
JoinColumnProperties.php
9 months ago
JoinColumns.php
9 months ago
JoinTable.php
9 months ago
ManyToMany.php
9 months ago
ManyToOne.php
9 months ago
MappedSuperclass.php
9 months ago
MappingAttribute.php
9 months ago
MappingException.php
9 months ago
NamedNativeQueries.php
9 months ago
NamedNativeQuery.php
9 months ago
NamedQueries.php
9 months ago
NamedQuery.php
9 months ago
NamingStrategy.php
9 months ago
OneToMany.php
9 months ago
OneToOne.php
9 months ago
OrderBy.php
9 months ago
PostLoad.php
9 months ago
PostPersist.php
9 months ago
PostRemove.php
9 months ago
PostUpdate.php
9 months ago
PreFlush.php
9 months ago
PrePersist.php
9 months ago
PreRemove.php
9 months ago
PreUpdate.php
9 months ago
QuoteStrategy.php
9 months ago
ReflectionEmbeddedProperty.php
9 months ago
ReflectionEnumProperty.php
9 months ago
ReflectionReadonlyProperty.php
9 months ago
SequenceGenerator.php
9 months ago
SqlResultSetMapping.php
9 months ago
SqlResultSetMappings.php
9 months ago
Table.php
9 months ago
TypedFieldMapper.php
9 months ago
UnderscoreNamingStrategy.php
9 months ago
UniqueConstraint.php
9 months ago
Version.php
9 months ago
ClassMetadataFactory.php
546 lines
| 1 | <?php |
| 2 | declare (strict_types=1); |
| 3 | namespace MailPoetVendor\Doctrine\ORM\Mapping; |
| 4 | if (!defined('ABSPATH')) exit; |
| 5 | use MailPoetVendor\Doctrine\Common\EventManager; |
| 6 | use MailPoetVendor\Doctrine\DBAL\Platforms; |
| 7 | use MailPoetVendor\Doctrine\DBAL\Platforms\AbstractPlatform; |
| 8 | use MailPoetVendor\Doctrine\DBAL\Platforms\MySQLPlatform; |
| 9 | use MailPoetVendor\Doctrine\DBAL\Platforms\SqlitePlatform; |
| 10 | use MailPoetVendor\Doctrine\DBAL\Platforms\SQLServerPlatform; |
| 11 | use MailPoetVendor\Doctrine\Deprecations\Deprecation; |
| 12 | use MailPoetVendor\Doctrine\ORM\EntityManagerInterface; |
| 13 | use MailPoetVendor\Doctrine\ORM\Event\LoadClassMetadataEventArgs; |
| 14 | use MailPoetVendor\Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs; |
| 15 | use MailPoetVendor\Doctrine\ORM\Events; |
| 16 | use MailPoetVendor\Doctrine\ORM\Exception\ORMException; |
| 17 | use MailPoetVendor\Doctrine\ORM\Id\AssignedGenerator; |
| 18 | use MailPoetVendor\Doctrine\ORM\Id\BigIntegerIdentityGenerator; |
| 19 | use MailPoetVendor\Doctrine\ORM\Id\IdentityGenerator; |
| 20 | use MailPoetVendor\Doctrine\ORM\Id\SequenceGenerator; |
| 21 | use MailPoetVendor\Doctrine\ORM\Id\UuidGenerator; |
| 22 | use MailPoetVendor\Doctrine\ORM\Mapping\Exception\CannotGenerateIds; |
| 23 | use MailPoetVendor\Doctrine\ORM\Mapping\Exception\InvalidCustomGenerator; |
| 24 | use MailPoetVendor\Doctrine\ORM\Mapping\Exception\UnknownGeneratorType; |
| 25 | use MailPoetVendor\Doctrine\ORM\Proxy\DefaultProxyClassNameResolver; |
| 26 | use MailPoetVendor\Doctrine\Persistence\Mapping\AbstractClassMetadataFactory; |
| 27 | use MailPoetVendor\Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface; |
| 28 | use MailPoetVendor\Doctrine\Persistence\Mapping\Driver\MappingDriver; |
| 29 | use MailPoetVendor\Doctrine\Persistence\Mapping\ReflectionService; |
| 30 | use ReflectionClass; |
| 31 | use ReflectionException; |
| 32 | use function assert; |
| 33 | use function class_exists; |
| 34 | use function count; |
| 35 | use function end; |
| 36 | use function explode; |
| 37 | use function get_class; |
| 38 | use function in_array; |
| 39 | use function is_a; |
| 40 | use function is_subclass_of; |
| 41 | use function str_contains; |
| 42 | use function strlen; |
| 43 | use function strtolower; |
| 44 | use function substr; |
| 45 | class ClassMetadataFactory extends AbstractClassMetadataFactory |
| 46 | { |
| 47 | private $em; |
| 48 | private $targetPlatform; |
| 49 | private $driver; |
| 50 | private $evm; |
| 51 | private $embeddablesActiveNesting = []; |
| 52 | private const NON_IDENTITY_DEFAULT_STRATEGY = ['MailPoetVendor\\Doctrine\\DBAL\\Platforms\\PostgreSqlPlatform' => ClassMetadata::GENERATOR_TYPE_SEQUENCE, Platforms\OraclePlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE, Platforms\PostgreSQLPlatform::class => ClassMetadata::GENERATOR_TYPE_SEQUENCE]; |
| 53 | public function setEntityManager(EntityManagerInterface $em) |
| 54 | { |
| 55 | parent::setProxyClassNameResolver(new DefaultProxyClassNameResolver()); |
| 56 | $this->em = $em; |
| 57 | } |
| 58 | protected function initialize() |
| 59 | { |
| 60 | $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl(); |
| 61 | $this->evm = $this->em->getEventManager(); |
| 62 | $this->initialized = \true; |
| 63 | } |
| 64 | protected function onNotFoundMetadata($className) |
| 65 | { |
| 66 | if (!$this->evm->hasListeners(Events::onClassMetadataNotFound)) { |
| 67 | return null; |
| 68 | } |
| 69 | $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $this->em); |
| 70 | $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs); |
| 71 | $classMetadata = $eventArgs->getFoundMetadata(); |
| 72 | assert($classMetadata instanceof ClassMetadata || $classMetadata === null); |
| 73 | return $classMetadata; |
| 74 | } |
| 75 | protected function doLoadMetadata($class, $parent, $rootEntityFound, array $nonSuperclassParents) |
| 76 | { |
| 77 | if ($parent) { |
| 78 | $class->setInheritanceType($parent->inheritanceType); |
| 79 | $class->setDiscriminatorColumn($parent->discriminatorColumn); |
| 80 | $class->setIdGeneratorType($parent->generatorType); |
| 81 | $this->addInheritedFields($class, $parent); |
| 82 | $this->addInheritedRelations($class, $parent); |
| 83 | $this->addInheritedEmbeddedClasses($class, $parent); |
| 84 | $class->setIdentifier($parent->identifier); |
| 85 | $class->setVersioned($parent->isVersioned); |
| 86 | $class->setVersionField($parent->versionField); |
| 87 | $class->setDiscriminatorMap($parent->discriminatorMap); |
| 88 | $class->addSubClasses($parent->subClasses); |
| 89 | $class->setLifecycleCallbacks($parent->lifecycleCallbacks); |
| 90 | $class->setChangeTrackingPolicy($parent->changeTrackingPolicy); |
| 91 | if (!empty($parent->customGeneratorDefinition)) { |
| 92 | $class->setCustomGeneratorDefinition($parent->customGeneratorDefinition); |
| 93 | } |
| 94 | if ($parent->isMappedSuperclass) { |
| 95 | $class->setCustomRepositoryClass($parent->customRepositoryClassName); |
| 96 | } |
| 97 | } |
| 98 | // Invoke driver |
| 99 | try { |
| 100 | $this->driver->loadMetadataForClass($class->getName(), $class); |
| 101 | } catch (ReflectionException $e) { |
| 102 | throw MappingException::reflectionFailure($class->getName(), $e); |
| 103 | } |
| 104 | // If this class has a parent the id generator strategy is inherited. |
| 105 | // However this is only true if the hierarchy of parents contains the root entity, |
| 106 | // if it consists of mapped superclasses these don't necessarily include the id field. |
| 107 | if ($parent && $rootEntityFound) { |
| 108 | $this->inheritIdGeneratorMapping($class, $parent); |
| 109 | } else { |
| 110 | $this->completeIdGeneratorMapping($class); |
| 111 | } |
| 112 | if (!$class->isMappedSuperclass) { |
| 113 | if ($rootEntityFound && $class->isInheritanceTypeNone()) { |
| 114 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/10431', "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.", $class->name, end($nonSuperclassParents)); |
| 115 | } |
| 116 | foreach ($class->embeddedClasses as $property => $embeddableClass) { |
| 117 | if (isset($embeddableClass['inherited'])) { |
| 118 | continue; |
| 119 | } |
| 120 | if (isset($this->embeddablesActiveNesting[$embeddableClass['class']])) { |
| 121 | throw MappingException::infiniteEmbeddableNesting($class->name, $property); |
| 122 | } |
| 123 | $this->embeddablesActiveNesting[$class->name] = \true; |
| 124 | $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']); |
| 125 | if ($embeddableMetadata->isEmbeddedClass) { |
| 126 | $this->addNestedEmbeddedClasses($embeddableMetadata, $class, $property); |
| 127 | } |
| 128 | $identifier = $embeddableMetadata->getIdentifier(); |
| 129 | if (!empty($identifier)) { |
| 130 | $this->inheritIdGeneratorMapping($class, $embeddableMetadata); |
| 131 | } |
| 132 | $class->inlineEmbeddable($property, $embeddableMetadata); |
| 133 | unset($this->embeddablesActiveNesting[$class->name]); |
| 134 | } |
| 135 | } |
| 136 | if ($parent) { |
| 137 | if ($parent->isInheritanceTypeSingleTable()) { |
| 138 | $class->setPrimaryTable($parent->table); |
| 139 | } |
| 140 | $this->addInheritedIndexes($class, $parent); |
| 141 | if ($parent->cache) { |
| 142 | $class->cache = $parent->cache; |
| 143 | } |
| 144 | if ($parent->containsForeignIdentifier) { |
| 145 | $class->containsForeignIdentifier = \true; |
| 146 | } |
| 147 | if ($parent->containsEnumIdentifier) { |
| 148 | $class->containsEnumIdentifier = \true; |
| 149 | } |
| 150 | if (!empty($parent->namedQueries)) { |
| 151 | $this->addInheritedNamedQueries($class, $parent); |
| 152 | } |
| 153 | if (!empty($parent->namedNativeQueries)) { |
| 154 | $this->addInheritedNamedNativeQueries($class, $parent); |
| 155 | } |
| 156 | if (!empty($parent->sqlResultSetMappings)) { |
| 157 | $this->addInheritedSqlResultSetMappings($class, $parent); |
| 158 | } |
| 159 | if (!empty($parent->entityListeners) && empty($class->entityListeners)) { |
| 160 | $class->entityListeners = $parent->entityListeners; |
| 161 | } |
| 162 | } |
| 163 | $class->setParentClasses($nonSuperclassParents); |
| 164 | if ($class->isRootEntity() && !$class->isInheritanceTypeNone() && !$class->discriminatorMap) { |
| 165 | $this->addDefaultDiscriminatorMap($class); |
| 166 | } |
| 167 | // During the following event, there may also be updates to the discriminator map as per GH-1257/GH-8402. |
| 168 | // So, we must not discover the missing subclasses before that. |
| 169 | if ($this->evm->hasListeners(Events::loadClassMetadata)) { |
| 170 | $eventArgs = new LoadClassMetadataEventArgs($class, $this->em); |
| 171 | $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); |
| 172 | } |
| 173 | $this->findAbstractEntityClassesNotListedInDiscriminatorMap($class); |
| 174 | if ($class->changeTrackingPolicy === ClassMetadata::CHANGETRACKING_NOTIFY) { |
| 175 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/issues/8383', 'NOTIFY Change Tracking policy used in "%s" is deprecated, use deferred explicit instead.', $class->name); |
| 176 | } |
| 177 | $this->validateRuntimeMetadata($class, $parent); |
| 178 | } |
| 179 | protected function validateRuntimeMetadata($class, $parent) |
| 180 | { |
| 181 | if (!$class->reflClass) { |
| 182 | // only validate if there is a reflection class instance |
| 183 | return; |
| 184 | } |
| 185 | $class->validateIdentifier(); |
| 186 | $class->validateAssociations(); |
| 187 | $class->validateLifecycleCallbacks($this->getReflectionService()); |
| 188 | // verify inheritance |
| 189 | if (!$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) { |
| 190 | if (!$parent) { |
| 191 | if (count($class->discriminatorMap) === 0) { |
| 192 | throw MappingException::missingDiscriminatorMap($class->name); |
| 193 | } |
| 194 | if (!$class->discriminatorColumn) { |
| 195 | throw MappingException::missingDiscriminatorColumn($class->name); |
| 196 | } |
| 197 | foreach ($class->subClasses as $subClass) { |
| 198 | if ((new ReflectionClass($subClass))->name !== $subClass) { |
| 199 | throw MappingException::invalidClassInDiscriminatorMap($subClass, $class->name); |
| 200 | } |
| 201 | } |
| 202 | } else { |
| 203 | assert($parent instanceof ClassMetadataInfo); |
| 204 | // https://github.com/doctrine/orm/issues/8746 |
| 205 | if (!$class->reflClass->isAbstract() && !in_array($class->name, $class->discriminatorMap, \true)) { |
| 206 | throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName); |
| 207 | } |
| 208 | } |
| 209 | } elseif ($class->isMappedSuperclass && $class->name === $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) { |
| 210 | // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy |
| 211 | throw MappingException::noInheritanceOnMappedSuperClass($class->name); |
| 212 | } |
| 213 | } |
| 214 | protected function newClassMetadataInstance($className) |
| 215 | { |
| 216 | return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy(), $this->em->getConfiguration()->getTypedFieldMapper()); |
| 217 | } |
| 218 | private function addDefaultDiscriminatorMap(ClassMetadata $class) : void |
| 219 | { |
| 220 | $allClasses = $this->driver->getAllClassNames(); |
| 221 | $fqcn = $class->getName(); |
| 222 | $map = [$this->getShortName($class->name) => $fqcn]; |
| 223 | $duplicates = []; |
| 224 | foreach ($allClasses as $subClassCandidate) { |
| 225 | if (is_subclass_of($subClassCandidate, $fqcn)) { |
| 226 | $shortName = $this->getShortName($subClassCandidate); |
| 227 | if (isset($map[$shortName])) { |
| 228 | $duplicates[] = $shortName; |
| 229 | } |
| 230 | $map[$shortName] = $subClassCandidate; |
| 231 | } |
| 232 | } |
| 233 | if ($duplicates) { |
| 234 | throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map); |
| 235 | } |
| 236 | $class->setDiscriminatorMap($map); |
| 237 | } |
| 238 | private function findAbstractEntityClassesNotListedInDiscriminatorMap(ClassMetadata $rootEntityClass) : void |
| 239 | { |
| 240 | // Only root classes in inheritance hierarchies need contain a discriminator map, |
| 241 | // so skip for other classes. |
| 242 | if (!$rootEntityClass->isRootEntity() || $rootEntityClass->isInheritanceTypeNone()) { |
| 243 | return; |
| 244 | } |
| 245 | $processedClasses = [$rootEntityClass->name => \true]; |
| 246 | foreach ($rootEntityClass->subClasses as $knownSubClass) { |
| 247 | $processedClasses[$knownSubClass] = \true; |
| 248 | } |
| 249 | foreach ($rootEntityClass->discriminatorMap as $declaredClassName) { |
| 250 | // This fetches non-transient parent classes only |
| 251 | $parentClasses = $this->getParentClasses($declaredClassName); |
| 252 | foreach ($parentClasses as $parentClass) { |
| 253 | if (isset($processedClasses[$parentClass])) { |
| 254 | continue; |
| 255 | } |
| 256 | $processedClasses[$parentClass] = \true; |
| 257 | // All non-abstract entity classes must be listed in the discriminator map, and |
| 258 | // this will be validated/enforced at runtime (possibly at a later time, when the |
| 259 | // subclass is loaded, but anyways). Also, subclasses is about entity classes only. |
| 260 | // That means we can ignore non-abstract classes here. The (expensive) driver |
| 261 | // check for mapped superclasses need only be run for abstract candidate classes. |
| 262 | if (!(new ReflectionClass($parentClass))->isAbstract() || $this->peekIfIsMappedSuperclass($parentClass)) { |
| 263 | continue; |
| 264 | } |
| 265 | // We have found a non-transient, non-mapped-superclass = an entity class (possibly abstract, but that does not matter) |
| 266 | $rootEntityClass->addSubClass($parentClass); |
| 267 | } |
| 268 | } |
| 269 | } |
| 270 | private function peekIfIsMappedSuperclass(string $className) : bool |
| 271 | { |
| 272 | $reflService = $this->getReflectionService(); |
| 273 | $class = $this->newClassMetadataInstance($className); |
| 274 | $this->initializeReflection($class, $reflService); |
| 275 | $this->driver->loadMetadataForClass($className, $class); |
| 276 | return $class->isMappedSuperclass; |
| 277 | } |
| 278 | private function getShortName(string $className) : string |
| 279 | { |
| 280 | if (!str_contains($className, '\\')) { |
| 281 | return strtolower($className); |
| 282 | } |
| 283 | $parts = explode('\\', $className); |
| 284 | return strtolower(end($parts)); |
| 285 | } |
| 286 | private function addMappingInheritanceInformation(array &$mapping, ClassMetadata $parentClass) : void |
| 287 | { |
| 288 | if (!isset($mapping['inherited']) && !$parentClass->isMappedSuperclass) { |
| 289 | $mapping['inherited'] = $parentClass->name; |
| 290 | } |
| 291 | if (!isset($mapping['declared'])) { |
| 292 | $mapping['declared'] = $parentClass->name; |
| 293 | } |
| 294 | } |
| 295 | private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
| 296 | { |
| 297 | foreach ($parentClass->fieldMappings as $mapping) { |
| 298 | $this->addMappingInheritanceInformation($mapping, $parentClass); |
| 299 | $subClass->addInheritedFieldMapping($mapping); |
| 300 | } |
| 301 | foreach ($parentClass->reflFields as $name => $field) { |
| 302 | $subClass->reflFields[$name] = $field; |
| 303 | } |
| 304 | } |
| 305 | private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
| 306 | { |
| 307 | foreach ($parentClass->associationMappings as $field => $mapping) { |
| 308 | $this->addMappingInheritanceInformation($mapping, $parentClass); |
| 309 | // When the class inheriting the relation ($subClass) is the first entity class since the |
| 310 | // relation has been defined in a mapped superclass (or in a chain |
| 311 | // of mapped superclasses) above, then declare this current entity class as the source of |
| 312 | // the relationship. |
| 313 | // According to the definitions given in https://github.com/doctrine/orm/pull/10396/, |
| 314 | // this is the case <=> ! isset($mapping['inherited']). |
| 315 | if (!isset($mapping['inherited'])) { |
| 316 | $mapping['sourceEntity'] = $subClass->name; |
| 317 | } |
| 318 | $subClass->addInheritedAssociationMapping($mapping); |
| 319 | } |
| 320 | } |
| 321 | private function addInheritedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
| 322 | { |
| 323 | foreach ($parentClass->embeddedClasses as $field => $embeddedClass) { |
| 324 | $this->addMappingInheritanceInformation($embeddedClass, $parentClass); |
| 325 | $subClass->embeddedClasses[$field] = $embeddedClass; |
| 326 | } |
| 327 | } |
| 328 | private function addNestedEmbeddedClasses(ClassMetadata $subClass, ClassMetadata $parentClass, string $prefix) : void |
| 329 | { |
| 330 | foreach ($subClass->embeddedClasses as $property => $embeddableClass) { |
| 331 | if (isset($embeddableClass['inherited'])) { |
| 332 | continue; |
| 333 | } |
| 334 | $embeddableMetadata = $this->getMetadataFor($embeddableClass['class']); |
| 335 | $parentClass->mapEmbedded(['fieldName' => $prefix . '.' . $property, 'class' => $embeddableMetadata->name, 'columnPrefix' => $embeddableClass['columnPrefix'], 'declaredField' => $embeddableClass['declaredField'] ? $prefix . '.' . $embeddableClass['declaredField'] : $prefix, 'originalField' => $embeddableClass['originalField'] ?: $property]); |
| 336 | } |
| 337 | } |
| 338 | private function addInheritedIndexes(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
| 339 | { |
| 340 | if (!$parentClass->isMappedSuperclass) { |
| 341 | return; |
| 342 | } |
| 343 | foreach (['uniqueConstraints', 'indexes'] as $indexType) { |
| 344 | if (isset($parentClass->table[$indexType])) { |
| 345 | foreach ($parentClass->table[$indexType] as $indexName => $index) { |
| 346 | if (isset($subClass->table[$indexType][$indexName])) { |
| 347 | continue; |
| 348 | // Let the inheriting table override indices |
| 349 | } |
| 350 | $subClass->table[$indexType][$indexName] = $index; |
| 351 | } |
| 352 | } |
| 353 | } |
| 354 | } |
| 355 | private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
| 356 | { |
| 357 | foreach ($parentClass->namedQueries as $name => $query) { |
| 358 | if (!isset($subClass->namedQueries[$name])) { |
| 359 | // @phpstan-ignore method.deprecated |
| 360 | $subClass->addNamedQuery(['name' => $query['name'], 'query' => $query['query']]); |
| 361 | } |
| 362 | } |
| 363 | } |
| 364 | private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
| 365 | { |
| 366 | foreach ($parentClass->namedNativeQueries as $name => $query) { |
| 367 | if (!isset($subClass->namedNativeQueries[$name])) { |
| 368 | // @phpstan-ignore method.deprecated |
| 369 | $subClass->addNamedNativeQuery(['name' => $query['name'], 'query' => $query['query'], 'isSelfClass' => $query['isSelfClass'], 'resultSetMapping' => $query['resultSetMapping'], 'resultClass' => $query['isSelfClass'] ? $subClass->name : $query['resultClass']]); |
| 370 | } |
| 371 | } |
| 372 | } |
| 373 | private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) : void |
| 374 | { |
| 375 | foreach ($parentClass->sqlResultSetMappings as $name => $mapping) { |
| 376 | if (!isset($subClass->sqlResultSetMappings[$name])) { |
| 377 | $entities = []; |
| 378 | foreach ($mapping['entities'] as $entity) { |
| 379 | $entities[] = ['fields' => $entity['fields'], 'isSelfClass' => $entity['isSelfClass'], 'discriminatorColumn' => $entity['discriminatorColumn'], 'entityClass' => $entity['isSelfClass'] ? $subClass->name : $entity['entityClass']]; |
| 380 | } |
| 381 | $subClass->addSqlResultSetMapping(['name' => $mapping['name'], 'columns' => $mapping['columns'], 'entities' => $entities]); |
| 382 | } |
| 383 | } |
| 384 | } |
| 385 | private function completeIdGeneratorMapping(ClassMetadataInfo $class) : void |
| 386 | { |
| 387 | $idGenType = $class->generatorType; |
| 388 | if ($idGenType === ClassMetadata::GENERATOR_TYPE_AUTO) { |
| 389 | $class->setIdGeneratorType($this->determineIdGeneratorStrategy($this->getTargetPlatform())); |
| 390 | } |
| 391 | // Create & assign an appropriate ID generator instance |
| 392 | switch ($class->generatorType) { |
| 393 | case ClassMetadata::GENERATOR_TYPE_IDENTITY: |
| 394 | $sequenceName = null; |
| 395 | $fieldName = $class->identifier ? $class->getSingleIdentifierFieldName() : null; |
| 396 | $platform = $this->getTargetPlatform(); |
| 397 | // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour. |
| 398 | // @phpstan-ignore method.deprecated |
| 399 | if (!$platform instanceof MySQLPlatform && !$platform instanceof SqlitePlatform && !$platform instanceof SQLServerPlatform && $platform->usesSequenceEmulatedIdentityColumns()) { |
| 400 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/issues/8850', <<<'DEPRECATION' |
| 401 | Context: Loading metadata for class %s |
| 402 | Problem: Using identity columns emulated with a sequence is deprecated and will not be possible in Doctrine ORM 3.0. |
| 403 | Solution: Use the SEQUENCE generator strategy instead. |
| 404 | DEPRECATION |
| 405 | , $class->name, get_class($this->getTargetPlatform())); |
| 406 | $columnName = $class->getSingleIdentifierColumnName(); |
| 407 | $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); |
| 408 | $sequencePrefix = $class->getSequencePrefix($this->getTargetPlatform()); |
| 409 | // @phpstan-ignore method.deprecated |
| 410 | $sequenceName = $this->getTargetPlatform()->getIdentitySequenceName($sequencePrefix, $columnName); |
| 411 | $definition = ['sequenceName' => $this->truncateSequenceName($sequenceName)]; |
| 412 | if ($quoted) { |
| 413 | $definition['quoted'] = \true; |
| 414 | } |
| 415 | $sequenceName = $this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->getTargetPlatform()); |
| 416 | } |
| 417 | $generator = $fieldName && $class->fieldMappings[$fieldName]['type'] === 'bigint' ? new BigIntegerIdentityGenerator($sequenceName) : new IdentityGenerator($sequenceName); |
| 418 | $class->setIdGenerator($generator); |
| 419 | break; |
| 420 | case ClassMetadata::GENERATOR_TYPE_SEQUENCE: |
| 421 | // If there is no sequence definition yet, create a default definition |
| 422 | $definition = $class->sequenceGeneratorDefinition; |
| 423 | if (!$definition) { |
| 424 | $fieldName = $class->getSingleIdentifierFieldName(); |
| 425 | $sequenceName = $class->getSequenceName($this->getTargetPlatform()); |
| 426 | $quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']); |
| 427 | $definition = ['sequenceName' => $this->truncateSequenceName($sequenceName), 'allocationSize' => 1, 'initialValue' => 1]; |
| 428 | if ($quoted) { |
| 429 | $definition['quoted'] = \true; |
| 430 | } |
| 431 | $class->setSequenceGeneratorDefinition($definition); |
| 432 | } |
| 433 | $sequenceGenerator = new SequenceGenerator($this->em->getConfiguration()->getQuoteStrategy()->getSequenceName($definition, $class, $this->getTargetPlatform()), (int) $definition['allocationSize']); |
| 434 | $class->setIdGenerator($sequenceGenerator); |
| 435 | break; |
| 436 | case ClassMetadata::GENERATOR_TYPE_NONE: |
| 437 | $class->setIdGenerator(new AssignedGenerator()); |
| 438 | break; |
| 439 | // @phpstan-ignore classConstant.deprecated |
| 440 | case ClassMetadata::GENERATOR_TYPE_UUID: |
| 441 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/issues/7312', 'Mapping for %s: the "UUID" id generator strategy is deprecated with no replacement', $class->name); |
| 442 | // @phpstan-ignore method.deprecatedClass, new.deprecatedClass |
| 443 | $class->setIdGenerator(new UuidGenerator()); |
| 444 | break; |
| 445 | case ClassMetadata::GENERATOR_TYPE_CUSTOM: |
| 446 | $definition = $class->customGeneratorDefinition; |
| 447 | if ($definition === null) { |
| 448 | throw InvalidCustomGenerator::onClassNotConfigured(); |
| 449 | } |
| 450 | if (!class_exists($definition['class'])) { |
| 451 | throw InvalidCustomGenerator::onMissingClass($definition); |
| 452 | } |
| 453 | $class->setIdGenerator(new $definition['class']()); |
| 454 | break; |
| 455 | default: |
| 456 | throw UnknownGeneratorType::create($class->generatorType); |
| 457 | } |
| 458 | } |
| 459 | private function determineIdGeneratorStrategy(AbstractPlatform $platform) : int |
| 460 | { |
| 461 | assert($this->em !== null); |
| 462 | foreach ($this->em->getConfiguration()->getIdentityGenerationPreferences() as $platformFamily => $strategy) { |
| 463 | if (is_a($platform, $platformFamily)) { |
| 464 | return $strategy; |
| 465 | } |
| 466 | } |
| 467 | foreach (self::NON_IDENTITY_DEFAULT_STRATEGY as $platformFamily => $strategy) { |
| 468 | if (is_a($platform, $platformFamily)) { |
| 469 | if ($platform instanceof Platforms\PostgreSQLPlatform || is_a($platform, 'MailPoetVendor\\Doctrine\\DBAL\\Platforms\\PostgreSqlPlatform')) { |
| 470 | Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/issues/8893', <<<'DEPRECATION' |
| 471 | Relying on non-optimal defaults for ID generation is deprecated, and IDENTITY |
| 472 | results in SERIAL, which is not recommended. |
| 473 | Instead, configure identifier generation strategies explicitly through |
| 474 | configuration. |
| 475 | We currently recommend "SEQUENCE" for "%s", so you should use |
| 476 | $configuration->setIdentityGenerationPreferences([ |
| 477 | "%s" => ClassMetadata::GENERATOR_TYPE_SEQUENCE, |
| 478 | ]); |
| 479 | DEPRECATION |
| 480 | , $platformFamily, $platformFamily); |
| 481 | } |
| 482 | return $strategy; |
| 483 | } |
| 484 | } |
| 485 | if ($platform->supportsIdentityColumns()) { |
| 486 | return ClassMetadata::GENERATOR_TYPE_IDENTITY; |
| 487 | } |
| 488 | if ($platform->supportsSequences()) { |
| 489 | return ClassMetadata::GENERATOR_TYPE_SEQUENCE; |
| 490 | } |
| 491 | throw CannotGenerateIds::withPlatform($platform); |
| 492 | } |
| 493 | private function truncateSequenceName(string $schemaElementName) : string |
| 494 | { |
| 495 | $platform = $this->getTargetPlatform(); |
| 496 | if (!$platform instanceof Platforms\OraclePlatform && !$platform instanceof Platforms\SQLAnywherePlatform) { |
| 497 | return $schemaElementName; |
| 498 | } |
| 499 | $maxIdentifierLength = $platform->getMaxIdentifierLength(); |
| 500 | if (strlen($schemaElementName) > $maxIdentifierLength) { |
| 501 | return substr($schemaElementName, 0, $maxIdentifierLength); |
| 502 | } |
| 503 | return $schemaElementName; |
| 504 | } |
| 505 | private function inheritIdGeneratorMapping(ClassMetadataInfo $class, ClassMetadataInfo $parent) : void |
| 506 | { |
| 507 | if ($parent->isIdGeneratorSequence()) { |
| 508 | $class->setSequenceGeneratorDefinition($parent->sequenceGeneratorDefinition); |
| 509 | } |
| 510 | if ($parent->generatorType) { |
| 511 | $class->setIdGeneratorType($parent->generatorType); |
| 512 | } |
| 513 | if ($parent->idGenerator) { |
| 514 | $class->setIdGenerator($parent->idGenerator); |
| 515 | } |
| 516 | } |
| 517 | protected function wakeupReflection(ClassMetadataInterface $class, ReflectionService $reflService) |
| 518 | { |
| 519 | $class->wakeupReflection($reflService); |
| 520 | } |
| 521 | protected function initializeReflection(ClassMetadataInterface $class, ReflectionService $reflService) |
| 522 | { |
| 523 | $class->initializeReflection($reflService); |
| 524 | } |
| 525 | protected function getFqcnFromAlias($namespaceAlias, $simpleClassName) |
| 526 | { |
| 527 | $classString = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; |
| 528 | return $classString; |
| 529 | } |
| 530 | protected function getDriver() |
| 531 | { |
| 532 | return $this->driver; |
| 533 | } |
| 534 | protected function isEntity(ClassMetadataInterface $class) |
| 535 | { |
| 536 | return !$class->isMappedSuperclass; |
| 537 | } |
| 538 | private function getTargetPlatform() : Platforms\AbstractPlatform |
| 539 | { |
| 540 | if (!$this->targetPlatform) { |
| 541 | $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform(); |
| 542 | } |
| 543 | return $this->targetPlatform; |
| 544 | } |
| 545 | } |
| 546 |