Failed Conditions
Pull Request — master (#6935)
by Michael
95:58
created

completeIdentifierGeneratorMappings()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

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

71
        $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...
72 444
        $this->evm         = $this->em->getEventManager();
73 444
        $this->initialized = true;
74 444
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79 12
    protected function onNotFoundMetadata(
80
        string $className,
81
        ClassMetadataBuildingContext $metadataBuildingContext
82
    ) : ?ClassMetadata {
83 12
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
84 10
            return null;
85
        }
86
87 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

87
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $metadataBuildingContext, /** @scrutinizer ignore-type */ $this->em);
Loading history...
88
89 2
        $this->evm->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
90
91 2
        return $eventArgs->getFoundMetadata();
92
    }
93
94
    /**
95
     * {@inheritdoc}
96
     *
97
     * @throws MappingException
98
     * @throws ORMException
99
     */
100 366
    protected function doLoadMetadata(
101
        string $className,
102
        ?ClassMetadata $parent,
103
        ClassMetadataBuildingContext $metadataBuildingContext
104
    ) : ClassMetadata {
105 366
        $classMetadata = new ClassMetadata($className, $metadataBuildingContext);
106
107 366
        if ($parent) {
108 100
            $classMetadata->setParent($parent);
109
110 100
            $this->addInheritedProperties($classMetadata, $parent);
111
112 99
            $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

112
            $classMetadata->setInheritanceType(/** @scrutinizer ignore-type */ $parent->inheritanceType);
Loading history...
113 99
            $classMetadata->setIdentifier($parent->identifier);
114
115 99
            if ($parent->discriminatorColumn) {
116 72
                $classMetadata->setDiscriminatorColumn($parent->discriminatorColumn);
117 72
                $classMetadata->setDiscriminatorMap($parent->discriminatorMap);
118
            }
119
120 99
            $classMetadata->setLifecycleCallbacks($parent->lifecycleCallbacks);
121 99
            $classMetadata->setChangeTrackingPolicy($parent->changeTrackingPolicy);
122
123 99
            if ($parent->isMappedSuperclass) {
124 29
                $classMetadata->setCustomRepositoryClassName($parent->getCustomRepositoryClassName());
125
            }
126
        }
127
128
        // Invoke driver
129
        try {
130 366
            $this->driver->loadMetadataForClass($classMetadata->getClassName(), $classMetadata, $metadataBuildingContext);
131 2
        } catch (ReflectionException $e) {
132
            throw MappingException::reflectionFailure($classMetadata->getClassName(), $e);
133
        }
134
135 364
        $this->completeIdentifierGeneratorMappings($classMetadata);
136
137 364
        if ($parent) {
138 99
            $this->addInheritedNamedQueries($classMetadata, $parent);
139
140 99
            if ($parent->getCache()) {
141 3
                $classMetadata->setCache(clone $parent->getCache());
142
            }
143
144 99
            if (! empty($parent->namedNativeQueries)) {
145 7
                $this->addInheritedNamedNativeQueries($classMetadata, $parent);
146
            }
147
148 99
            if (! empty($parent->sqlResultSetMappings)) {
149 7
                $this->addInheritedSqlResultSetMappings($classMetadata, $parent);
150
            }
151
152 99
            if (! empty($parent->entityListeners) && empty($classMetadata->entityListeners)) {
153 7
                $classMetadata->entityListeners = $parent->entityListeners;
154
            }
155
        }
156
157 364
        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...
158 1
            $this->addDefaultDiscriminatorMap($classMetadata);
159
        }
160
161 364
        $this->completeRuntimeMetadata($classMetadata, $parent);
162
163 364
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
164 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

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

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