Passed
Push — master ( 3e1d12...91acb4 )
by Luís
13:51 queued 06:19
created

buildValueGenerationExecutorForProperty()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 13
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
eloc 5
nc 3
nop 2
dl 0
loc 13
ccs 6
cts 6
cp 1
crap 5
rs 8.8571
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\EventManager;
8
use Doctrine\DBAL\Platforms;
9
use Doctrine\DBAL\Platforms\AbstractPlatform;
10
use Doctrine\ORM\EntityManagerInterface;
11
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
12
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
13
use Doctrine\ORM\Events;
14
use Doctrine\ORM\ORMException;
15
use Doctrine\ORM\Sequencing;
16
use Doctrine\ORM\Sequencing\Planning\AssociationValueGeneratorExecutor;
17
use Doctrine\ORM\Sequencing\Planning\ColumnValueGeneratorExecutor;
18
use Doctrine\ORM\Sequencing\Planning\CompositeValueGenerationPlan;
19
use Doctrine\ORM\Sequencing\Planning\NoopValueGenerationPlan;
20
use Doctrine\ORM\Sequencing\Planning\SingleValueGenerationPlan;
21
use Doctrine\ORM\Sequencing\Planning\ValueGenerationExecutor;
22
use ReflectionException;
23
use function array_map;
24
use function class_exists;
25
use function count;
26
use function end;
27
use function explode;
28
use function is_subclass_of;
29
use function sprintf;
30
use function strpos;
31
use function strtolower;
32
use function var_export;
33
34
/**
35
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
36
 * metadata mapping information of a class which describes how a class should be mapped
37
 * to a relational database.
38
 */
39
class ClassMetadataFactory extends AbstractClassMetadataFactory
40
{
41
    /**
42
     * @var EntityManagerInterface|null
43
     */
44
    private $em;
45
46
    /**
47
     * @var AbstractPlatform
48
     */
49
    private $targetPlatform;
50
51
    /**
52
     * @var Driver\MappingDriver
53
     */
54
    private $driver;
55
56
    /**
57
     * @var EventManager
58
     */
59
    private $evm;
60
61
    /**
62
     * {@inheritdoc}
63
     */
64 377
    protected function loadMetadata(string $name, ClassMetadataBuildingContext $metadataBuildingContext) : array
65
    {
66 377
        $loaded = parent::loadMetadata($name, $metadataBuildingContext);
67
68 358
        array_map([$this, 'resolveDiscriminatorValue'], $loaded);
69
70 358
        return $loaded;
71
    }
72
73 2262
    public function setEntityManager(EntityManagerInterface $em)
74
    {
75 2262
        $this->em = $em;
76 2262
    }
77
78
    /**
79
     * {@inheritdoc}
80
     *
81
     * @throws ORMException
82
     */
83 443
    protected function initialize() : void
84
    {
85 443
        $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

85
        $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...
86 443
        $this->evm         = $this->em->getEventManager();
87 443
        $this->initialized = true;
88 443
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 12
    protected function onNotFoundMetadata(
94
        string $className,
95
        ClassMetadataBuildingContext $metadataBuildingContext
96
    ) : ?ClassMetadata {
97 12
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
98 10
            return null;
99
        }
100
101 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

101
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $metadataBuildingContext, /** @scrutinizer ignore-type */ $this->em);
Loading history...
102
103 2
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
104
105 2
        return $eventArgs->getFoundMetadata();
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     *
111
     * @throws MappingException
112
     * @throws ORMException
113
     */
114 365
    protected function doLoadMetadata(
115
        string $className,
116
        ?ClassMetadata $parent,
117
        ClassMetadataBuildingContext $metadataBuildingContext
118
    ) : ClassMetadata {
119 365
        $classMetadata = new ClassMetadata($className, $metadataBuildingContext);
120
121 365
        if ($parent) {
122 98
            $classMetadata->setParent($parent);
123
124 98
            foreach ($parent->getDeclaredPropertiesIterator() as $fieldName => $property) {
125 97
                $classMetadata->addInheritedProperty($property);
126
            }
127
128 98
            $classMetadata->setInheritanceType($parent->inheritanceType);
0 ignored issues
show
Bug introduced by
$parent->inheritanceType of type string is incompatible with the type integer expected by parameter $type of Doctrine\ORM\Mapping\Cla...a::setInheritanceType(). ( Ignorable by Annotation )

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

128
            $classMetadata->setInheritanceType(/** @scrutinizer ignore-type */ $parent->inheritanceType);
Loading history...
129 98
            $classMetadata->setIdentifier($parent->identifier);
130
131 98
            if ($parent->discriminatorColumn) {
132 72
                $classMetadata->setDiscriminatorColumn($parent->discriminatorColumn);
133 72
                $classMetadata->setDiscriminatorMap($parent->discriminatorMap);
134
            }
135
136 98
            $classMetadata->setLifecycleCallbacks($parent->lifecycleCallbacks);
137 98
            $classMetadata->setChangeTrackingPolicy($parent->changeTrackingPolicy);
138
139 98
            if ($parent->isMappedSuperclass) {
140 28
                $classMetadata->setCustomRepositoryClassName($parent->getCustomRepositoryClassName());
141
            }
142
        }
143
144
        // Invoke driver
145
        try {
146 365
            $this->driver->loadMetadataForClass($classMetadata->getClassName(), $classMetadata, $metadataBuildingContext);
147 3
        } catch (ReflectionException $e) {
148
            throw MappingException::reflectionFailure($classMetadata->getClassName(), $e);
149
        }
150
151 362
        $this->completeIdentifierGeneratorMappings($classMetadata);
152
153 362
        if ($parent) {
154 98
            if ($parent->getCache()) {
155 3
                $classMetadata->setCache(clone $parent->getCache());
156
            }
157
158 98
            if (! empty($parent->namedNativeQueries)) {
159 7
                $this->addInheritedNamedNativeQueries($classMetadata, $parent);
160
            }
161
162 98
            if (! empty($parent->sqlResultSetMappings)) {
163 7
                $this->addInheritedSqlResultSetMappings($classMetadata, $parent);
164
            }
165
166 98
            if (! empty($parent->entityListeners) && empty($classMetadata->entityListeners)) {
167 7
                $classMetadata->entityListeners = $parent->entityListeners;
168
            }
169
        }
170
171 362
        if (! $classMetadata->discriminatorMap && $classMetadata->inheritanceType !== InheritanceType::NONE && $classMetadata->isRootEntity()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classMetadata->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...
172 1
            $this->addDefaultDiscriminatorMap($classMetadata);
173
        }
174
175 362
        $this->completeRuntimeMetadata($classMetadata, $parent);
176
177 362
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
178 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

178
            $eventArgs = new LoadClassMetadataEventArgs($classMetadata, /** @scrutinizer ignore-type */ $this->em);
Loading history...
179
180 6
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
181
        }
182
183 361
        $this->buildValueGenerationPlan($classMetadata);
184 361
        $this->validateRuntimeMetadata($classMetadata, $parent);
185
186 359
        return $classMetadata;
187
    }
188
189 362
    protected function completeRuntimeMetadata(ClassMetadata $class, ?ClassMetadata $parent = null) : void
190
    {
191 362
        if (! $parent || ! $parent->isMappedSuperclass) {
192 362
            return;
193
        }
194
195 28
        if ($class->isMappedSuperclass) {
196 1
            return;
197
        }
198
199 28
        $tableName = $class->getTableName();
200
201
        // Resolve column table names
202 28
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
203 28
            if ($property instanceof FieldMetadata) {
204 28
                $property->setTableName($property->getTableName() ?? $tableName);
205
206 28
                continue;
207
            }
208
209 12
            if (! ($property instanceof ToOneAssociationMetadata)) {
210 12
                continue;
211
            }
212
213
            // Resolve association join column table names
214 9
            foreach ($property->getJoinColumns() as $joinColumn) {
215
                /** @var JoinColumnMetadata $joinColumn */
216 9
                $joinColumn->setTableName($joinColumn->getTableName() ?? $tableName);
217
            }
218
        }
219 28
    }
220
221
    /**
222
     * Validate runtime metadata is correctly defined.
223
     *
224
     * @throws MappingException
225
     */
226 361
    protected function validateRuntimeMetadata(ClassMetadata $class, ?ClassMetadata $parent = null) : void
227
    {
228 361
        if (! $class->getReflectionClass()) {
229
            // only validate if there is a reflection class instance
230
            return;
231
        }
232
233 361
        $class->validateIdentifier();
234 359
        $class->validateAssociations();
235 359
        $class->validateLifecycleCallbacks($this->getReflectionService());
236
237
        // verify inheritance
238 359
        if (! $class->isMappedSuperclass && $class->inheritanceType !== InheritanceType::NONE) {
239 75
            if (! $parent) {
240 74
                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...
241
                    throw MappingException::missingDiscriminatorMap($class->getClassName());
242
                }
243
244 74
                if (! $class->discriminatorColumn) {
245 75
                    throw MappingException::missingDiscriminatorColumn($class->getClassName());
246
                }
247
            }
248 323
        } 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...
249
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
250
            throw MappingException::noInheritanceOnMappedSuperClass($class->getClassName());
251
        }
252 359
    }
253
254
    /**
255
     * {@inheritdoc}
256
     */
257 1961
    protected function newClassMetadataBuildingContext() : ClassMetadataBuildingContext
258
    {
259 1961
        return new ClassMetadataBuildingContext(
260 1961
            $this,
261 1961
            $this->getReflectionService(),
262 1961
            $this->em->getConfiguration()->getNamingStrategy()
263
        );
264
    }
265
266
    /**
267
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
268
     * map classes and looking for a fitting one.
269
     *
270
     * @throws \InvalidArgumentException
271
     * @throws \ReflectionException
272
     * @throws MappingException
273
     */
274 358
    private function resolveDiscriminatorValue(ClassMetadata $metadata) : void
275
    {
276 358
        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...
277 358
            ! $metadata->getReflectionClass() || $metadata->getReflectionClass()->isAbstract()) {
278 358
            return;
279
        }
280
281
        // minor optimization: avoid loading related metadata when not needed
282 4
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
283 4
            if ($discriminatorClass === $metadata->getClassName()) {
284 3
                $metadata->discriminatorValue = $discriminatorValue;
285
286 4
                return;
287
            }
288
        }
289
290
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
291 1
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
292 1
            if ($metadata->getClassName() === $this->getMetadataFor($discriminatorClass)->getClassName()) {
293
                $metadata->discriminatorValue = $discriminatorValue;
294
295 1
                return;
296
            }
297
        }
298
299 1
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->getClassName(), $metadata->getRootClassName());
300
    }
301
302
    /**
303
     * Adds a default discriminator map if no one is given
304
     *
305
     * If an entity is of any inheritance type and does not contain a
306
     * discriminator map, then the map is generated automatically. This process
307
     * is expensive computation wise.
308
     *
309
     * The automatically generated discriminator map contains the lowercase short name of
310
     * each class as key.
311
     *
312
     * @throws MappingException
313
     */
314 1
    private function addDefaultDiscriminatorMap(ClassMetadata $class) : void
315
    {
316 1
        $allClasses = $this->driver->getAllClassNames();
317 1
        $fqcn       = $class->getClassName();
318 1
        $map        = [$this->getShortName($fqcn) => $fqcn];
319 1
        $duplicates = [];
320
321 1
        foreach ($allClasses as $subClassCandidate) {
322 1
            if (is_subclass_of($subClassCandidate, $fqcn)) {
323 1
                $shortName = $this->getShortName($subClassCandidate);
324
325 1
                if (isset($map[$shortName])) {
326
                    $duplicates[] = $shortName;
327
                }
328
329 1
                $map[$shortName] = $subClassCandidate;
330
            }
331
        }
332
333 1
        if ($duplicates) {
334
            throw MappingException::duplicateDiscriminatorEntry($class->getClassName(), $duplicates, $map);
335
        }
336
337 1
        $class->setDiscriminatorMap($map);
338 1
    }
339
340
    /**
341
     * Gets the lower-case short name of a class.
342
     *
343
     * @param string $className
344
     */
345 1
    private function getShortName($className) : string
346
    {
347 1
        if (strpos($className, '\\') === false) {
348
            return strtolower($className);
349
        }
350
351 1
        $parts = explode('\\', $className);
352
353 1
        return strtolower(end($parts));
354
    }
355
356
    /**
357
     * Adds inherited named native queries to the subclass mapping.
358
     *
359
     * @throws MappingException
360
     */
361 7
    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void
362
    {
363 7
        foreach ($parentClass->namedNativeQueries as $name => $query) {
364 7
            if (isset($subClass->namedNativeQueries[$name])) {
365 4
                continue;
366
            }
367
368 7
            $subClass->addNamedNativeQuery(
369 7
                $name,
370 7
                $query['query'],
371
                [
372 7
                    'resultSetMapping' => $query['resultSetMapping'],
373 7
                    'resultClass'      => $query['resultClass'],
374
                ]
375
            );
376
        }
377 7
    }
378
379
    /**
380
     * Adds inherited sql result set mappings to the subclass mapping.
381
     *
382
     * @throws MappingException
383
     */
384 7
    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) : void
385
    {
386 7
        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
387 7
            if (isset($subClass->sqlResultSetMappings[$name])) {
388 4
                continue;
389
            }
390
391 7
            $entities = [];
392
393 7
            foreach ($mapping['entities'] as $entity) {
394 7
                $entities[] = [
395 7
                    'fields'              => $entity['fields'],
396 7
                    'discriminatorColumn' => $entity['discriminatorColumn'],
397 7
                    'entityClass'         => $entity['entityClass'],
398
                ];
399
            }
400
401 7
            $subClass->addSqlResultSetMapping(
402
                [
403 7
                    'name'     => $mapping['name'],
404 7
                    'columns'  => $mapping['columns'],
405 7
                    'entities' => $entities,
406
                ]
407
            );
408
        }
409 7
    }
410
411
    /**
412
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
413
     * most appropriate for the targeted database platform.
414
     *
415
     * @throws ORMException
416
     */
417 362
    private function completeIdentifierGeneratorMappings(ClassMetadata $class) : void
418
    {
419 362
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
420 360
            if (! $property instanceof FieldMetadata /*&& ! $property instanceof AssocationMetadata*/) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
421 249
                continue;
422
            }
423
424 358
            $this->completeFieldIdentifierGeneratorMapping($property);
425
        }
426 362
    }
427
428 358
    private function completeFieldIdentifierGeneratorMapping(FieldMetadata $field)
429
    {
430 358
        if (! $field->hasValueGenerator()) {
431 278
            return;
432
        }
433
434 288
        $platform  = $this->getTargetPlatform();
435 288
        $class     = $field->getDeclaringClass();
0 ignored issues
show
Unused Code introduced by
The assignment to $class is dead and can be removed.
Loading history...
436 288
        $generator = $field->getValueGenerator();
437
438 288
        if ($generator->getType() === GeneratorType::AUTO) {
439 278
            $generator = new ValueGeneratorMetadata(
440 278
                $platform->prefersSequences()
441
                    ? GeneratorType::SEQUENCE
442 278
                    : ($platform->prefersIdentityColumns()
443 278
                        ? GeneratorType::IDENTITY
444 278
                        : GeneratorType::TABLE
445
                ),
446 278
                $field->getValueGenerator()->getDefinition()
447
            );
448 278
            $field->setValueGenerator($generator);
449
        }
450
451
        // Validate generator definition and set defaults where needed
452 288
        switch ($generator->getType()) {
453 288
            case GeneratorType::SEQUENCE:
454
                // If there is no sequence definition yet, create a default definition
455 6
                if ($generator->getDefinition()) {
456 6
                    break;
457
                }
458
459
                // @todo guilhermeblanco Move sequence generation to DBAL
460
                $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

460
                $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...
461
                $idSequenceName = sprintf('%s_%s_seq', $sequencePrefix, $field->getColumnName());
462
                $sequenceName   = $platform->fixSchemaElementName($idSequenceName);
463
464
                $field->setValueGenerator(
465
                    new ValueGeneratorMetadata(
466
                        $generator->getType(),
467
                        [
468
                            'sequenceName'   => $sequenceName,
469
                            'allocationSize' => 1,
470
                        ]
471
                    )
472
                );
473
474
                break;
475
476 282
            case GeneratorType::TABLE:
477
                throw new ORMException('TableGenerator not yet implemented.');
478
                break;
479
480 282
            case GeneratorType::CUSTOM:
481 1
                $definition = $generator->getDefinition();
482 1
                if (! isset($definition['class'])) {
483
                    throw new ORMException(sprintf('Cannot instantiate custom generator, no class has been defined'));
484
                }
485 1
                if (! class_exists($definition['class'])) {
486
                    throw new ORMException(sprintf('Cannot instantiate custom generator : %s', var_export($definition, true))); //$definition['class']));
0 ignored issues
show
Unused Code Comprehensibility introduced by
100% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
487
                }
488
489 1
                break;
490
491 281
            case GeneratorType::IDENTITY:
492
            case GeneratorType::NONE:
493
            case GeneratorType::UUID:
494 281
                break;
495
496
            default:
497
                throw new ORMException('Unknown generator type: ' . $generator->getType());
498
        }
499 288
    }
500
501
    /**
502
     * {@inheritDoc}
503
     */
504 198
    protected function getDriver() : Driver\MappingDriver
505
    {
506 198
        return $this->driver;
507
    }
508
509
    /**
510
     * {@inheritDoc}
511
     */
512
    protected function isEntity(ClassMetadata $class) : bool
513
    {
514
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
515
    }
516
517 288
    private function getTargetPlatform() : Platforms\AbstractPlatform
518
    {
519 288
        if (! $this->targetPlatform) {
520 288
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
521
        }
522
523 288
        return $this->targetPlatform;
524
    }
525
526 361
    private function buildValueGenerationPlan(ClassMetadata $class) : void
527
    {
528 361
        $executors = $this->buildValueGenerationExecutorList($class);
529
530 361
        switch (count($executors)) {
531 361
            case 0:
532 90
                $class->setValueGenerationPlan(new NoopValueGenerationPlan());
533 90
                break;
534
535 301
            case 1:
536 296
                $class->setValueGenerationPlan(new SingleValueGenerationPlan($class, $executors[0]));
537 296
                break;
538
539
            default:
540 17
                $class->setValueGenerationPlan(new CompositeValueGenerationPlan($class, $executors));
541 17
                break;
542
        }
543 361
    }
544
545
    /**
546
     * @return ValueGenerationExecutor[]
547
     */
548 361
    private function buildValueGenerationExecutorList(ClassMetadata $class) : array
549
    {
550 361
        $executors = [];
551
552 361
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
553 359
            $executor = $this->buildValueGenerationExecutorForProperty($class, $property);
554
555 359
            if ($executor instanceof ValueGenerationExecutor) {
556 359
                $executors[] = $executor;
557
            }
558
        }
559
560 361
        return $executors;
561
    }
562
563 359
    private function buildValueGenerationExecutorForProperty(
564
        ClassMetadata $class,
565
        Property $property
566
    ) : ?ValueGenerationExecutor {
567 359
        if ($property instanceof LocalColumnMetadata && $property->hasValueGenerator()) {
568 287
            return new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property));
569
        }
570
571 329
        if ($property instanceof ToOneAssociationMetadata && $property->isPrimaryKey()) {
572 39
            return new AssociationValueGeneratorExecutor();
573
        }
574
575 325
        return null;
576
    }
577
578 287
    private function createPropertyValueGenerator(
579
        ClassMetadata $class,
580
        LocalColumnMetadata $property
581
    ) : Sequencing\Generator {
582 287
        $platform = $this->getTargetPlatform();
583
584 287
        switch ($property->getValueGenerator()->getType()) {
585 287
            case GeneratorType::IDENTITY:
586 280
                $sequenceName = null;
587
588
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
589 280
                if ($platform->usesSequenceEmulatedIdentityColumns()) {
590
                    $sequencePrefix = $platform->getSequencePrefix($class->getTableName(), $class->getSchemaName());
591
                    $idSequenceName = $platform->getIdentitySequenceName($sequencePrefix, $property->getColumnName());
592
                    $sequenceName   = $platform->quoteIdentifier($platform->fixSchemaElementName($idSequenceName));
593
                }
594
595 280
                return $property->getTypeName() === 'bigint'
596 1
                    ? new Sequencing\BigIntegerIdentityGenerator($sequenceName)
597 280
                    : new Sequencing\IdentityGenerator($sequenceName);
598
599 7
            case GeneratorType::SEQUENCE:
600 6
                $definition = $property->getValueGenerator()->getDefinition();
601 6
                return new Sequencing\SequenceGenerator(
602 6
                    $platform->quoteIdentifier($definition['sequenceName']),
603 6
                    $definition['allocationSize']
604
                );
605
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
606
607 1
            case GeneratorType::UUID:
608
                return new Sequencing\UuidGenerator();
609
                break;
610
611 1
            case GeneratorType::CUSTOM:
612 1
                $class = $property->getValueGenerator()->getDefinition()['class'];
613 1
                return new $class();
614
                break;
615
        }
616
    }
617
}
618