Failed Conditions
Push — master ( 047620...906c14 )
by Guilherme
08:56
created

ClassMetadataFactory::buildValueGenerationPlan()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 3
nop 1
dl 0
loc 19
ccs 12
cts 12
cp 1
crap 3
rs 9.8666
c 0
b 0
f 0
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
394 369
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
395 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

395
            $eventArgs = new LoadClassMetadataEventArgs($classMetadata, /** @scrutinizer ignore-type */ $this->em);
Loading history...
396
397 6
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
398
        }
399
400 369
        $this->buildValueGenerationPlan($classMetadata);
401 369
        $this->validateRuntimeMetadata($classMetadata, $parent);
402
403 364
        return $classMetadata;
404
    }
405
406
    /**
407
     * Validate runtime metadata is correctly defined.
408
     *
409
     * @throws MappingException
410
     */
411 369
    protected function validateRuntimeMetadata(ClassMetadata $class, ?ClassMetadata $parent = null) : void
412
    {
413 369
        if (! $class->getReflectionClass()) {
414
            // only validate if there is a reflection class instance
415
            return;
416
        }
417
418 369
        $class->validateIdentifier();
419 367
        $class->validateAssociations();
420 367
        $class->validateLifecycleCallbacks($this->getReflectionService());
421
422
        // verify inheritance
423 367
        if (! $class->isMappedSuperclass && $class->inheritanceType !== InheritanceType::NONE) {
424 76
            if (! $parent) {
425 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...
426 3
                    throw MappingException::missingDiscriminatorMap($class->getClassName());
427
                }
428
429 72
                if (! $class->discriminatorColumn) {
430 73
                    throw MappingException::missingDiscriminatorColumn($class->getClassName());
431
                }
432
            }
433 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...
434
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
435
            throw MappingException::noInheritanceOnMappedSuperClass($class->getClassName());
436
        }
437 364
    }
438
439
    /**
440
     * {@inheritdoc}
441
     */
442 1965
    protected function newClassMetadataBuildingContext() : ClassMetadataBuildingContext
443
    {
444 1965
        return new ClassMetadataBuildingContext(
445 1965
            $this,
446 1965
            $this->getReflectionService(),
447 1965
            $this->em->getConfiguration()->getNamingStrategy()
448
        );
449
    }
450
451
    /**
452
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
453
     * map classes and looking for a fitting one.
454
     *
455
     * @throws InvalidArgumentException
456
     * @throws ReflectionException
457
     * @throws MappingException
458
     * @throws PersistenceMappingException
459
     */
460 363
    private function resolveDiscriminatorValue(ClassMetadata $metadata) : void
461
    {
462 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...
463 363
            ! $metadata->getReflectionClass() || $metadata->getReflectionClass()->isAbstract()) {
464 363
            return;
465
        }
466
467
        // minor optimization: avoid loading related metadata when not needed
468 4
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
469 4
            if ($discriminatorClass === $metadata->getClassName()) {
470 3
                $metadata->discriminatorValue = $discriminatorValue;
471
472 3
                return;
473
            }
474
        }
475
476
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
477 1
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
478 1
            if ($metadata->getClassName() === $this->getMetadataFor($discriminatorClass)->getClassName()) {
479
                $metadata->discriminatorValue = $discriminatorValue;
480
481
                return;
482
            }
483
        }
484
485 1
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->getClassName(), $metadata->getRootClassName());
486
    }
487
488
    /**
489
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
490
     * most appropriate for the targeted database platform.
491
     *
492
     * @throws ORMException
493
     */
494 369
    private function completeIdentifierGeneratorMappings(ClassMetadata $class) : void
495
    {
496 369
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
497 367
            if (! $property instanceof FieldMetadata /*&& ! $property instanceof AssociationMetadata*/) {
498 251
                continue;
499
            }
500
501 365
            $this->completeFieldIdentifierGeneratorMapping($property);
502
        }
503 369
    }
504
505 365
    private function completeFieldIdentifierGeneratorMapping(FieldMetadata $field)
506
    {
507 365
        if (! $field->hasValueGenerator()) {
508 280
            return;
509
        }
510
511 292
        $platform  = $this->getTargetPlatform();
512 292
        $class     = $field->getDeclaringClass();
0 ignored issues
show
Unused Code introduced by
The assignment to $class is dead and can be removed.
Loading history...
513 292
        $generator = $field->getValueGenerator();
514
515 292
        if (in_array($generator->getType(), [GeneratorType::AUTO, GeneratorType::IDENTITY], true)) {
516 285
            $generatorType = $platform->prefersSequences() || $platform->usesSequenceEmulatedIdentityColumns()
517
                ? GeneratorType::SEQUENCE
518 285
                : ($platform->prefersIdentityColumns() ? GeneratorType::IDENTITY : GeneratorType::TABLE);
519
520 285
            $generator = new ValueGeneratorMetadata($generatorType, $field->getValueGenerator()->getDefinition());
521
522 285
            $field->setValueGenerator($generator);
523
        }
524
525
        // Validate generator definition and set defaults where needed
526 292
        switch ($generator->getType()) {
527 292
            case GeneratorType::SEQUENCE:
528
                // If there is no sequence definition yet, create a default definition
529 6
                if ($generator->getDefinition()) {
530 6
                    break;
531
                }
532
533
                // @todo guilhermeblanco Move sequence generation to DBAL
534
                $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

534
                $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...
535
                $idSequenceName = sprintf('%s_%s_seq', $sequencePrefix, $field->getColumnName());
536
                $sequenceName   = $platform->fixSchemaElementName($idSequenceName);
537
538
                $field->setValueGenerator(
539
                    new ValueGeneratorMetadata(
540
                        $generator->getType(),
541
                        [
542
                            'sequenceName'   => $sequenceName,
543
                            'allocationSize' => 1,
544
                        ]
545
                    )
546
                );
547
548
                break;
549
550 286
            case GeneratorType::TABLE:
551
                throw TableGeneratorNotImplementedYet::create();
552
                break;
553
554 286
            case GeneratorType::CUSTOM:
555 285
            case GeneratorType::IDENTITY:
556
            case GeneratorType::NONE:
557 286
                break;
558
559
            default:
560
                throw UnknownGeneratorType::create($generator->getType());
561
        }
562 292
    }
563
564 367
    private function getTargetPlatform() : Platforms\AbstractPlatform
565
    {
566 367
        if (! $this->targetPlatform) {
567 367
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
568
        }
569
570 367
        return $this->targetPlatform;
571
    }
572
573 369
    private function buildValueGenerationPlan(ClassMetadata $class) : void
574
    {
575 369
        $valueGenerationExecutorList = $this->buildValueGenerationExecutorList($class);
576
577 369
        switch (count($valueGenerationExecutorList)) {
578 369
            case 0:
579 92
                $valueGenerationPlan = new NoopValueGenerationPlan();
580 92
                break;
581
582 308
            case 1:
583 303
                $valueGenerationPlan = new SingleValueGenerationPlan($class, reset($valueGenerationExecutorList));
584 303
                break;
585
586
            default:
587 18
                $valueGenerationPlan = new CompositeValueGenerationPlan($class, $valueGenerationExecutorList);
588 18
                break;
589
        }
590
591 369
        $class->setValueGenerationPlan($valueGenerationPlan);
592 369
    }
593
594
    /**
595
     * @return ValueGenerationExecutor[]
596
     */
597 369
    private function buildValueGenerationExecutorList(ClassMetadata $class) : array
598
    {
599 369
        $executors = [];
600
601 369
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
602 367
            $executor = $property->getValueGenerationExecutor($this->getTargetPlatform());
603
604 367
            if ($executor instanceof ValueGenerationExecutor) {
605 308
                $executors[$property->getName()] = $executor;
606
            }
607
        }
608
609 369
        return $executors;
610
    }
611
}
612