Failed Conditions
Push — master ( 9b8b5d...3cda3f )
by Guilherme
08:40
created

ClassMetadataFactory::getParentClasses()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 12
ccs 6
cts 6
cp 1
crap 3
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Mapping;
6
7
use Doctrine\Common\Cache\Cache;
8
use Doctrine\Common\EventManager;
9
use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory as PersistenceClassMetadataFactory;
10
use Doctrine\Common\Persistence\Mapping\MappingException as PersistenceMappingException;
11
use Doctrine\Common\Persistence\Mapping\MappingException as CommonMappingException;
0 ignored issues
show
introduced by
Type Doctrine\Common\Persistence\Mapping\MappingException (as CommonMappingException) is not used in this file.
Loading history...
12
use Doctrine\DBAL\Platforms;
13
use Doctrine\DBAL\Platforms\AbstractPlatform;
14
use Doctrine\ORM\EntityManagerInterface;
15
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
16
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
17
use Doctrine\ORM\Events;
18
use Doctrine\ORM\Exception\ORMException;
19
use Doctrine\ORM\Mapping\Exception\TableGeneratorNotImplementedYet;
20
use Doctrine\ORM\Mapping\Exception\UnknownGeneratorType;
21
use Doctrine\ORM\Reflection\ReflectionService;
22
use Doctrine\ORM\Reflection\RuntimeReflectionService;
23
use Doctrine\ORM\Sequencing\Planning\CompositeValueGenerationPlan;
24
use Doctrine\ORM\Sequencing\Planning\NoopValueGenerationPlan;
25
use Doctrine\ORM\Sequencing\Planning\SingleValueGenerationPlan;
26
use Doctrine\ORM\Sequencing\Planning\ValueGenerationExecutor;
27
use Doctrine\ORM\Utility\StaticClassNameConverter;
28
use InvalidArgumentException;
29
use ReflectionException;
30
use function array_map;
31
use function array_reverse;
32
use function count;
33
use function in_array;
34
use function reset;
35
use function sprintf;
36
37
/**
38
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
39
 * metadata mapping information of a class which describes how a class should be mapped
40
 * to a relational database.
41
 */
42
class ClassMetadataFactory implements PersistenceClassMetadataFactory
43
{
44
    /**
45
     * Salt used by specific Object Manager implementation.
46
     *
47
     * @var string
48
     */
49
    protected $cacheSalt = '$CLASSMETADATA';
50
51
    /** @var bool */
52
    protected $initialized = false;
53
54
    /** @var ReflectionService|null */
55
    protected $reflectionService;
56
57
    /** @var EntityManagerInterface|null */
58
    private $em;
59
60
    /** @var AbstractPlatform */
61
    private $targetPlatform;
62
63
    /** @var Driver\MappingDriver */
64
    private $driver;
65
66
    /** @var EventManager */
67
    private $evm;
68
69
    /** @var Cache|null */
70
    private $cacheDriver;
71
72
    /** @var ClassMetadata[] */
73
    private $loadedMetadata = [];
74
75
    /**
76
     * Sets the entity manager used to build ClassMetadata instances.
77
     */
78 2277
    public function setEntityManager(EntityManagerInterface $em)
79
    {
80 2277
        $this->em = $em;
81 2277
    }
82
83
    /**
84
     * Sets the cache driver used by the factory to cache ClassMetadata instances.
85
     */
86 2275
    public function setCacheDriver(?Cache $cacheDriver = null) : void
87
    {
88 2275
        $this->cacheDriver = $cacheDriver;
89 2275
    }
90
91
    /**
92
     * Gets the cache driver used by the factory to cache ClassMetadata instances.
93
     */
94
    public function getCacheDriver() : ?Cache
95
    {
96
        return $this->cacheDriver;
97
    }
98
99
    /**
100
     * Returns an array of all the loaded metadata currently in memory.
101
     *
102
     * @return ClassMetadata[]
103
     */
104
    public function getLoadedMetadata() : array
105
    {
106
        return $this->loadedMetadata;
107
    }
108
109
    /**
110
     * Sets the reflectionService.
111
     */
112
    public function setReflectionService(ReflectionService $reflectionService) : void
113
    {
114
        $this->reflectionService = $reflectionService;
115
    }
116
117
    /**
118
     * Gets the reflection service associated with this metadata factory.
119
     */
120 1965
    public function getReflectionService() : ReflectionService
121
    {
122 1965
        if ($this->reflectionService === null) {
123 1965
            $this->reflectionService = new RuntimeReflectionService();
124
        }
125
126 1965
        return $this->reflectionService;
127
    }
128
129
    /**
130
     * {@inheritDoc}
131
     */
132 48
    public function isTransient($className) : bool
133
    {
134 48
        if (! $this->initialized) {
135 15
            $this->initialize();
136
        }
137
138 48
        $entityClassName = StaticClassNameConverter::getRealClass($className);
139
140 48
        return $this->getDriver()->isTransient($entityClassName);
141
    }
142
143
    /**
144
     * Checks whether the factory has the metadata for a class loaded already.
145
     *
146
     * @param string $className
147
     *
148
     * @return bool TRUE if the metadata of the class in question is already loaded, FALSE otherwise.
149
     */
150 66
    public function hasMetadataFor($className) : bool
151
    {
152 66
        return isset($this->loadedMetadata[$className]);
153
    }
154
155
    /**
156
     * Sets the metadata descriptor for a specific class.
157
     *
158
     * NOTE: This is only useful in very special cases, like when generating proxy classes.
159
     *
160
     * @param string        $className
161
     * @param ClassMetadata $class
162
     */
163 5
    public function setMetadataFor($className, $class) : void
164
    {
165 5
        $this->loadedMetadata[$className] = $class;
166 5
    }
167
168
    /**
169
     * Forces the factory to load the metadata of all classes known to the underlying
170
     * mapping driver.
171
     *
172
     * @return ClassMetadata[] The ClassMetadata instances of all mapped classes.
173
     *
174
     * @throws InvalidArgumentException
175
     * @throws ReflectionException
176
     * @throws MappingException
177
     * @throws PersistenceMappingException
178
     * @throws ORMException
179
     */
180 56
    public function getAllMetadata() : array
181
    {
182 56
        if (! $this->initialized) {
183 56
            $this->initialize();
184
        }
185
186 56
        $driver   = $this->getDriver();
187 56
        $metadata = [];
188
189 56
        foreach ($driver->getAllClassNames() as $className) {
190 55
            $metadata[] = $this->getMetadataFor($className);
191
        }
192
193 56
        return $metadata;
0 ignored issues
show
introduced by
The expression return $metadata returns an array which contains values of type Doctrine\ORM\Mapping\ClassMetadata which are incompatible with the return type Doctrine\Common\Persistence\Mapping\ClassMetadata mandated by Doctrine\Common\Persiste...ctory::getAllMetadata().
Loading history...
194
    }
195
196
    /**
197
     * {@inheritdoc}
198
     *
199
     * @throws ORMException
200
     */
201 453
    protected function initialize() : void
202
    {
203 453
        $this->driver      = $this->em->getConfiguration()->getMetadataDriverImpl();
0 ignored issues
show
Bug introduced by
The method getConfiguration() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

203
        $this->driver      = $this->em->/** @scrutinizer ignore-call */ getConfiguration()->getMetadataDriverImpl();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
204 453
        $this->evm         = $this->em->getEventManager();
205 453
        $this->initialized = true;
206 453
    }
207
208
    /**
209
     * {@inheritdoc}
210
     */
211 198
    protected function getDriver() : Driver\MappingDriver
212
    {
213 198
        return $this->driver;
214
    }
215
216
    /**
217
     * {@inheritdoc}
218
     */
219
    protected function isEntity(ClassMetadata $class) : bool
220
    {
221
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
222
    }
223
224
    /**
225
     * Gets the class metadata descriptor for a class.
226
     *
227
     * @param string $className The name of the class.
228
     *
229
     * @throws InvalidArgumentException
230
     * @throws PersistenceMappingException
231
     * @throws ORMException
232
     */
233 1970
    public function getMetadataFor($className) : ClassMetadata
234
    {
235 1970
        if (isset($this->loadedMetadata[$className])) {
236 1646
            return $this->loadedMetadata[$className];
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->loadedMetadata[$className] returns the type Doctrine\ORM\Mapping\ClassMetadata which is incompatible with the return type mandated by Doctrine\Common\Persiste...ctory::getMetadataFor() of Doctrine\Common\Persistence\Mapping\ClassMetadata.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
237
        }
238
239 1965
        $entityClassName = StaticClassNameConverter::getRealClass($className);
240
241 1965
        if (isset($this->loadedMetadata[$entityClassName])) {
242
            // We do not have the alias name in the map, include it
243 214
            return $this->loadedMetadata[$className] = $this->loadedMetadata[$entityClassName];
244
        }
245
246 1965
        $metadataBuildingContext = $this->newClassMetadataBuildingContext();
247 1965
        $loadingException        = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $loadingException is dead and can be removed.
Loading history...
248
249
        try {
250 1965
            if ($this->cacheDriver) {
251 1859
                $cached = $this->cacheDriver->fetch($entityClassName . $this->cacheSalt);
252
253 1859
                if ($cached instanceof ClassMetadata) {
254 1632
                    $this->loadedMetadata[$entityClassName] = $cached;
255
256 1632
                    $cached->wakeupReflection($metadataBuildingContext->getReflectionService());
257
                } else {
258 1859
                    foreach ($this->loadMetadata($entityClassName, $metadataBuildingContext) as $loadedClass) {
259 267
                        $loadedClassName = $loadedClass->getClassName();
260
261 267
                        $this->cacheDriver->save($loadedClassName . $this->cacheSalt, $loadedClass, null);
262
                    }
263
                }
264
            } else {
265 1957
                $this->loadMetadata($entityClassName, $metadataBuildingContext);
266
            }
267 26
        } catch (PersistenceMappingException $loadingException) {
268 12
            $fallbackMetadataResponse = $this->onNotFoundMetadata($entityClassName, $metadataBuildingContext);
269
270 12
            if (! $fallbackMetadataResponse) {
271 10
                throw $loadingException;
272
            }
273
274 2
            $this->loadedMetadata[$entityClassName] = $fallbackMetadataResponse;
275
        }
276
277 1947
        if ($className !== $entityClassName) {
278
            // We do not have the alias name in the map, include it
279 3
            $this->loadedMetadata[$className] = $this->loadedMetadata[$entityClassName];
280
        }
281
282 1947
        $metadataBuildingContext->validate();
283
284 1947
        return $this->loadedMetadata[$entityClassName];
285
    }
286
287
    /**
288
     * Loads the metadata of the class in question and all it's ancestors whose metadata
289
     * is still not loaded.
290
     *
291
     * Important: The class $name does not necessarily exist at this point here.
292
     * Scenarios in a code-generation setup might have access to XML
293
     * Mapping files without the actual PHP code existing here. That is why the
294
     * {@see Doctrine\ORM\Reflection\ReflectionService} interface
295
     * should be used for reflection.
296
     *
297
     * @param string $name The name of the class for which the metadata should get loaded.
298
     *
299
     * @return ClassMetadata[]
300
     *
301
     * @throws InvalidArgumentException
302
     * @throws ORMException
303
     */
304 387
    protected function loadMetadata(string $name, ClassMetadataBuildingContext $metadataBuildingContext) : array
305
    {
306 387
        if (! $this->initialized) {
307 382
            $this->initialize();
308
        }
309
310 387
        $loaded          = [];
311 387
        $parentClasses   = $this->getParentClasses($name);
312 376
        $parentClasses[] = $name;
313
314
        // Move down the hierarchy of parent classes, starting from the topmost class
315 376
        $parent = null;
316
317 376
        foreach ($parentClasses as $className) {
318 376
            if (isset($this->loadedMetadata[$className])) {
319 71
                $parent = $this->loadedMetadata[$className];
320
321 71
                continue;
322
            }
323
324 376
            $class = $this->doLoadMetadata($className, $parent, $metadataBuildingContext);
325
326 365
            $this->loadedMetadata[$className] = $class;
327
328 365
            $parent   = $class;
329 365
            $loaded[] = $class;
330
        }
331
332 363
        array_map([$this, 'resolveDiscriminatorValue'], $loaded);
333
334 363
        return $loaded;
335
    }
336
337
    /**
338
     * Gets an array of parent classes for the given entity class.
339
     *
340
     * @param string $name
341
     *
342
     * @return string[]
343
     *
344
     * @throws InvalidArgumentException
345
     */
346 387
    protected function getParentClasses($name) : array
347
    {
348
        // Collect parent classes, ignoring transient (not-mapped) classes.
349 387
        $parentClasses = [];
350
351 387
        foreach (array_reverse($this->getReflectionService()->getParentClasses($name)) as $parentClass) {
352 104
            if (! $this->getDriver()->isTransient($parentClass)) {
353 99
                $parentClasses[] = $parentClass;
354
            }
355
        }
356
357 376
        return $parentClasses;
358
    }
359
360
    /**
361
     * {@inheritdoc}
362
     */
363 12
    protected function onNotFoundMetadata(
364
        string $className,
365
        ClassMetadataBuildingContext $metadataBuildingContext
366
    ) : ?ClassMetadata {
367 12
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
368 10
            return null;
369
        }
370
371 2
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $metadataBuildingContext, $this->em);
0 ignored issues
show
Bug introduced by
It seems like $this->em can also be of type null; however, parameter $entityManager of Doctrine\ORM\Event\OnCla...ventArgs::__construct() does only seem to accept Doctrine\ORM\EntityManagerInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

371
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $metadataBuildingContext, /** @scrutinizer ignore-type */ $this->em);
Loading history...
372
373 2
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
374
375 2
        return $eventArgs->getFoundMetadata();
376
    }
377
378
    /**
379
     * {@inheritdoc}
380
     *
381
     * @throws MappingException
382
     * @throws ORMException
383
     */
384 375
    protected function doLoadMetadata(
385
        string $className,
386
        ?ClassMetadata $parent,
387
        ClassMetadataBuildingContext $metadataBuildingContext
388
    ) : ?ComponentMetadata {
389
        /** @var ClassMetadata $classMetadata */
390 375
        $classMetadata = $this->driver->loadMetadataForClass($className, $parent, $metadataBuildingContext);
391
392 369
        $this->completeIdentifierGeneratorMappings($classMetadata);
393 369
        $this->completeRuntimeMetadata($classMetadata, $parent);
394
395 369
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
396 6
            $eventArgs = new LoadClassMetadataEventArgs($classMetadata, $this->em);
0 ignored issues
show
Bug introduced by
It seems like $this->em can also be of type null; however, parameter $entityManager of Doctrine\ORM\Event\LoadC...ventArgs::__construct() does only seem to accept Doctrine\ORM\EntityManagerInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

396
            $eventArgs = new LoadClassMetadataEventArgs($classMetadata, /** @scrutinizer ignore-type */ $this->em);
Loading history...
397
398 6
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
399
        }
400
401 369
        $this->buildValueGenerationPlan($classMetadata);
402 369
        $this->validateRuntimeMetadata($classMetadata, $parent);
403
404 364
        return $classMetadata;
405
    }
406
407 369
    protected function completeRuntimeMetadata(ClassMetadata $class, ?ClassMetadata $parent = null) : void
408
    {
409 369
        if (! $parent || ! $parent->isMappedSuperclass) {
410 369
            return;
411
        }
412
413 27
        if ($class->isMappedSuperclass) {
414 1
            return;
415
        }
416
417 27
        $tableName = $class->getTableName();
418
419
        // Resolve column table names
420 27
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
421 27
            if ($property instanceof FieldMetadata) {
422 27
                $property->setTableName($property->getTableName() ?? $tableName);
423
424 27
                continue;
425
            }
426
427 12
            if (! ($property instanceof ToOneAssociationMetadata)) {
428 12
                continue;
429
            }
430
431
            // Resolve association join column table names
432 9
            foreach ($property->getJoinColumns() as $joinColumn) {
433
                /** @var JoinColumnMetadata $joinColumn */
434 9
                $joinColumn->setTableName($joinColumn->getTableName() ?? $tableName);
435
            }
436
        }
437 27
    }
438
439
    /**
440
     * Validate runtime metadata is correctly defined.
441
     *
442
     * @throws MappingException
443
     */
444 369
    protected function validateRuntimeMetadata(ClassMetadata $class, ?ClassMetadata $parent = null) : void
445
    {
446 369
        if (! $class->getReflectionClass()) {
447
            // only validate if there is a reflection class instance
448
            return;
449
        }
450
451 369
        $class->validateIdentifier();
452 367
        $class->validateAssociations();
453 367
        $class->validateLifecycleCallbacks($this->getReflectionService());
454
455
        // verify inheritance
456 367
        if (! $class->isMappedSuperclass && $class->inheritanceType !== InheritanceType::NONE) {
457 76
            if (! $parent) {
458 75
                if (! $class->discriminatorMap) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->discriminatorMap of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
459 3
                    throw MappingException::missingDiscriminatorMap($class->getClassName());
460
                }
461
462 72
                if (! $class->discriminatorColumn) {
463 73
                    throw MappingException::missingDiscriminatorColumn($class->getClassName());
464
                }
465
            }
466 329
        } elseif (($class->discriminatorMap || $class->discriminatorColumn) && $class->isMappedSuperclass && $class->isRootEntity()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->discriminatorMap of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
467
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
468
            throw MappingException::noInheritanceOnMappedSuperClass($class->getClassName());
469
        }
470 364
    }
471
472
    /**
473
     * {@inheritdoc}
474
     */
475 1965
    protected function newClassMetadataBuildingContext() : ClassMetadataBuildingContext
476
    {
477 1965
        return new ClassMetadataBuildingContext(
478 1965
            $this,
479 1965
            $this->getReflectionService(),
480 1965
            $this->em->getConfiguration()->getNamingStrategy()
481
        );
482
    }
483
484
    /**
485
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
486
     * map classes and looking for a fitting one.
487
     *
488
     * @throws InvalidArgumentException
489
     * @throws ReflectionException
490
     * @throws MappingException
491
     * @throws PersistenceMappingException
492
     */
493 363
    private function resolveDiscriminatorValue(ClassMetadata $metadata) : void
494
    {
495 363
        if ($metadata->discriminatorValue || ! $metadata->discriminatorMap || $metadata->isMappedSuperclass ||
0 ignored issues
show
Bug Best Practice introduced by
The expression $metadata->discriminatorMap of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
496 363
            ! $metadata->getReflectionClass() || $metadata->getReflectionClass()->isAbstract()) {
497 363
            return;
498
        }
499
500
        // minor optimization: avoid loading related metadata when not needed
501 4
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
502 4
            if ($discriminatorClass === $metadata->getClassName()) {
503 3
                $metadata->discriminatorValue = $discriminatorValue;
504
505 3
                return;
506
            }
507
        }
508
509
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
510 1
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
511 1
            if ($metadata->getClassName() === $this->getMetadataFor($discriminatorClass)->getClassName()) {
512
                $metadata->discriminatorValue = $discriminatorValue;
513
514
                return;
515
            }
516
        }
517
518 1
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->getClassName(), $metadata->getRootClassName());
519
    }
520
521
    /**
522
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
523
     * most appropriate for the targeted database platform.
524
     *
525
     * @throws ORMException
526
     */
527 369
    private function completeIdentifierGeneratorMappings(ClassMetadata $class) : void
528
    {
529 369
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
530 367
            if (! $property instanceof FieldMetadata /*&& ! $property instanceof AssociationMetadata*/) {
531 251
                continue;
532
            }
533
534 365
            $this->completeFieldIdentifierGeneratorMapping($property);
535
        }
536 369
    }
537
538 365
    private function completeFieldIdentifierGeneratorMapping(FieldMetadata $field)
539
    {
540 365
        if (! $field->hasValueGenerator()) {
541 280
            return;
542
        }
543
544 292
        $platform  = $this->getTargetPlatform();
545 292
        $class     = $field->getDeclaringClass();
0 ignored issues
show
Unused Code introduced by
The assignment to $class is dead and can be removed.
Loading history...
546 292
        $generator = $field->getValueGenerator();
547
548 292
        if (in_array($generator->getType(), [GeneratorType::AUTO, GeneratorType::IDENTITY], true)) {
549 285
            $generatorType = $platform->prefersSequences() || $platform->usesSequenceEmulatedIdentityColumns()
550
                ? GeneratorType::SEQUENCE
551 285
                : ($platform->prefersIdentityColumns() ? GeneratorType::IDENTITY : GeneratorType::TABLE);
552
553 285
            $generator = new ValueGeneratorMetadata($generatorType, $field->getValueGenerator()->getDefinition());
554
555 285
            $field->setValueGenerator($generator);
556
        }
557
558
        // Validate generator definition and set defaults where needed
559 292
        switch ($generator->getType()) {
560 292
            case GeneratorType::SEQUENCE:
561
                // If there is no sequence definition yet, create a default definition
562 6
                if ($generator->getDefinition()) {
563 6
                    break;
564
                }
565
566
                // @todo guilhermeblanco Move sequence generation to DBAL
567
                $sequencePrefix = $platform->getSequencePrefix($field->getTableName(), $field->getSchemaName());
0 ignored issues
show
Bug introduced by
The method getSchemaName() does not exist on Doctrine\ORM\Mapping\FieldMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

567
                $sequencePrefix = $platform->getSequencePrefix($field->getTableName(), $field->/** @scrutinizer ignore-call */ getSchemaName());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
568
                $idSequenceName = sprintf('%s_%s_seq', $sequencePrefix, $field->getColumnName());
569
                $sequenceName   = $platform->fixSchemaElementName($idSequenceName);
570
571
                $field->setValueGenerator(
572
                    new ValueGeneratorMetadata(
573
                        $generator->getType(),
574
                        [
575
                            'sequenceName'   => $sequenceName,
576
                            'allocationSize' => 1,
577
                        ]
578
                    )
579
                );
580
581
                break;
582
583 286
            case GeneratorType::TABLE:
584
                throw TableGeneratorNotImplementedYet::create();
585
                break;
586
587 286
            case GeneratorType::CUSTOM:
588 285
            case GeneratorType::IDENTITY:
589
            case GeneratorType::NONE:
590 286
                break;
591
592
            default:
593
                throw UnknownGeneratorType::create($generator->getType());
594
        }
595 292
    }
596
597 367
    private function getTargetPlatform() : Platforms\AbstractPlatform
598
    {
599 367
        if (! $this->targetPlatform) {
600 367
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
601
        }
602
603 367
        return $this->targetPlatform;
604
    }
605
606 369
    private function buildValueGenerationPlan(ClassMetadata $class) : void
607
    {
608 369
        $valueGenerationExecutorList = $this->buildValueGenerationExecutorList($class);
609
610 369
        switch (count($valueGenerationExecutorList)) {
611 369
            case 0:
612 92
                $valueGenerationPlan = new NoopValueGenerationPlan();
613 92
                break;
614
615 308
            case 1:
616 303
                $valueGenerationPlan = new SingleValueGenerationPlan($class, reset($valueGenerationExecutorList));
617 303
                break;
618
619
            default:
620 18
                $valueGenerationPlan = new CompositeValueGenerationPlan($class, $valueGenerationExecutorList);
621 18
                break;
622
        }
623
624 369
        $class->setValueGenerationPlan($valueGenerationPlan);
625 369
    }
626
627
    /**
628
     * @return ValueGenerationExecutor[]
629
     */
630 369
    private function buildValueGenerationExecutorList(ClassMetadata $class) : array
631
    {
632 369
        $executors = [];
633
634 369
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
635 367
            $executor = $property->getValueGenerationExecutor($this->getTargetPlatform());
636
637 367
            if ($executor instanceof ValueGenerationExecutor) {
638 308
                $executors[$property->getName()] = $executor;
639
            }
640
        }
641
642 369
        return $executors;
643
    }
644
}
645