Failed Conditions
Pull Request — master (#6959)
by Matthew
10:00
created

ClassMetadataFactory::doLoadMetadata()   F

Complexity

Conditions 15
Paths 345

Size

Total Lines 76
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 37
CRAP Score 15.004

Importance

Changes 0
Metric Value
cc 15
eloc 38
nc 345
nop 3
dl 0
loc 76
ccs 37
cts 38
cp 0.9737
crap 15.004
rs 3.9632
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 386
    protected function loadMetadata(string $name, ClassMetadataBuildingContext $metadataBuildingContext) : array
57
    {
58 386
        $loaded = parent::loadMetadata($name, $metadataBuildingContext);
59
60 359
        array_map([$this, 'resolveDiscriminatorValue'], $loaded);
61
62 359
        return $loaded;
63
    }
64
65
    /**
66
     * @param EntityManagerInterface $em
67
     */
68 2274
    public function setEntityManager(EntityManagerInterface $em)
69
    {
70 2274
        $this->em = $em;
71 2274
    }
72
73
    /**
74
     * {@inheritdoc}
75
     *
76
     * @throws ORMException
77
     */
78 453
    protected function initialize() : void
79
    {
80 453
        $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 453
        $this->evm = $this->em->getEventManager();
82 453
        $this->initialized = true;
83 453
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 19
    protected function onNotFoundMetadata(
89
        string $className,
90
        ClassMetadataBuildingContext $metadataBuildingContext
91
    ) : ?ClassMetadata
92
    {
93 19
        if (! $this->evm->hasListeners(Events::onClassMetadataNotFound)) {
94 17
            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 367
    protected function doLoadMetadata(
111
        string $className,
112
        ?ClassMetadata $parent,
113
        ClassMetadataBuildingContext $metadataBuildingContext
114
    ) : ClassMetadata
115
    {
116 367
        $classMetadata = new ClassMetadata($className, $metadataBuildingContext);
117
118 367
        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 367
            $this->driver->loadMetadataForClass($classMetadata->getClassName(), $classMetadata, $metadataBuildingContext);
142 2
        } catch (ReflectionException $e) {
143
            throw MappingException::reflectionFailure($classMetadata->getClassName(), $e);
144
        }
145
146 365
        $this->completeIdentifierGeneratorMappings($classMetadata);
147
148 365
        if ($parent) {
149 99
            $this->addInheritedNamedQueries($classMetadata, $parent);
150
151 99
            $parent_cache = $parent->getCache();
152
            
153 99
            if ($parent_cache) {
154 3
                $classMetadata->setCache(clone $parent_cache);
155
            }
156
157 99
            if ( ! empty($parent->namedNativeQueries)) {
158 7
                $this->addInheritedNamedNativeQueries($classMetadata, $parent);
159
            }
160
161 99
            if ( ! empty($parent->sqlResultSetMappings)) {
162 7
                $this->addInheritedSqlResultSetMappings($classMetadata, $parent);
163
            }
164
165 99
            if ( ! empty($parent->entityListeners) && empty($classMetadata->entityListeners)) {
166 7
                $classMetadata->entityListeners = $parent->entityListeners;
167
            }
168
        }
169
170 365
        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...
171 1
            $this->addDefaultDiscriminatorMap($classMetadata);
172
        }
173
174 365
        $this->completeRuntimeMetadata($classMetadata, $parent);
175
176 365
        if ($this->evm->hasListeners(Events::loadClassMetadata)) {
177 6
            $eventArgs = new LoadClassMetadataEventArgs($classMetadata, $this->em);
0 ignored issues
show
Bug introduced by
It seems like $this->em can also be of type null; however, parameter $entityManager of Doctrine\ORM\Event\LoadC...ventArgs::__construct() does only seem to accept Doctrine\ORM\EntityManagerInterface, maybe add an additional type check? ( Ignorable by Annotation )

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

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

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