Passed
Pull Request — master (#7054)
by Luís
12:52
created

ClassMetadataFactory   F

Complexity

Total Complexity 102

Size/Duplication

Total Lines 552
Duplicated Lines 0 %

Test Coverage

Coverage 87.35%

Importance

Changes 0
Metric Value
wmc 102
dl 0
loc 552
ccs 214
cts 245
cp 0.8735
rs 1.5789
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A isEntity() 0 3 2
A initialize() 0 5 1
A getShortName() 0 9 2
B addInheritedSqlResultSetMappings() 0 22 4
C buildValueGenerationPlan() 0 25 8
B addDefaultDiscriminatorMap() 0 24 5
A setEntityManager() 0 3 1
C completeFieldIdentifierGeneratorMapping() 0 70 14
A getTargetPlatform() 0 7 2
A getDriver() 0 3 1
D doLoadMetadata() 0 73 16
A addInheritedNamedNativeQueries() 0 13 3
A completeIdentifierGeneratorMappings() 0 8 3
A loadMetadata() 0 7 1
C validateRuntimeMetadata() 0 25 11
D resolveDiscriminatorValue() 0 26 10
C createPropertyValueGenerator() 0 37 7
A onNotFoundMetadata() 0 13 2
C completeRuntimeMetadata() 0 28 8
A newClassMetadataBuildingContext() 0 6 1

How to fix   Complexity   

Complex Class

Complex classes like ClassMetadataFactory often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ClassMetadataFactory, and based on these observations, apply Extract Interface, too.

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

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

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

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

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

129
            $classMetadata->setInheritanceType(/** @scrutinizer ignore-type */ $parent->inheritanceType);
Loading history...
130 98
            $classMetadata->setIdentifier($parent->identifier);
131
132 98
            if ($parent->discriminatorColumn) {
133 72
                $classMetadata->setDiscriminatorColumn($parent->discriminatorColumn);
134 72
                $classMetadata->setDiscriminatorMap($parent->discriminatorMap);
135
            }
136
137 98
            $classMetadata->setLifecycleCallbacks($parent->lifecycleCallbacks);
138 98
            $classMetadata->setChangeTrackingPolicy($parent->changeTrackingPolicy);
139
140 98
            if ($parent->isMappedSuperclass) {
141 28
                $classMetadata->setCustomRepositoryClassName($parent->getCustomRepositoryClassName());
142
            }
143
        }
144
145
        // Invoke driver
146
        try {
147 365
            $this->driver->loadMetadataForClass($classMetadata->getClassName(), $classMetadata, $metadataBuildingContext);
148 3
        } catch (ReflectionException $e) {
149
            throw MappingException::reflectionFailure($classMetadata->getClassName(), $e);
150
        }
151
152 362
        $this->completeIdentifierGeneratorMappings($classMetadata);
153
154 362
        if ($parent) {
155 98
            if ($parent->getCache()) {
156 3
                $classMetadata->setCache(clone $parent->getCache());
157
            }
158
159 98
            if (! empty($parent->namedNativeQueries)) {
160 7
                $this->addInheritedNamedNativeQueries($classMetadata, $parent);
161
            }
162
163 98
            if (! empty($parent->sqlResultSetMappings)) {
164 7
                $this->addInheritedSqlResultSetMappings($classMetadata, $parent);
165
            }
166
167 98
            if (! empty($parent->entityListeners) && empty($classMetadata->entityListeners)) {
168 7
                $classMetadata->entityListeners = $parent->entityListeners;
169
            }
170
        }
171
172 362
        if (! $classMetadata->discriminatorMap && $classMetadata->inheritanceType !== InheritanceType::NONE && $classMetadata->isRootEntity()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classMetadata->discriminatorMap of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
173 1
            $this->addDefaultDiscriminatorMap($classMetadata);
174
        }
175
176 362
        $this->completeRuntimeMetadata($classMetadata, $parent);
177
178 362
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
179 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

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

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

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

Loading history...
242
                    throw MappingException::missingDiscriminatorMap($class->getClassName());
243
                }
244
245 74
                if (! $class->discriminatorColumn) {
246 75
                    throw MappingException::missingDiscriminatorColumn($class->getClassName());
247
                }
248
            }
249 323
        } elseif (($class->discriminatorMap || $class->discriminatorColumn) && $class->isMappedSuperclass && $class->isRootEntity()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->discriminatorMap of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
250
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
251
            throw MappingException::noInheritanceOnMappedSuperClass($class->getClassName());
252
        }
253 359
    }
254
255
    /**
256
     * {@inheritdoc}
257
     */
258 1961
    protected function newClassMetadataBuildingContext() : ClassMetadataBuildingContext
259
    {
260 1961
        return new ClassMetadataBuildingContext(
261 1961
            $this,
262 1961
            $this->getReflectionService(),
263 1961
            $this->em->getConfiguration()->getNamingStrategy()
264
        );
265
    }
266
267
    /**
268
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
269
     * map classes and looking for a fitting one.
270
     *
271
     * @throws \InvalidArgumentException
272
     * @throws \ReflectionException
273
     * @throws MappingException
274
     */
275 358
    private function resolveDiscriminatorValue(ClassMetadata $metadata) : void
276
    {
277 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...
278 358
            ! $metadata->getReflectionClass() || $metadata->getReflectionClass()->isAbstract()) {
279 358
            return;
280
        }
281
282
        // minor optimization: avoid loading related metadata when not needed
283 4
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
284 4
            if ($discriminatorClass === $metadata->getClassName()) {
285 3
                $metadata->discriminatorValue = $discriminatorValue;
286
287 4
                return;
288
            }
289
        }
290
291
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
292 1
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
293 1
            if ($metadata->getClassName() === $this->getMetadataFor($discriminatorClass)->getClassName()) {
294
                $metadata->discriminatorValue = $discriminatorValue;
295
296 1
                return;
297
            }
298
        }
299
300 1
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->getClassName(), $metadata->getRootClassName());
301
    }
302
303
    /**
304
     * Adds a default discriminator map if no one is given
305
     *
306
     * If an entity is of any inheritance type and does not contain a
307
     * discriminator map, then the map is generated automatically. This process
308
     * is expensive computation wise.
309
     *
310
     * The automatically generated discriminator map contains the lowercase short name of
311
     * each class as key.
312
     *
313
     * @throws MappingException
314
     */
315 1
    private function addDefaultDiscriminatorMap(ClassMetadata $class) : void
316
    {
317 1
        $allClasses = $this->driver->getAllClassNames();
318 1
        $fqcn       = $class->getClassName();
319 1
        $map        = [$this->getShortName($fqcn) => $fqcn];
320 1
        $duplicates = [];
321
322 1
        foreach ($allClasses as $subClassCandidate) {
323 1
            if (is_subclass_of($subClassCandidate, $fqcn)) {
324 1
                $shortName = $this->getShortName($subClassCandidate);
325
326 1
                if (isset($map[$shortName])) {
327
                    $duplicates[] = $shortName;
328
                }
329
330 1
                $map[$shortName] = $subClassCandidate;
331
            }
332
        }
333
334 1
        if ($duplicates) {
335
            throw MappingException::duplicateDiscriminatorEntry($class->getClassName(), $duplicates, $map);
336
        }
337
338 1
        $class->setDiscriminatorMap($map);
339 1
    }
340
341
    /**
342
     * Gets the lower-case short name of a class.
343
     *
344
     * @param string $className
345
     */
346 1
    private function getShortName($className) : string
347
    {
348 1
        if (strpos($className, '\\') === false) {
349
            return strtolower($className);
350
        }
351
352 1
        $parts = explode('\\', $className);
353
354 1
        return strtolower(end($parts));
355
    }
356
357
    /**
358
     * Adds inherited named native queries to the subclass mapping.
359
     *
360
     * @throws MappingException
361
     */
362 7
    private function addInheritedNamedNativeQueries(ClassMetadata $subClass, ClassMetadata $parentClass) : void
363
    {
364 7
        foreach ($parentClass->namedNativeQueries as $name => $query) {
365 7
            if (isset($subClass->namedNativeQueries[$name])) {
366 4
                continue;
367
            }
368
369 7
            $subClass->addNamedNativeQuery(
370 7
                $name,
371 7
                $query['query'],
372
                [
373 7
                    'resultSetMapping' => $query['resultSetMapping'],
374 7
                    'resultClass'      => $query['resultClass'],
375
                ]
376
            );
377
        }
378 7
    }
379
380
    /**
381
     * Adds inherited sql result set mappings to the subclass mapping.
382
     *
383
     * @throws MappingException
384
     */
385 7
    private function addInheritedSqlResultSetMappings(ClassMetadata $subClass, ClassMetadata $parentClass) : void
386
    {
387 7
        foreach ($parentClass->sqlResultSetMappings as $name => $mapping) {
388 7
            if (isset($subClass->sqlResultSetMappings[$name])) {
389 4
                continue;
390
            }
391
392 7
            $entities = [];
393
394 7
            foreach ($mapping['entities'] as $entity) {
395 7
                $entities[] = [
396 7
                    'fields'              => $entity['fields'],
397 7
                    'discriminatorColumn' => $entity['discriminatorColumn'],
398 7
                    'entityClass'         => $entity['entityClass'],
399
                ];
400
            }
401
402 7
            $subClass->addSqlResultSetMapping(
403
                [
404 7
                    'name'     => $mapping['name'],
405 7
                    'columns'  => $mapping['columns'],
406 7
                    'entities' => $entities,
407
                ]
408
            );
409
        }
410 7
    }
411
412
    /**
413
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
414
     * most appropriate for the targeted database platform.
415
     *
416
     * @throws ORMException
417
     */
418 362
    private function completeIdentifierGeneratorMappings(ClassMetadata $class) : void
419
    {
420 362
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
421 360
            if (! $property instanceof FieldMetadata /*&& ! $property instanceof AssocationMetadata*/) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

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

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