Failed Conditions
Pull Request — master (#6959)
by Matthew
19:32
created

newClassMetadataBuildingContext()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
ccs 5
cts 5
cp 1
crap 1
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 385
    protected function loadMetadata(string $name, ClassMetadataBuildingContext $metadataBuildingContext) : array
51
    {
52 385
        $loaded = parent::loadMetadata($name, $metadataBuildingContext);
53
54 358
        array_map([$this, 'resolveDiscriminatorValue'], $loaded);
55
56 358
        return $loaded;
57
    }
58
59 2204
    public function setEntityManager(EntityManagerInterface $em)
60
    {
61 2204
        $this->em = $em;
62 2204
    }
63
64
    /**
65
     * {@inheritdoc}
66
     *
67
     * @throws ORMException
68
     */
69 452
    protected function initialize() : void
70
    {
71 452
        $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 452
        $this->evm         = $this->em->getEventManager();
73 452
        $this->initialized = true;
74 452
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79 19
    protected function onNotFoundMetadata(
80
        string $className,
81
        ClassMetadataBuildingContext $metadataBuildingContext
82
    ) : ?ClassMetadata {
83 19
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
84 17
            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
            $parent_cache = $parent->getCache();
141
            
142 99
            if ($parent_cache) {
143 3
                $classMetadata->setCache(clone $parent_cache);
144
            }
145
146 99
            if (! empty($parent->namedNativeQueries)) {
147 7
                $this->addInheritedNamedNativeQueries($classMetadata, $parent);
148
            }
149
150 99
            if (! empty($parent->sqlResultSetMappings)) {
151 7
                $this->addInheritedSqlResultSetMappings($classMetadata, $parent);
152
            }
153
154 99
            if (! empty($parent->entityListeners) && empty($classMetadata->entityListeners)) {
155 7
                $classMetadata->entityListeners = $parent->entityListeners;
156
            }
157
        }
158
159 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...
160 1
            $this->addDefaultDiscriminatorMap($classMetadata);
161
        }
162
163 364
        $this->completeRuntimeMetadata($classMetadata, $parent);
164
165 364
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
166 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

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

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