Failed Conditions
Pull Request — master (#7143)
by Mike
09:42
created

completeIdentifierGeneratorMappings()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
ccs 4
cts 4
cp 1
crap 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM\Mapping;
6
7
use Doctrine\Common\EventManager;
8
use Doctrine\DBAL\Connection;
9
use Doctrine\DBAL\Platforms;
10
use Doctrine\DBAL\Platforms\AbstractPlatform;
11
use Doctrine\ORM\Configuration;
12
use Doctrine\ORM\EntityManagerInterface;
0 ignored issues
show
introduced by
Type Doctrine\ORM\EntityManagerInterface is not used in this file.
Loading history...
13
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
14
use Doctrine\ORM\Event\OnClassMetadataNotFoundEventArgs;
15
use Doctrine\ORM\Events;
16
use Doctrine\ORM\ORMException;
17
use Doctrine\ORM\Sequencing;
18
use Doctrine\ORM\Sequencing\Planning\AssociationValueGeneratorExecutor;
19
use Doctrine\ORM\Sequencing\Planning\ColumnValueGeneratorExecutor;
20
use Doctrine\ORM\Sequencing\Planning\CompositeValueGenerationPlan;
21
use Doctrine\ORM\Sequencing\Planning\NoopValueGenerationPlan;
22
use Doctrine\ORM\Sequencing\Planning\SingleValueGenerationPlan;
23
use Doctrine\ORM\Sequencing\Planning\ValueGenerationExecutor;
24
use ReflectionException;
25
use function array_map;
26
use function class_exists;
27
use function count;
28
use function end;
29
use function explode;
30
use function is_subclass_of;
31
use function sprintf;
32
use function strpos;
33
use function strtolower;
34
use function var_export;
35
36
/**
37
 * The ClassMetadataFactory is used to create ClassMetadata objects that contain all the
38
 * metadata mapping information of a class which describes how a class should be mapped
39
 * to a relational database.
40
 */
41
class ClassMetadataFactory extends AbstractClassMetadataFactory
42
{
0 ignored issues
show
introduced by
There must be exactly 0 empty lines after class opening brace.
Loading history...
43
44
    /** @var AbstractPlatform */
45
    private $targetPlatform;
46
47
    /**
0 ignored issues
show
introduced by
Found multi-line comment for property \Doctrine\ORM\Mapping\ClassMetadataFactory::$configuration with single line content, use one-line comment instead.
Loading history...
48
     * @var Configuration
49
     */
50
    private $configuration;
51
    /**
0 ignored issues
show
introduced by
Found multi-line comment for property \Doctrine\ORM\Mapping\ClassMetadataFactory::$connection with single line content, use one-line comment instead.
Loading history...
52
     * @var Connection
53
     */
54
    private $connection;
55
    /**
0 ignored issues
show
introduced by
Found multi-line comment for property \Doctrine\ORM\Mapping\ClassMetadataFactory::$eventManager with single line content, use one-line comment instead.
Loading history...
56 379
     * @var EventManager
57
     */
58 379
    private $eventManager;
59
60 360
    public function __construct(Configuration $configuration, Connection $connection, EventManager $eventManager)
61
    {
62 360
        $this->configuration = $configuration;
63
        $this->connection = $connection;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
64
        $this->eventManager = $eventManager;
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
65 2249
        $this->driver      = $this->configuration->getMetadataDriverImpl();
0 ignored issues
show
Bug Best Practice introduced by
The property driver does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 8 spaces but found 6 spaces

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
66
        $this->initialize = true;
0 ignored issues
show
Bug Best Practice introduced by
The property initialize does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 4 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
67 2249
    }
68 2249
69
    /**
70
     * {@inheritdoc}
71
     */
72
    protected function loadMetadata(string $name, ClassMetadataBuildingContext $metadataBuildingContext) : array
73
    {
74
        $loaded = parent::loadMetadata($name, $metadataBuildingContext);
75 445
76
        array_map([$this, 'resolveDiscriminatorValue'], $loaded);
77 445
78 445
        return $loaded;
79 445
    }
80 445
81
    protected function initialize() : void
82
    {
83
        $this->initialized = true;
84
    }
85 12
86
    /**
87
     * {@inheritdoc}
88
     */
89 12
    protected function onNotFoundMetadata(
90 10
        string $className,
91
        ClassMetadataBuildingContext $metadataBuildingContext
92
    ) : ?ClassMetadata {
93 2
        if (! $this->eventManager->hasListeners(Events::onClassMetadataNotFound)) {
94
            return null;
95 2
        }
96
97 2
        $eventArgs = new OnClassMetadataNotFoundEventArgs($className, $metadataBuildingContext, $this->em);
0 ignored issues
show
Bug Best Practice introduced by
The property em does not exist on Doctrine\ORM\Mapping\ClassMetadataFactory. Did you maybe forget to declare it?
Loading history...
98
99
        $this->eventManager->dispatchEvent(Events::onClassMetadataNotFound, $eventArgs);
100
101
        return $eventArgs->getFoundMetadata();
102
    }
103
104
    /**
105
     * {@inheritdoc}
106 367
     *
107
     * @throws MappingException
108
     * @throws ORMException
109
     */
110
    protected function doLoadMetadata(
111 367
        string $className,
112
        ?ClassMetadata $parent,
113 367
        ClassMetadataBuildingContext $metadataBuildingContext
114 98
    ) : ClassMetadata {
115
        $classMetadata = new ClassMetadata($className, $metadataBuildingContext);
116 98
117 97
        if ($parent) {
118
            $classMetadata->setParent($parent);
119
120 98
            foreach ($parent->getDeclaredPropertiesIterator() as $fieldName => $property) {
121 98
                $classMetadata->addInheritedProperty($property);
122
            }
123 98
124 72
            $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

124
            $classMetadata->setInheritanceType(/** @scrutinizer ignore-type */ $parent->inheritanceType);
Loading history...
125 72
            $classMetadata->setIdentifier($parent->identifier);
126
127
            if ($parent->discriminatorColumn) {
128 98
                $classMetadata->setDiscriminatorColumn($parent->discriminatorColumn);
129 98
                $classMetadata->setDiscriminatorMap($parent->discriminatorMap);
130
            }
131 98
132 28
            $classMetadata->setLifecycleCallbacks($parent->lifecycleCallbacks);
133
            $classMetadata->setChangeTrackingPolicy($parent->changeTrackingPolicy);
134
135
            if ($parent->isMappedSuperclass) {
136
                $classMetadata->setCustomRepositoryClassName($parent->getCustomRepositoryClassName());
137
            }
138 367
        }
139 3
140
        // Invoke driver
141
        try {
142
            $this->driver->loadMetadataForClass($classMetadata->getClassName(), $classMetadata, $metadataBuildingContext);
0 ignored issues
show
Bug introduced by
The method loadMetadataForClass() 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

142
            $this->driver->/** @scrutinizer ignore-call */ 
143
                           loadMetadataForClass($classMetadata->getClassName(), $classMetadata, $metadataBuildingContext);

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...
143 364
        } catch (ReflectionException $e) {
144
            throw MappingException::reflectionFailure($classMetadata->getClassName(), $e);
145 364
        }
146 98
147 3
        $this->completeIdentifierGeneratorMappings($classMetadata);
148
149
        if ($parent) {
150 98
            if ($parent->getCache()) {
151 7
                $classMetadata->setCache(clone $parent->getCache());
152
            }
153
154
            if (! empty($parent->entityListeners) && empty($classMetadata->entityListeners)) {
155 364
                $classMetadata->entityListeners = $parent->entityListeners;
156 1
            }
157
        }
158
159 364
        if (! $classMetadata->discriminatorMap && $classMetadata->inheritanceType !== InheritanceType::NONE && $classMetadata->isRootEntity()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $classMetadata->discriminatorMap of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
160
            $this->addDefaultDiscriminatorMap($classMetadata);
161 364
        }
162 6
163
        $this->completeRuntimeMetadata($classMetadata, $parent);
164 6
165
        if ($this->eventManager->hasListeners(Events::loadClassMetadata)) {
166
            $eventArgs = new LoadClassMetadataEventArgs($classMetadata, $this->em);
0 ignored issues
show
Bug Best Practice introduced by
The property em does not exist on Doctrine\ORM\Mapping\ClassMetadataFactory. Did you maybe forget to declare it?
Loading history...
167 363
168 363
            $this->eventManager->dispatchEvent(Events::loadClassMetadata, $eventArgs);
169
        }
170 361
171
        $this->buildValueGenerationPlan($classMetadata);
172
        $this->validateRuntimeMetadata($classMetadata, $parent);
173 364
174
        return $classMetadata;
175 364
    }
176 364
177
    protected function completeRuntimeMetadata(ClassMetadata $class, ?ClassMetadata $parent = null) : void
178
    {
179 28
        if (! $parent || ! $parent->isMappedSuperclass) {
180 1
            return;
181
        }
182
183 28
        if ($class->isMappedSuperclass) {
184
            return;
185
        }
186 28
187 28
        $tableName = $class->getTableName();
188 28
189
        // Resolve column table names
190 28
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
191
            if ($property instanceof FieldMetadata) {
192
                $property->setTableName($property->getTableName() ?? $tableName);
193 12
194 12
                continue;
195
            }
196
197
            if (! ($property instanceof ToOneAssociationMetadata)) {
198 9
                continue;
199
            }
200 9
201
            // Resolve association join column table names
202
            foreach ($property->getJoinColumns() as $joinColumn) {
203 28
                /** @var JoinColumnMetadata $joinColumn */
204
                $joinColumn->setTableName($joinColumn->getTableName() ?? $tableName);
205
            }
206
        }
207
    }
208
209
    /**
210 363
     * Validate runtime metadata is correctly defined.
211
     *
212 363
     * @throws MappingException
213
     */
214
    protected function validateRuntimeMetadata(ClassMetadata $class, ?ClassMetadata $parent = null) : void
215
    {
216
        if (! $class->getReflectionClass()) {
217 363
            // only validate if there is a reflection class instance
218 361
            return;
219 361
        }
220
221
        $class->validateIdentifier();
222 361
        $class->validateAssociations();
223 75
        $class->validateLifecycleCallbacks($this->getReflectionService());
224 74
225
        // verify inheritance
226
        if (! $class->isMappedSuperclass && $class->inheritanceType !== InheritanceType::NONE) {
227
            if (! $parent) {
228 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...
229 75
                    throw MappingException::missingDiscriminatorMap($class->getClassName());
230
                }
231
232 325
                if (! $class->discriminatorColumn) {
233
                    throw MappingException::missingDiscriminatorColumn($class->getClassName());
234
                }
235
            }
236 361
        } elseif (($class->discriminatorMap || $class->discriminatorColumn) && $class->isMappedSuperclass && $class->isRootEntity()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class->discriminatorMap of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
237
            // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy
238
            throw MappingException::noInheritanceOnMappedSuperClass($class->getClassName());
239
        }
240
    }
241 1951
242
    /**
243 1951
     * {@inheritdoc}
244 1951
     */
245 1951
    protected function newClassMetadataBuildingContext() : ClassMetadataBuildingContext
246 1951
    {
247
        return new ClassMetadataBuildingContext(
248
            $this,
249
            $this->getReflectionService(),
250
            $this->configuration->getNamingStrategy()
251
        );
252
    }
253
254
    /**
255
     * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator
256
     * map classes and looking for a fitting one.
257
     *
258 360
     * @throws \InvalidArgumentException
259
     * @throws \ReflectionException
260 360
     * @throws MappingException
261 360
     */
262 360
    private function resolveDiscriminatorValue(ClassMetadata $metadata) : void
263
    {
264
        if ($metadata->discriminatorValue || ! $metadata->discriminatorMap || $metadata->isMappedSuperclass ||
0 ignored issues
show
Bug Best Practice introduced by
The expression $metadata->discriminatorMap of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
265
            ! $metadata->getReflectionClass() || $metadata->getReflectionClass()->isAbstract()) {
266 4
            return;
267 4
        }
268 3
269
        // minor optimization: avoid loading related metadata when not needed
270 4
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
271
            if ($discriminatorClass === $metadata->getClassName()) {
272
                $metadata->discriminatorValue = $discriminatorValue;
273
274
                return;
275 1
            }
276 1
        }
277
278
        // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata
279 1
        foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) {
280
            if ($metadata->getClassName() === $this->getMetadataFor($discriminatorClass)->getClassName()) {
281
                $metadata->discriminatorValue = $discriminatorValue;
282
283 1
                return;
284
            }
285
        }
286
287
        throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->getClassName(), $metadata->getRootClassName());
288
    }
289
290
    /**
291
     * Adds a default discriminator map if no one is given
292
     *
293
     * If an entity is of any inheritance type and does not contain a
294
     * discriminator map, then the map is generated automatically. This process
295
     * is expensive computation wise.
296
     *
297
     * The automatically generated discriminator map contains the lowercase short name of
298 1
     * each class as key.
299
     *
300 1
     * @throws MappingException
301 1
     */
302 1
    private function addDefaultDiscriminatorMap(ClassMetadata $class) : void
303 1
    {
304
        $allClasses = $this->driver->getAllClassNames();
305 1
        $fqcn       = $class->getClassName();
306 1
        $map        = [$this->getShortName($fqcn) => $fqcn];
307 1
        $duplicates = [];
308
309 1
        foreach ($allClasses as $subClassCandidate) {
310
            if (is_subclass_of($subClassCandidate, $fqcn)) {
311
                $shortName = $this->getShortName($subClassCandidate);
312
313 1
                if (isset($map[$shortName])) {
314
                    $duplicates[] = $shortName;
315
                }
316
317 1
                $map[$shortName] = $subClassCandidate;
318
            }
319
        }
320
321 1
        if ($duplicates) {
322 1
            throw MappingException::duplicateDiscriminatorEntry($class->getClassName(), $duplicates, $map);
323
        }
324
325
        $class->setDiscriminatorMap($map);
326
    }
327
328
    /**
329 1
     * Gets the lower-case short name of a class.
330
     *
331 1
     * @param string $className
332
     */
333
    private function getShortName($className) : string
334
    {
335 1
        if (strpos($className, '\\') === false) {
336
            return strtolower($className);
337 1
        }
338
339
        $parts = explode('\\', $className);
340
341
        return strtolower(end($parts));
342
    }
343
344
    /**
345
     * Completes the ID generator mapping. If "auto" is specified we choose the generator
346 364
     * most appropriate for the targeted database platform.
347
     *
348 364
     * @throws ORMException
349 362
     */
350 251
    private function completeIdentifierGeneratorMappings(ClassMetadata $class) : void
351
    {
352
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
353 360
            if (! $property instanceof FieldMetadata /*&& ! $property instanceof AssocationMetadata*/) {
354
                continue;
355 364
            }
356
357 360
            $this->completeFieldIdentifierGeneratorMapping($property);
358
        }
359 360
    }
360 280
361
    private function completeFieldIdentifierGeneratorMapping(FieldMetadata $field)
362
    {
363 288
        if (! $field->hasValueGenerator()) {
364 288
            return;
365 288
        }
366
367 288
        $platform  = $this->getTargetPlatform();
368 278
        $class     = $field->getDeclaringClass();
0 ignored issues
show
Unused Code introduced by
The assignment to $class is dead and can be removed.
Loading history...
369 278
        $generator = $field->getValueGenerator();
370
371 278
        if ($generator->getType() === GeneratorType::AUTO) {
372 278
            $generator = new ValueGeneratorMetadata(
373 278
                $platform->prefersSequences()
374
                    ? GeneratorType::SEQUENCE
375 278
                    : ($platform->prefersIdentityColumns()
376
                        ? GeneratorType::IDENTITY
377 278
                        : GeneratorType::TABLE
378
                ),
379
                $field->getValueGenerator()->getDefinition()
380
            );
381 288
            $field->setValueGenerator($generator);
382 288
        }
383
384 6
        // Validate generator definition and set defaults where needed
385 6
        switch ($generator->getType()) {
386
            case GeneratorType::SEQUENCE:
387
                // If there is no sequence definition yet, create a default definition
388
                if ($generator->getDefinition()) {
389
                    break;
390
                }
391
392
                // @todo guilhermeblanco Move sequence generation to DBAL
393
                $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

393
                $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...
394
                $idSequenceName = sprintf('%s_%s_seq', $sequencePrefix, $field->getColumnName());
395
                $sequenceName   = $platform->fixSchemaElementName($idSequenceName);
396
397
                $field->setValueGenerator(
398
                    new ValueGeneratorMetadata(
399
                        $generator->getType(),
400
                        [
401
                            'sequenceName'   => $sequenceName,
402
                            'allocationSize' => 1,
403
                        ]
404
                    )
405 282
                );
406
407
                break;
408
409 282
            case GeneratorType::TABLE:
410 1
                throw new ORMException('TableGenerator not yet implemented.');
411 1
                break;
412
413
            case GeneratorType::CUSTOM:
414 1
                $definition = $generator->getDefinition();
415
                if (! isset($definition['class'])) {
416
                    throw new ORMException(sprintf('Cannot instantiate custom generator, no class has been defined'));
417
                }
418 1
                if (! class_exists($definition['class'])) {
419
                    throw new ORMException(sprintf('Cannot instantiate custom generator : %s', var_export($definition, true))); //$definition['class']));
420 281
                }
421
422
                break;
423 281
424
            case GeneratorType::IDENTITY:
425
            case GeneratorType::NONE:
426
            case GeneratorType::UUID:
427
                break;
428 288
429
            default:
430
                throw new ORMException('Unknown generator type: ' . $generator->getType());
431
        }
432
    }
433 198
434
    /**
435 198
     * {@inheritDoc}
436
     */
437
    protected function getDriver() : Driver\MappingDriver
438
    {
439
        return $this->driver;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->driver could return the type null which is incompatible with the type-hinted return Doctrine\ORM\Mapping\Driver\MappingDriver. Consider adding an additional type-check to rule them out.
Loading history...
440
    }
441
442
    /**
443
     * {@inheritDoc}
444
     */
445
    protected function isEntity(ClassMetadata $class) : bool
446 288
    {
447
        return isset($class->isMappedSuperclass) && $class->isMappedSuperclass === false;
448 288
    }
449 288
450
    private function getTargetPlatform() : Platforms\AbstractPlatform
451
    {
452 288
        if (! $this->targetPlatform) {
453
            $this->targetPlatform = $this->connection->getDatabasePlatform();
454
        }
455 363
456
        return $this->targetPlatform;
457 363
    }
458
459 363
    private function buildValueGenerationPlan(ClassMetadata $class) : void
460 363
    {
461 91
        $executors = $this->buildValueGenerationExecutorList($class);
462 91
463
        switch (count($executors)) {
464 303
            case 0:
465 298
                $class->setValueGenerationPlan(new NoopValueGenerationPlan());
466 298
                break;
467
468
            case 1:
469 18
                $class->setValueGenerationPlan(new SingleValueGenerationPlan($class, $executors[0]));
470 18
                break;
471
472 363
            default:
473
                $class->setValueGenerationPlan(new CompositeValueGenerationPlan($class, $executors));
474
                break;
475
        }
476
    }
477 363
478
    /**
479 363
     * @return ValueGenerationExecutor[]
480
     */
481 363
    private function buildValueGenerationExecutorList(ClassMetadata $class) : array
482 361
    {
483
        $executors = [];
484 361
485 361
        foreach ($class->getDeclaredPropertiesIterator() as $property) {
486
            $executor = $this->buildValueGenerationExecutorForProperty($class, $property);
487
488
            if ($executor instanceof ValueGenerationExecutor) {
489 363
                $executors[] = $executor;
490
            }
491
        }
492 361
493
        return $executors;
494
    }
495
496 361
    private function buildValueGenerationExecutorForProperty(
497 287
        ClassMetadata $class,
498
        Property $property
499
    ) : ?ValueGenerationExecutor {
500 331
        if ($property instanceof LocalColumnMetadata && $property->hasValueGenerator()) {
501 41
            return new ColumnValueGeneratorExecutor($property, $this->createPropertyValueGenerator($class, $property));
502
        }
503
504 327
        if ($property instanceof ToOneAssociationMetadata && $property->isPrimaryKey()) {
505
            return new AssociationValueGeneratorExecutor();
506
        }
507 287
508
        return null;
509
    }
510
511 287
    private function createPropertyValueGenerator(
512
        ClassMetadata $class,
513 287
        LocalColumnMetadata $property
514 287
    ) : Sequencing\Generator {
515 280
        $platform = $this->getTargetPlatform();
516
517
        switch ($property->getValueGenerator()->getType()) {
518 280
            case GeneratorType::IDENTITY:
519
                $sequenceName = null;
520
521
                // Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
522
                if ($platform->usesSequenceEmulatedIdentityColumns()) {
523
                    $sequencePrefix = $platform->getSequencePrefix($class->getTableName(), $class->getSchemaName());
524 280
                    $idSequenceName = $platform->getIdentitySequenceName($sequencePrefix, $property->getColumnName());
525 1
                    $sequenceName   = $platform->quoteIdentifier($platform->fixSchemaElementName($idSequenceName));
526 280
                }
527
528 7
                return $property->getTypeName() === 'bigint'
529 6
                    ? new Sequencing\BigIntegerIdentityGenerator($sequenceName)
530 6
                    : new Sequencing\IdentityGenerator($sequenceName);
531 6
532 6
            case GeneratorType::SEQUENCE:
533
                $definition = $property->getValueGenerator()->getDefinition();
534
                return new Sequencing\SequenceGenerator(
535
                    $platform->quoteIdentifier($definition['sequenceName']),
536 1
                    $definition['allocationSize']
537
                );
538
                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...
539
540 1
            case GeneratorType::UUID:
541 1
                return new Sequencing\UuidGenerator();
542 1
                break;
543
544
            case GeneratorType::CUSTOM:
545
                $class = $property->getValueGenerator()->getDefinition()['class'];
546
                return new $class();
547
                break;
548
        }
549
    }
550
}
551