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