Failed Conditions
Pull Request — develop (#6935)
by Michael
167:08 queued 149:28
created

ClassMetadataFactory::completeRuntimeMetadata()   C

Complexity

Conditions 8
Paths 6

Size

Total Lines 28
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 13
nc 6
nop 2
dl 0
loc 28
ccs 14
cts 14
cp 1
crap 8
rs 5.3846
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
 * @since   2.0
26
 * @author  Benjamin Eberlei <[email protected]>
27
 * @author  Guilherme Blanco <[email protected]>
28
 * @author  Jonathan Wage <[email protected]>
29
 * @author  Roman Borschel <[email protected]>
30
 */
31
class ClassMetadataFactory extends AbstractClassMetadataFactory
32
{
33
    /**
34
     * @var EntityManagerInterface|null
35
     */
36
    private $em;
37
38
    /**
39
     * @var \Doctrine\DBAL\Platforms\AbstractPlatform
40
     */
41
    private $targetPlatform;
42
43
    /**
44
     * @var Driver\MappingDriver
45
     */
46
    private $driver;
47
48
    /**
49
     * @var \Doctrine\Common\EventManager
50
     */
51
    private $evm;
52
53
    /**
54
     * {@inheritdoc}
55
     */
56 378
    protected function loadMetadata(string $name, ClassMetadataBuildingContext $metadataBuildingContext) : array
57
    {
58 378
        $loaded = parent::loadMetadata($name, $metadataBuildingContext);
59
60 358
        array_map([$this, 'resolveDiscriminatorValue'], $loaded);
61
62 358
        return $loaded;
63
    }
64
65
    /**
66
     * @param EntityManagerInterface $em
67
     */
68 2261
    public function setEntityManager(EntityManagerInterface $em)
69
    {
70 2261
        $this->em = $em;
71 2261
    }
72
73
    /**
74
     * {@inheritdoc}
75
     *
76
     * @throws ORMException
77
     */
78 444
    protected function initialize() : void
79
    {
80 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

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

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

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

175
            $eventArgs = new LoadClassMetadataEventArgs($classMetadata, /** @scrutinizer ignore-type */ $this->em);
Loading history...
176
177 6
            $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
178
        }
179
180 363
        $this->buildValueGenerationPlan($classMetadata);
181 363
        $this->validateRuntimeMetadata($classMetadata, $parent);
182
183 360
        return $classMetadata;
184
    }
185
186
    /**
187
     * @param ClassMetadata      $class
188
     * @param ClassMetadata|null $parent
189
     *
190
     * @return void
191
     */
192 364
    protected function completeRuntimeMetadata(ClassMetadata $class, ClassMetadata $parent = null) : void
193
    {
194 364
        if (! $parent || ! $parent->isMappedSuperclass) {
195 364
            return;
196
        }
197
198 29
        if ($class->isMappedSuperclass) {
199 1
            return;
200
        }
201
202 29
        $tableName = $class->getTableName();
203
204
        // Resolve column table names
205 29
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
206 29
            if ($property instanceof FieldMetadata) {
207 29
                $property->setTableName($property->getTableName() ?? $tableName);
208
209 29
                continue;
210
            }
211
212 12
            if (! ($property instanceof ToOneAssociationMetadata)) {
213 12
                continue;
214
            }
215
216
            // Resolve association join column table names
217 9
            foreach ($property->getJoinColumns() as $joinColumn) {
218
                /** @var JoinColumnMetadata $joinColumn */
219 9
                $joinColumn->setTableName($joinColumn->getTableName() ?? $tableName);
220
            }
221
        }
222 29
    }
223
224
    /**
225
     * Validate runtime metadata is correctly defined.
226
     *
227
     * @param ClassMetadata      $class
228
     * @param ClassMetadata|null $parent
229
     *
230
     * @return void
231
     *
232
     * @throws MappingException
233
     */
234 363
    protected function validateRuntimeMetadata(ClassMetadata $class, ClassMetadata $parent = null) : void
235
    {
236 363
        if (! $class->getReflectionClass()) {
237
            // only validate if there is a reflection class instance
238
            return;
239
        }
240
241 363
        $class->validateIdentifier();
242 360
        $class->validateAssociations();
243 360
        $class->validateLifecycleCallbacks($this->getReflectionService());
244
245
        // verify inheritance
246 360
        if ( ! $class->isMappedSuperclass && $class->inheritanceType !== InheritanceType::NONE) {
247 75
            if ( ! $parent) {
248 73
                if (! $class->discriminatorMap) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->discriminatorMap of type array<string,string> is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
249
                    throw MappingException::missingDiscriminatorMap($class->getClassName());
250
                }
251
252 73
                if ( ! $class->discriminatorColumn) {
253 75
                    throw MappingException::missingDiscriminatorColumn($class->getClassName());
254
                }
255
            }
256 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 array<string,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...
257
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
258
            throw MappingException::noInheritanceOnMappedSuperClass($class->getClassName());
259
        }
260 360
    }
261
262
    /**
263
     * {@inheritdoc}
264
     */
265 1958
    protected function newClassMetadataBuildingContext() : ClassMetadataBuildingContext
266
    {
267 1958
        return new ClassMetadataBuildingContext(
268 1958
            $this,
269 1958
            $this->getReflectionService(),
270 1958
            $this->em->getConfiguration()->getNamingStrategy()
271
        );
272
    }
273
274
    /**
275
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
276
     * map classes and looking for a fitting one.
277
     *
278
     * @param ClassMetadata $metadata
279
     *
280
     * @return void
281
     *
282
     * @throws \InvalidArgumentException
283
     * @throws \ReflectionException
284
     * @throws MappingException
285
     */
286 358
    private function resolveDiscriminatorValue(ClassMetadata $metadata) : void
287
    {
288 358
        if ($metadata->discriminatorValue || ! $metadata->discriminatorMap || $metadata->isMappedSuperclass ||
0 ignored issues
show
Bug Best Practice introduced by
The expression $metadata->discriminatorMap of type array<string,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...
289 358
            ! $metadata->getReflectionClass() || $metadata->getReflectionClass()->isAbstract()) {
290 358
            return;
291
        }
292
293
        // minor optimization: avoid loading related metadata when not needed
294 4
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
295 4
            if ($discriminatorClass === $metadata->getClassName()) {
296 3
                $metadata->discriminatorValue = $discriminatorValue;
297
298 4
                return;
299
            }
300
        }
301
302
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
303 1
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
304 1
            if ($metadata->getClassName() === $this->getMetadataFor($discriminatorClass)->getClassName()) {
305
                $metadata->discriminatorValue = $discriminatorValue;
306
307 1
                return;
308
            }
309
        }
310
311 1
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->getClassName(), $metadata->getRootClassName());
312
    }
313
314
    /**
315
     * Adds a default discriminator map if no one is given
316
     *
317
     * If an entity is of any inheritance type and does not contain a
318
     * discriminator map, then the map is generated automatically. This process
319
     * is expensive computation wise.
320
     *
321
     * The automatically generated discriminator map contains the lowercase short name of
322
     * each class as key.
323
     *
324
     * @param \Doctrine\ORM\Mapping\ClassMetadata $class
325
     *
326
     * @throws MappingException
327
     */
328 1
    private function addDefaultDiscriminatorMap(ClassMetadata $class) : void
329
    {
330 1
        $allClasses = $this->driver->getAllClassNames();
331 1
        $fqcn       = $class->getClassName();
332 1
        $map        = [$this->getShortName($fqcn) => $fqcn];
333 1
        $duplicates = [];
334
335 1
        foreach ($allClasses as $subClassCandidate) {
336 1
            if (is_subclass_of($subClassCandidate, $fqcn)) {
337 1
                $shortName = $this->getShortName($subClassCandidate);
338
339 1
                if (isset($map[$shortName])) {
340
                    $duplicates[] = $shortName;
341
                }
342
343 1
                $map[$shortName] = $subClassCandidate;
344
            }
345
        }
346
347 1
        if ($duplicates) {
348
            throw MappingException::duplicateDiscriminatorEntry($class->getClassName(), $duplicates, $map);
349
        }
350
351 1
        $class->setDiscriminatorMap($map);
352 1
    }
353
354
    /**
355
     * Gets the lower-case short name of a class.
356
     *
357
     * @param string $className
358
     *
359
     * @return string
360
     */
361 1
    private function getShortName($className) : string
362
    {
363 1
        if (strpos($className, "\\") === false) {
364
            return strtolower($className);
365
        }
366
367 1
        $parts = explode("\\", $className);
368
369 1
        return strtolower(end($parts));
370
    }
371
372
    /**
373
     * Adds inherited fields to the subclass mapping.
374
     *
375
     * @param ClassMetadata $subClass
376
     * @param ClassMetadata $parentClass
377
     *
378
     * @return void
379
     *
380
     * @throws MappingException
381
     */
382 100
    private function addInheritedProperties(ClassMetadata $subClass, ClassMetadata $parentClass) : void
383
    {
384 100
        $isAbstract = $parentClass->isMappedSuperclass;
385
386 100
        foreach ($parentClass->getDeclaredPropertiesIterator() as $fieldName => $property) {
387 99
            if ($isAbstract && $property instanceof ToManyAssociationMetadata && ! $property->isOwningSide()) {
388 1
                throw MappingException::illegalToManyAssociationOnMappedSuperclass($parentClass->getClassName(), $fieldName);
389
            }
390
391 98
            $subClass->addInheritedProperty($property);
392
        }
393 99
    }
394
395
    /**
396
     * Adds inherited named queries to the subclass mapping.
397
     *
398
     * @since 2.2
399
     *
400
     * @param ClassMetadata $subClass
401
     * @param ClassMetadata $parentClass
402
     *
403
     * @return void
404
     *
405
     * @throws MappingException
406
     */
407 99
    private function addInheritedNamedQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void
408
    {
409 99
        foreach ($parentClass->getNamedQueries() as $name => $query) {
410 1
            if ($subClass->hasNamedQuery($name)) {
411 1
                continue;
412
            }
413
414 1
            $subClass->addNamedQuery($name, $query);
415
        }
416 99
    }
417
418
    /**
419
     * Adds inherited named native queries to the subclass mapping.
420
     *
421
     * @since 2.3
422
     *
423
     * @param ClassMetadata $subClass
424
     * @param ClassMetadata $parentClass
425
     *
426
     * @return void
427
     *
428
     * @throws MappingException
429
     */
430 7
    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void
431
    {
432 7
        foreach ($parentClass->namedNativeQueries as $name => $query) {
433 7
            if (isset($subClass->namedNativeQueries[$name])) {
434 4
                continue;
435
            }
436
437 7
            $subClass->addNamedNativeQuery(
438 7
                $name,
439 7
                $query['query'],
440
                [
441 7
                    'resultSetMapping' => $query['resultSetMapping'],
442 7
                    'resultClass'      => $query['resultClass'],
443
                ]
444
            );
445
        }
446 7
    }
447
448
    /**
449
     * Adds inherited sql result set mappings to the subclass mapping.
450
     *
451
     * @since 2.3
452
     *
453
     * @param ClassMetadata $subClass
454
     * @param ClassMetadata $parentClass
455
     *
456
     * @return void
457
     *
458
     * @throws MappingException
459
     */
460 7
    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) : void
461
    {
462 7
        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
463 7
            if (isset ($subClass->sqlResultSetMappings[$name])) {
464 4
                continue;
465
            }
466
467 7
            $entities = [];
468
469 7
            foreach ($mapping['entities'] as $entity) {
470 7
                $entities[] = [
471 7
                    'fields'              => $entity['fields'],
472 7
                    'discriminatorColumn' => $entity['discriminatorColumn'],
473 7
                    'entityClass'         => $entity['entityClass'],
474
                ];
475
            }
476
477 7
            $subClass->addSqlResultSetMapping(
478
                [
479 7
                    'name'     => $mapping['name'],
480 7
                    'columns'  => $mapping['columns'],
481 7
                    'entities' => $entities,
482
                ]
483
            );
484
        }
485 7
    }
486
487
    /**
488
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
489
     * most appropriate for the targeted database platform.
490
     *
491
     * @param ClassMetadata $class
492
     *
493
     * @return void
494
     *
495
     * @throws ORMException
496
     */
497 364
    private function completeIdentifierGeneratorMappings(ClassMetadata $class) : void
498
    {
499 364
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
500 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...
501 249
                continue;
502
            }
503
504 358
            $this->completeFieldIdentifierGeneratorMapping($property);
505
        }
506 364
    }
507
508 358
    private function completeFieldIdentifierGeneratorMapping(FieldMetadata $field)
509
    {
510 358
        if (!$field->hasValueGenerator()) {
511 278
            return;
512
        }
513
514 286
        $platform  = $this->getTargetPlatform();
515 286
        $class     = $field->getDeclaringClass();
0 ignored issues
show
Unused Code introduced by
The assignment to $class is dead and can be removed.
Loading history...
516 286
        $generator = $field->getValueGenerator();
517
518 286
        if ($generator->getType() === GeneratorType::AUTO) {
519 275
            $generator = new ValueGeneratorMetadata(
520 275
                $platform->prefersSequences()
521
                    ? GeneratorType::SEQUENCE
522 275
                    : ($platform->prefersIdentityColumns()
523 275
                        ? GeneratorType::IDENTITY
524 275
                        : GeneratorType::TABLE
525
                ),
526 275
                $field->getValueGenerator()->getDefinition()
527
            );
528 275
            $field->setValueGenerator($generator);
529
        }
530
531
        // Validate generator definition and set defaults where needed
532 286
        switch ($generator->getType()) {
533 286
            case GeneratorType::SEQUENCE:
534
                // If there is no sequence definition yet, create a default definition
535 6
                if ($generator->getDefinition()) {
536 6
                    break;
537
                }
538
539
                // @todo guilhermeblanco Move sequence generation to DBAL
540
                $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

540
                $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...
541
                $idSequenceName = sprintf('%s_%s_seq', $sequencePrefix, $field->getColumnName());
542
                $sequenceName   = $platform->fixSchemaElementName($idSequenceName);
543
544
                $field->setValueGenerator(
545
                    new ValueGeneratorMetadata(
546
                        $generator->getType(),
547
                        [
548
                            'sequenceName'   => $sequenceName,
549
                            'allocationSize' => 1,
550
                        ]
551
                    )
552
                );
553
554
                break;
555
556 280
            case GeneratorType::TABLE:
557
                throw new ORMException("TableGenerator not yet implemented.");
558
                break;
559
560 280
            case GeneratorType::CUSTOM:
561 1
                $definition = $generator->getDefinition();
562 1
                if ( ! isset($definition['class'])) {
563
                    throw new ORMException(sprintf('Cannot instantiate custom generator, no class has been defined'));
564
                }
565 1
                if ( ! class_exists($definition['class'])) {
566
                    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...
567
                }
568
569 1
                break;
570
571 279
            case GeneratorType::IDENTITY:
572
            case GeneratorType::NONE:
573
            case GeneratorType::UUID:
574 279
                break;
575
576
            default:
577
                throw new ORMException("Unknown generator type: " . $generator->getType());
578
        }
579 286
    }
580
581
    /**
582
     * {@inheritDoc}
583
     */
584 198
    protected function getDriver() : Driver\MappingDriver
585
    {
586 198
        return $this->driver;
587
    }
588
589
    /**
590
     * {@inheritDoc}
591
     */
592
    protected function isEntity(ClassMetadata $class) : bool
593
    {
594
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
595
    }
596
597
    /**
598
     * @return Platforms\AbstractPlatform
599
     */
600 286
    private function getTargetPlatform() : Platforms\AbstractPlatform
601
    {
602 286
        if (!$this->targetPlatform) {
603 286
            $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
604
        }
605
606 286
        return $this->targetPlatform;
607
    }
608
609
    /**
610
     * @param ClassMetadata $class
611
     *
612
     * @return void
613
     */
614 363
    private function buildValueGenerationPlan(ClassMetadata $class) : void
615
    {
616
        /** @var LocalColumnMetadata[] $generatedProperties */
617 363
        $generatedProperties = [];
618
619 363
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
620 360
            if (! ($property instanceof LocalColumnMetadata && $property->hasValueGenerator())) {
621 330
                continue;
622
            }
623
624 285
            $generatedProperties[] = $property;
625
        }
626
627 363
        switch (count($generatedProperties)) {
628 363
            case 0:
629 117
                $class->setValueGenerationPlan(new NoopValueGenerationPlan());
630 117
                break;
631
632 285
            case 1:
633 285
                $property  = reset($generatedProperties);
634 285
                $executor  = new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property));
635
636 285
                $class->setValueGenerationPlan(new SingleValueGenerationPlan($class, $executor));
637 285
                break;
638
639
            default:
640
                $executors = [];
641
642
                foreach ($generatedProperties as $property) {
643
                    $executors[] = new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property));
644
                }
645
646
                $class->setValueGenerationPlan(new CompositeValueGenerationPlan($class, $executors));
647
                break;
648
        }
649 363
    }
650
651
    /**
652
     * @param ClassMetadata $class
653
     * @param LocalColumnMetadata $property
654
     *
655
     * @return Sequencing\Generator
656
     */
657 285
    private function createPropertyValueGenerator(
658
        ClassMetadata $class,
659
        LocalColumnMetadata $property
660
    ) : Sequencing\Generator
661
    {
662 285
        $platform = $this->getTargetPlatform();
663
664 285
        switch ($property->getValueGenerator()->getType()) {
665 285
            case GeneratorType::IDENTITY:
666 278
                $sequenceName = null;
667
668
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
669 278
                if ($platform->usesSequenceEmulatedIdentityColumns()) {
670
                    $sequencePrefix = $platform->getSequencePrefix($class->getTableName(), $class->getSchemaName());
671
                    $idSequenceName = $platform->getIdentitySequenceName($sequencePrefix, $property->getColumnName());
672
                    $sequenceName   = $platform->quoteIdentifier($platform->fixSchemaElementName($idSequenceName));
673
                }
674
675 278
                return $property->getTypeName() === 'bigint'
676 1
                    ? new Sequencing\BigIntegerIdentityGenerator($sequenceName)
677 278
                    : new Sequencing\IdentityGenerator($sequenceName);
678
679 7
            case GeneratorType::SEQUENCE:
680 6
                $definition = $property->getValueGenerator()->getDefinition();
681 6
                return new Sequencing\SequenceGenerator(
682 6
                    $platform->quoteIdentifier($definition['sequenceName']),
683 6
                    $definition['allocationSize']
684
                );
685
                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...
686
687 1
            case GeneratorType::UUID:
688
                return new Sequencing\UuidGenerator();
689
                break;
690
691 1
            case GeneratorType::CUSTOM:
692 1
                $class = $property->getValueGenerator()->getDefinition()['class'];
693 1
                return new $class();
694
                break;
695
        }
696
    }
697
}
698