Failed Conditions
Pull Request — master (#7085)
by Guilherme
14:34
created

addInheritedSqlResultSetMappings()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

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

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

100
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $metadataBuildingContext, /** @scrutinizer ignore-type */ $this->em);
Loading history...
101
102 2
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
103
104 2
        return $eventArgs->getFoundMetadata();
105
    }
106
107
    /**
108
     * {@inheritdoc}
109
     *
110
     * @throws MappingException
111
     * @throws ORMException
112
     */
113 364
    protected function doLoadMetadata(
114
        string $className,
115
        ?ClassMetadata $parent,
116
        ClassMetadataBuildingContext $metadataBuildingContext
117
    ) : ClassMetadata {
118 364
        $classMetadata = new ClassMetadata($className, $metadataBuildingContext);
119
120 364
        if ($parent) {
121 98
            $classMetadata->setParent($parent);
122
123 98
            foreach ($parent->getDeclaredPropertiesIterator() as $fieldName => $property) {
124 97
                $classMetadata->addInheritedProperty($property);
125
            }
126
127 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

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

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

459
                $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...
460
                $idSequenceName = sprintf('%s_%s_seq', $sequencePrefix, $field->getColumnName());
461
                $sequenceName   = $platform->fixSchemaElementName($idSequenceName);
462
463
                $field->setValueGenerator(
464
                    new ValueGeneratorMetadata(
465
                        $generator->getType(),
466
                        [
467
                            'sequenceName'   => $sequenceName,
468
                            'allocationSize' => 1,
469
                        ]
470
                    )
471
                );
472
473
                break;
474
475 281
            case GeneratorType::TABLE:
476
                throw new ORMException('TableGenerator not yet implemented.');
477
                break;
478
479 281
            case GeneratorType::CUSTOM:
480 1
                $definition = $generator->getDefinition();
481 1
                if (! isset($definition['class'])) {
482
                    throw new ORMException(sprintf('Cannot instantiate custom generator, no class has been defined'));
483
                }
484 1
                if (! class_exists($definition['class'])) {
485
                    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...
486
                }
487
488 1
                break;
489
490 280
            case GeneratorType::IDENTITY:
491
            case GeneratorType::NONE:
492
            case GeneratorType::UUID:
493 280
                break;
494
495
            default:
496
                throw new ORMException('Unknown generator type: ' . $generator->getType());
497
        }
498 287
    }
499
500
    /**
501
     * {@inheritDoc}
502
     */
503 198
    protected function getDriver() : Driver\MappingDriver
504
    {
505 198
        return $this->driver;
506
    }
507
508
    /**
509
     * {@inheritDoc}
510
     */
511
    protected function isEntity(ClassMetadata $class) : bool
512
    {
513
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
514
    }
515
516 287
    private function getTargetPlatform() : Platforms\AbstractPlatform
517
    {
518 287
        if (! $this->targetPlatform) {
519 287
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
520
        }
521
522 287
        return $this->targetPlatform;
523
    }
524
525 360
    private function buildValueGenerationPlan(ClassMetadata $class) : void
526
    {
527
        /** @var LocalColumnMetadata[] $generatedProperties */
528 360
        $generatedProperties = [];
529
530 360
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
531 358
            if (! ($property instanceof LocalColumnMetadata && $property->hasValueGenerator())) {
532 328
                continue;
533
            }
534
535 286
            $generatedProperties[] = $property;
536
        }
537
538 360
        switch (count($generatedProperties)) {
539 360
            case 0:
540 113
                $class->setValueGenerationPlan(new NoopValueGenerationPlan());
541 113
                break;
542
543 286
            case 1:
544 286
                $property = reset($generatedProperties);
545 286
                $executor = new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property));
546
547 286
                $class->setValueGenerationPlan(new SingleValueGenerationPlan($class, $executor));
548 286
                break;
549
550
            default:
551
                $executors = [];
552
553
                foreach ($generatedProperties as $property) {
554
                    $executors[] = new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property));
555
                }
556
557
                $class->setValueGenerationPlan(new CompositeValueGenerationPlan($class, $executors));
558
                break;
559
        }
560 360
    }
561
562 286
    private function createPropertyValueGenerator(
563
        ClassMetadata $class,
564
        LocalColumnMetadata $property
565
    ) : Sequencing\Generator {
566 286
        $platform = $this->getTargetPlatform();
567
568 286
        switch ($property->getValueGenerator()->getType()) {
569 286
            case GeneratorType::IDENTITY:
570 279
                $sequenceName = null;
571
572
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
573 279
                if ($platform->usesSequenceEmulatedIdentityColumns()) {
574
                    $sequencePrefix = $platform->getSequencePrefix($class->getTableName(), $class->getSchemaName());
575
                    $idSequenceName = $platform->getIdentitySequenceName($sequencePrefix, $property->getColumnName());
576
                    $sequenceName   = $platform->quoteIdentifier($platform->fixSchemaElementName($idSequenceName));
577
                }
578
579 279
                return $property->getTypeName() === 'bigint'
580 1
                    ? new Sequencing\BigIntegerIdentityGenerator($sequenceName)
581 279
                    : new Sequencing\IdentityGenerator($sequenceName);
582
583 7
            case GeneratorType::SEQUENCE:
584 6
                $definition = $property->getValueGenerator()->getDefinition();
585 6
                return new Sequencing\SequenceGenerator(
586 6
                    $platform->quoteIdentifier($definition['sequenceName']),
587 6
                    $definition['allocationSize']
588
                );
589
                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...
590
591 1
            case GeneratorType::UUID:
592
                return new Sequencing\UuidGenerator();
593
                break;
594
595 1
            case GeneratorType::CUSTOM:
596 1
                $class = $property->getValueGenerator()->getDefinition()['class'];
597 1
                return new $class();
598
                break;
599
        }
600
    }
601
}
602