Failed Conditions
Pull Request — master (#7688)
by Gabriel
09:39
created

validateAndCompleteAssociationMapping()   C

Complexity

Conditions 12
Paths 17

Size

Total Lines 54
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 14.0256

Importance

Changes 0
Metric Value
cc 12
eloc 28
c 0
b 0
f 0
nc 17
nop 1
dl 0
loc 54
ccs 22
cts 29
cp 0.7586
crap 14.0256
rs 6.9666

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 ArrayIterator;
8
use Doctrine\ORM\Cache\Exception\CacheException;
9
use Doctrine\ORM\Cache\Exception\NonCacheableEntityAssociation;
10
use Doctrine\ORM\EntityManagerInterface;
11
use Doctrine\ORM\Mapping\Factory\NamingStrategy;
12
use Doctrine\ORM\Reflection\ReflectionService;
13
use Doctrine\ORM\Sequencing\Planning\ValueGenerationPlan;
14
use Doctrine\ORM\Utility\PersisterHelper;
15
use ReflectionException;
16
use RuntimeException;
17
use function array_diff;
18
use function array_filter;
19
use function array_intersect;
20
use function array_map;
21
use function array_merge;
22
use function class_exists;
23
use function count;
24
use function get_class;
25
use function in_array;
26
use function interface_exists;
27
use function is_subclass_of;
28
use function method_exists;
29
use function spl_object_id;
30
use function sprintf;
31
32
/**
33
 * A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
34
 * of an entity and its associations.
35
 */
36
class ClassMetadata extends ComponentMetadata implements TableOwner
37
{
38
    /**
39
     * The name of the custom repository class used for the entity class.
40
     * (Optional).
41
     *
42
     * @var string
43
     */
44
    protected $customRepositoryClassName;
45
46
    /**
47
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
48
     *
49
     * @var bool
50
     */
51
    public $isMappedSuperclass = false;
52
53
    /**
54
     * READ-ONLY: Whether this class describes the mapping of an embeddable class.
55
     *
56
     * @var bool
57
     */
58
    public $isEmbeddedClass = false;
59
60
    /**
61
     * Whether this class describes the mapping of a read-only class.
62
     * That means it is never considered for change-tracking in the UnitOfWork.
63
     * It is a very helpful performance optimization for entities that are immutable,
64
     * either in your domain or through the relation database (coming from a view,
65
     * or a history table for example).
66
     *
67
     * @var bool
68
     */
69
    private $readOnly = false;
70
71
    /**
72
     * The names of all subclasses (descendants).
73
     *
74
     * @var string[]
75
     */
76
    protected $subClasses = [];
77
78
    /**
79
     * READ-ONLY: The names of all embedded classes based on properties.
80
     *
81
     * @var string[]
82
     */
83
    //public $embeddedClasses = [];
84
85
    /**
86
     * READ-ONLY: The registered lifecycle callbacks for entities of this class.
87
     *
88
     * @var string[][]
89
     */
90
    public $lifecycleCallbacks = [];
91
92
    /**
93
     * READ-ONLY: The registered entity listeners.
94
     *
95
     * @var mixed[][]
96
     */
97
    public $entityListeners = [];
98
99
    /**
100
     * READ-ONLY: The field names of all fields that are part of the identifier/primary key
101
     * of the mapped entity class.
102
     *
103
     * @var string[]
104
     */
105
    public $identifier = [];
106
107
    /**
108
     * READ-ONLY: The inheritance mapping type used by the class.
109
     *
110
     * @var string
111
     */
112
    public $inheritanceType = InheritanceType::NONE;
113
114
    /**
115
     * READ-ONLY: The policy used for change-tracking on entities of this class.
116
     *
117
     * @var string
118
     */
119
    public $changeTrackingPolicy = ChangeTrackingPolicy::DEFERRED_IMPLICIT;
120
121
    /**
122
     * READ-ONLY: The discriminator value of this class.
123
     *
124
     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
125
     * where a discriminator column is used.</b>
126
     *
127
     * @see discriminatorColumn
128
     *
129
     * @var mixed
130
     */
131
    public $discriminatorValue;
132
133
    /**
134
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
135
     *
136
     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
137
     * where a discriminator column is used.</b>
138
     *
139
     * @see discriminatorColumn
140
     *
141
     * @var string[]
142
     */
143
    public $discriminatorMap = [];
144
145
    /**
146
     * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
147
     * inheritance mappings.
148
     *
149
     * @var DiscriminatorColumnMetadata
150
     */
151
    public $discriminatorColumn;
152
153
    /**
154
     * READ-ONLY: The primary table metadata.
155
     *
156
     * @var TableMetadata
157
     */
158
    public $table;
159
160
    /**
161
     * READ-ONLY: An array of field names. Used to look up field names from column names.
162
     * Keys are column names and values are field names.
163
     *
164
     * @var string[]
165
     */
166
    public $fieldNames = [];
167
168
    /**
169
     * READ-ONLY: The field which is used for versioning in optimistic locking (if any).
170
     *
171
     * @var FieldMetadata|null
172
     */
173
    public $versionProperty;
174
175
    /**
176
     * NamingStrategy determining the default column and table names.
177
     *
178
     * @var NamingStrategy
179
     */
180
    protected $namingStrategy;
181
182
    /**
183
     * Value generation plan is responsible for generating values for auto-generated fields.
184
     *
185
     * @var ValueGenerationPlan
186
     */
187
    protected $valueGenerationPlan;
188
189
    /**
190
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
191
     * metadata of the class with the given name.
192
     *
193
     * @param string             $entityName The name of the entity class.
194
     * @param ClassMetadata|null $parent     Optional parent class metadata.
195
     */
196 450
    public function __construct(
197
        string $entityName,
198
        ?ComponentMetadata $parent,
199
        ClassMetadataBuildingContext $metadataBuildingContext
200
    ) {
201 450
        parent::__construct($entityName, $metadataBuildingContext);
202
203 450
        $this->namingStrategy = $metadataBuildingContext->getNamingStrategy();
204
205 450
        if ($parent) {
206 98
            $this->setParent($parent);
207
        }
208 450
    }
209
210
    /**
211
     * {@inheritdoc}
212
     *
213
     * @throws MappingException
214
     */
215 98
    public function setParent(ComponentMetadata $parent) : void
216
    {
217 98
        parent::setParent($parent);
218
219 98
        foreach ($parent->getPropertiesIterator() as $fieldName => $property) {
220 95
            $this->addInheritedProperty($property);
221
        }
222
223
        // @todo guilhermeblanco Assume to be a ClassMetadata temporarily until ClassMetadata split is complete.
224
        /** @var ClassMetadata $parent */
225 98
        $this->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

225
        $this->setInheritanceType(/** @scrutinizer ignore-type */ $parent->inheritanceType);
Loading history...
226 98
        $this->setIdentifier($parent->identifier);
227 98
        $this->setChangeTrackingPolicy($parent->changeTrackingPolicy);
228
229 98
        if ($parent->discriminatorColumn) {
230 70
            $this->setDiscriminatorColumn($parent->discriminatorColumn);
231 70
            $this->setDiscriminatorMap($parent->discriminatorMap);
232
        }
233
234 98
        if ($parent->isMappedSuperclass) {
235 27
            $this->setCustomRepositoryClassName($parent->getCustomRepositoryClassName());
236
        }
237
238 98
        if ($parent->cache) {
239 3
            $this->setCache(clone $parent->cache);
240
        }
241
242 98
        if (! empty($parent->lifecycleCallbacks)) {
243 5
            $this->lifecycleCallbacks = $parent->lifecycleCallbacks;
244
        }
245
246 98
        if (! empty($parent->entityListeners)) {
247 7
            $this->entityListeners = $parent->entityListeners;
248
        }
249 98
    }
250
251
    public function setClassName(string $className)
252
    {
253
        $this->className = $className;
254
    }
255
256
    public function getColumnsIterator() : ArrayIterator
257
    {
258
        $iterator = parent::getColumnsIterator();
259
260
        if ($this->discriminatorColumn) {
261
            $iterator->offsetSet($this->discriminatorColumn->getColumnName(), $this->discriminatorColumn);
0 ignored issues
show
Bug introduced by
$this->discriminatorColumn of type Doctrine\ORM\Mapping\DiscriminatorColumnMetadata is incompatible with the type string expected by parameter $newval of ArrayIterator::offsetSet(). ( Ignorable by Annotation )

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

261
            $iterator->offsetSet($this->discriminatorColumn->getColumnName(), /** @scrutinizer ignore-type */ $this->discriminatorColumn);
Loading history...
262
        }
263
264
        return $iterator;
265
    }
266
267 11
    public function getAncestorsIterator() : ArrayIterator
268
    {
269 11
        $ancestors = new ArrayIterator();
270 11
        $parent    = $this;
271
272 11
        while (($parent = $parent->parent) !== null) {
273 8
            if ($parent instanceof ClassMetadata && $parent->isMappedSuperclass) {
274 1
                continue;
275
            }
276
277 7
            $ancestors->append($parent);
278
        }
279
280 11
        return $ancestors;
281
    }
282
283 1259
    public function getRootClassName() : string
284
    {
285 1259
        return $this->parent instanceof ClassMetadata && ! $this->parent->isMappedSuperclass
286 402
            ? $this->parent->getRootClassName()
287 1259
            : $this->className;
288
    }
289
290
    /**
291
     * Handles metadata cloning nicely.
292
     */
293 13
    public function __clone()
294
    {
295 13
        if ($this->cache) {
296 12
            $this->cache = clone $this->cache;
297
        }
298
299 13
        foreach ($this->properties as $name => $property) {
300 13
            $this->properties[$name] = clone $property;
301
        }
302 13
    }
303
304
    /**
305
     * Creates a string representation of this instance.
306
     *
307
     * @return string The string representation of this instance.
308
     *
309
     * @todo Construct meaningful string representation.
310
     */
311
    public function __toString()
312
    {
313
        return self::class . '@' . spl_object_id($this);
314
    }
315
316
    /**
317
     * Determines which fields get serialized.
318
     *
319
     * It is only serialized what is necessary for best unserialization performance.
320
     * That means any metadata properties that are not set or empty or simply have
321
     * their default value are NOT serialized.
322
     *
323
     * Parts that are also NOT serialized because they can not be properly unserialized:
324
     * - reflectionClass
325
     *
326
     * @return string[] The names of all the fields that should be serialized.
327
     */
328 4
    public function __sleep()
329
    {
330 4
        $serialized = [];
331
332
        // This metadata is always serialized/cached.
333 4
        $serialized = array_merge($serialized, [
334 4
            'properties',
335
            'fieldNames',
336
            //'embeddedClasses',
337
            'identifier',
338
            'className',
339
            'parent',
340
            'table',
341
            'valueGenerationPlan',
342
        ]);
343
344
        // The rest of the metadata is only serialized if necessary.
345 4
        if ($this->changeTrackingPolicy !== ChangeTrackingPolicy::DEFERRED_IMPLICIT) {
346
            $serialized[] = 'changeTrackingPolicy';
347
        }
348
349 4
        if ($this->customRepositoryClassName) {
350 1
            $serialized[] = 'customRepositoryClassName';
351
        }
352
353 4
        if ($this->inheritanceType !== InheritanceType::NONE) {
354 1
            $serialized[] = 'inheritanceType';
355 1
            $serialized[] = 'discriminatorColumn';
356 1
            $serialized[] = 'discriminatorValue';
357 1
            $serialized[] = 'discriminatorMap';
358 1
            $serialized[] = 'subClasses';
359
        }
360
361 4
        if ($this->isMappedSuperclass) {
362
            $serialized[] = 'isMappedSuperclass';
363
        }
364
365 4
        if ($this->isEmbeddedClass) {
366
            $serialized[] = 'isEmbeddedClass';
367
        }
368
369 4
        if ($this->isVersioned()) {
370
            $serialized[] = 'versionProperty';
371
        }
372
373 4
        if ($this->lifecycleCallbacks) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->lifecycleCallbacks of type array<mixed,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...
374
            $serialized[] = 'lifecycleCallbacks';
375
        }
376
377 4
        if ($this->entityListeners) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityListeners of type array<mixed,array<mixed,mixed>> 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...
378 1
            $serialized[] = 'entityListeners';
379
        }
380
381 4
        if ($this->cache) {
382
            $serialized[] = 'cache';
383
        }
384
385 4
        if ($this->readOnly) {
386 1
            $serialized[] = 'readOnly';
387
        }
388
389 4
        return $serialized;
390
    }
391
392
    /**
393
     * Restores some state that can not be serialized/unserialized.
394
     */
395 1634
    public function wakeupReflection(ReflectionService $reflectionService) : void
396
    {
397
        // Restore ReflectionClass and properties
398 1634
        $this->reflectionClass = $reflectionService->getClass($this->className);
399
400 1634
        if (! $this->reflectionClass) {
401
            return;
402
        }
403
404 1634
        $this->className = $this->reflectionClass->getName();
405
406 1634
        foreach ($this->properties as $property) {
407
            /** @var Property $property */
408 1633
            $property->wakeupReflection($reflectionService);
409
        }
410 1634
    }
411
412
    /**
413
     * Sets the change tracking policy used by this class.
414
     */
415 104
    public function setChangeTrackingPolicy(string $policy) : void
416
    {
417 104
        $this->changeTrackingPolicy = $policy;
418 104
    }
419
420
    /**
421
     * Checks whether a field is part of the identifier/primary key field(s).
422
     *
423
     * @param string $fieldName The field name.
424
     *
425
     * @return bool TRUE if the field is part of the table identifier/primary key field(s), FALSE otherwise.
426
     */
427 1027
    public function isIdentifier(string $fieldName) : bool
428
    {
429 1027
        if (! $this->identifier) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->identifier 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...
430 1
            return false;
431
        }
432
433 1026
        if (! $this->isIdentifierComposite()) {
434 1022
            return $fieldName === $this->identifier[0];
435
        }
436
437 93
        return in_array($fieldName, $this->identifier, true);
438
    }
439
440 1213
    public function isIdentifierComposite() : bool
441
    {
442 1213
        return isset($this->identifier[1]);
443
    }
444
445
    /**
446
     * Validates Identifier.
447
     *
448
     * @throws MappingException
449
     */
450 367
    public function validateIdentifier() : void
451
    {
452 367
        if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
453 27
            return;
454
        }
455
456
        // Verify & complete identifier mapping
457 367
        if (! $this->identifier) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->identifier 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...
458 2
            throw MappingException::identifierRequired($this->className);
459
        }
460
461
        $explicitlyGeneratedProperties = array_filter($this->properties, static function (Property $property) : bool {
462 365
            return $property instanceof FieldMetadata
463 365
                && $property->isPrimaryKey()
464 365
                && $property->hasValueGenerator();
465 365
        });
466
467 365
        if ($explicitlyGeneratedProperties && $this->isIdentifierComposite()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $explicitlyGeneratedProperties of type array 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...
468
            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->className);
469
        }
470 365
    }
471
472
    /**
473
     * Validates association targets actually exist.
474
     *
475
     * @throws MappingException
476
     */
477 368
    public function validateAssociations() : void
478
    {
479 368
        array_map(
480
            function (Property $property) {
481 368
                if (! ($property instanceof AssociationMetadata)) {
482 365
                    return;
483
                }
484
485 249
                $targetEntity = $property->getTargetEntity();
486
487 249
                if (! class_exists($targetEntity)) {
488 1
                    throw MappingException::invalidTargetEntityClass($targetEntity, $this->className, $property->getName());
489
                }
490 368
            },
491 368
            $this->properties
492
        );
493 367
    }
494
495
    /**
496
     * Validates lifecycle callbacks.
497
     *
498
     * @throws MappingException
499
     */
500 368
    public function validateLifecycleCallbacks(ReflectionService $reflectionService) : void
501
    {
502 368
        foreach ($this->lifecycleCallbacks as $callbacks) {
503
            /** @var array $callbacks */
504 10
            foreach ($callbacks as $callbackFuncName) {
505 10
                if (! $reflectionService->hasPublicMethod($this->className, $callbackFuncName)) {
506 1
                    throw MappingException::lifecycleCallbackMethodNotFound($this->className, $callbackFuncName);
507
                }
508
            }
509
        }
510 367
    }
511
512
    /**
513
     * Validates & completes the basic mapping information that is common to all
514
     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
515
     *
516
     * @throws MappingException If something is wrong with the mapping.
517
     * @throws CacheException   If entity is not cacheable.
518
     */
519 276
    protected function validateAndCompleteAssociationMapping(AssociationMetadata $property)
520
    {
521 276
        $fieldName    = $property->getName();
522 276
        $targetEntity = $property->getTargetEntity();
523
524 276
        if (! $targetEntity) {
525
            throw MappingException::missingTargetEntity($fieldName);
526
        }
527
528 276
        $property->setSourceEntity($this->className);
529 276
        $property->setTargetEntity($targetEntity);
530
531
        // Complete id mapping
532 276
        if ($property->isPrimaryKey()) {
533 46
            if ($property->isOrphanRemoval()) {
534 1
                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->className, $fieldName);
535
            }
536
537 45
            if ($property instanceof ToOneAssociationMetadata && count($property->getJoinColumns()) >= 2) {
538
                throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
539
                    $property->getTargetEntity(),
540
                    $this->className,
541
                    $fieldName
542
                );
543
            }
544
545 45
            if ($this->cache && ! $property->getCache()) {
546 1
                throw NonCacheableEntityAssociation::fromEntityAndField($this->className, $fieldName);
547
            }
548
549 44
            if ($property instanceof ToManyAssociationMetadata) {
550 1
                throw MappingException::illegalToManyIdentifierAssociation($this->className, $property->getName());
551
            }
552
553 43
            if (! in_array($property->getName(), $this->identifier, true)) {
554 43
                $this->identifier[] = $property->getName();
555
            }
556
        }
557
558
        // Cascades
559 273
        $cascadeTypes = ['remove', 'persist', 'refresh'];
560 273
        $cascades     = array_map('strtolower', $property->getCascade());
561
562 273
        if (in_array('all', $cascades, true)) {
563 6
            $cascades = $cascadeTypes;
564
        }
565
566 273
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
567
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
568
569
            throw MappingException::invalidCascadeOption($diffCascades, $this->className, $fieldName);
570
        }
571
572 273
        $property->setCascade($cascades);
573 273
    }
574
575
    /**
576
     * Validates & completes a to-one association mapping.
577
     *
578
     * @param ToOneAssociationMetadata $property The association mapping to validate & complete.
579
     *
580
     * @throws RuntimeException
581
     * @throws MappingException
582
     */
583 242
    protected function validateAndCompleteToOneAssociationMetadata(ToOneAssociationMetadata $property)
584
    {
585 242
        $fieldName = $property->getName();
586
587 242
        if ($property->isOwningSide()) {
588 238
            $uniqueConstraintColumns = [];
589
590 238
            foreach ($property->getJoinColumns() as $joinColumn) {
591
                /** @var JoinColumnMetadata $joinColumn */
592 233
                if ($property instanceof OneToOneAssociationMetadata && $this->inheritanceType !== InheritanceType::SINGLE_TABLE) {
593 103
                    if (count($property->getJoinColumns()) === 1) {
594 101
                        if (! $property->isPrimaryKey()) {
595 101
                            $joinColumn->setUnique(true);
596
                        }
597
                    } else {
598 2
                        $uniqueConstraintColumns[] = $joinColumn->getColumnName();
599
                    }
600
                }
601
602 233
                $this->fieldNames[$joinColumn->getColumnName()] = $fieldName;
603
            }
604
605 238
            if ($uniqueConstraintColumns) {
606 2
                if (! $this->table) {
607
                    throw new RuntimeException(
608
                        'ClassMetadata::setTable() has to be called before defining a one to one relationship.'
609
                    );
610
                }
611
612 2
                $this->table->addUniqueConstraint(
613
                    [
614 2
                        'name'    => sprintf('%s_uniq', $fieldName),
615 2
                        'columns' => $uniqueConstraintColumns,
616
                        'options' => [],
617
                        'flags'   => [],
618
                    ]
619
                );
620
            }
621
        }
622
623 242
        if ($property->isOrphanRemoval()) {
624 7
            $cascades = $property->getCascade();
625
626 7
            if (! in_array('remove', $cascades, true)) {
627 6
                $cascades[] = 'remove';
628
629 6
                $property->setCascade($cascades);
630
            }
631
632
            // @todo guilhermeblanco where is this used?
633
            // @todo guilhermeblanco Shouldn￿'t we iterate through JoinColumns to set non-uniqueness?
634
            //$property->setUnique(false);
635
        }
636
637 242
        if ($property->isPrimaryKey() && ! $property->isOwningSide()) {
638 1
            throw MappingException::illegalInverseIdentifierAssociation($this->className, $fieldName);
639
        }
640 241
    }
641
642
    /**
643
     * Validates & completes a one-to-many association mapping.
644
     *
645
     * @param OneToManyAssociationMetadata $property The association mapping to validate & complete.
646
     *
647
     * @throws MappingException
648
     */
649 118
    protected function validateAndCompleteOneToManyMapping(OneToManyAssociationMetadata $property)
650
    {
651
        // OneToMany MUST have mappedBy
652 118
        if (! $property->getMappedBy()) {
653
            throw MappingException::oneToManyRequiresMappedBy($property->getName());
654
        }
655
656 118
        if ($property->isOrphanRemoval()) {
657 19
            $cascades = $property->getCascade();
658
659 19
            if (! in_array('remove', $cascades, true)) {
660 16
                $cascades[] = 'remove';
661
662 16
                $property->setCascade($cascades);
663
            }
664
        }
665 118
    }
666
667
    /**
668
     * {@inheritDoc}
669
     */
670 401
    public function getIdentifierFieldNames()
671
    {
672 401
        return $this->identifier;
673
    }
674
675
    /**
676
     * Gets the name of the single id field. Note that this only works on
677
     * entity classes that have a single-field pk.
678
     *
679
     * @return string
680
     *
681
     * @throws MappingException If the class has a composite primary key.
682
     */
683 151
    public function getSingleIdentifierFieldName()
684
    {
685 151
        if ($this->isIdentifierComposite()) {
686 1
            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->className);
687
        }
688
689 150
        if (! isset($this->identifier[0])) {
690 1
            throw MappingException::noIdDefined($this->className);
691
        }
692
693 149
        return $this->identifier[0];
694
    }
695
696
    /**
697
     * INTERNAL:
698
     * Sets the mapped identifier/primary key fields of this class.
699
     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
700
     *
701
     * @param mixed[] $identifier
702
     */
703 100
    public function setIdentifier(array $identifier)
704
    {
705 100
        $this->identifier = $identifier;
706 100
    }
707
708
    /**
709
     * {@inheritDoc}
710
     */
711 1053
    public function getIdentifier()
712
    {
713 1053
        return $this->identifier;
714
    }
715
716
    /**
717
     * {@inheritDoc}
718
     */
719 189
    public function hasField($fieldName)
720
    {
721 189
        return isset($this->properties[$fieldName])
722 189
            && $this->properties[$fieldName] instanceof FieldMetadata;
723
    }
724
725
    /**
726
     * Returns an array with identifier column names and their corresponding ColumnMetadata.
727
     *
728
     * @return ColumnMetadata[]
729
     */
730 441
    public function getIdentifierColumns(EntityManagerInterface $em) : array
731
    {
732 441
        $columns = [];
733
734 441
        foreach ($this->identifier as $idProperty) {
735 441
            $property = $this->getProperty($idProperty);
736
737 441
            if ($property instanceof FieldMetadata) {
738 435
                $columns[$property->getColumnName()] = $property;
739
740 435
                continue;
741
            }
742
743
            /** @var AssociationMetadata $property */
744
745
            // Association defined as Id field
746 25
            $targetClass = $em->getClassMetadata($property->getTargetEntity());
747
748 25
            if (! $property->isOwningSide()) {
749
                $property    = $targetClass->getProperty($property->getMappedBy());
0 ignored issues
show
Bug introduced by
The method getProperty() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. ( Ignorable by Annotation )

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

749
                /** @scrutinizer ignore-call */ 
750
                $property    = $targetClass->getProperty($property->getMappedBy());

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...
750
                $targetClass = $em->getClassMetadata($property->getTargetEntity());
751
            }
752
753 25
            $joinColumns = $property instanceof ManyToManyAssociationMetadata
754
                ? $property->getJoinTable()->getInverseJoinColumns()
755 25
                : $property->getJoinColumns();
756
757 25
            foreach ($joinColumns as $joinColumn) {
758
                /** @var JoinColumnMetadata $joinColumn */
759 25
                $columnName           = $joinColumn->getColumnName();
760 25
                $referencedColumnName = $joinColumn->getReferencedColumnName();
761
762 25
                if (! $joinColumn->getType()) {
763 13
                    $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $em));
764
                }
765
766 25
                $columns[$columnName] = $joinColumn;
767
            }
768
        }
769
770 441
        return $columns;
771
    }
772
773
    /**
774
     * Gets the name of the primary table.
775
     */
776 1571
    public function getTableName() : ?string
777
    {
778 1571
        return $this->table->getName();
779
    }
780
781
    /**
782
     * Gets primary table's schema name.
783
     */
784 23
    public function getSchemaName() : ?string
785
    {
786 23
        return $this->table->getSchema();
787
    }
788
789
    /**
790
     * Gets the table name to use for temporary identifier tables of this class.
791
     */
792 7
    public function getTemporaryIdTableName() : string
793
    {
794 7
        $schema = $this->getSchemaName() === null
795 6
            ? ''
796 7
            : $this->getSchemaName() . '_';
797
798
        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
799 7
        return $schema . $this->getTableName() . '_id_tmp';
800
    }
801
802
    /**
803
     * Sets the mapped subclasses of this class.
804
     *
805
     * @param string[] $subclasses The names of all mapped subclasses.
806
     *
807
     * @todo guilhermeblanco Only used for ClassMetadataTest. Remove if possible!
808
     */
809 4
    public function setSubclasses(array $subclasses) : void
810
    {
811 4
        foreach ($subclasses as $subclass) {
812 3
            $this->subClasses[] = $subclass;
813
        }
814 4
    }
815
816
    /**
817
     * @return string[]
818
     */
819 1080
    public function getSubClasses() : array
820
    {
821 1080
        return $this->subClasses;
822
    }
823
824
    /**
825
     * Sets the inheritance type used by the class and its subclasses.
826
     *
827
     * @param int $type
828
     *
829
     * @throws MappingException
830
     */
831 120
    public function setInheritanceType($type) : void
832
    {
833 120
        if (! $this->isInheritanceType($type)) {
834
            throw MappingException::invalidInheritanceType($this->className, $type);
835
        }
836
837 120
        $this->inheritanceType = $type;
838 120
    }
839
840
    /**
841
     * Sets the override property mapping for an entity relationship.
842
     *
843
     * @throws RuntimeException
844
     * @throws MappingException
845
     * @throws CacheException
846
     */
847 10
    public function setPropertyOverride(Property $property) : void
848
    {
849 10
        $fieldName = $property->getName();
850
851 10
        if (! isset($this->properties[$fieldName])) {
852
            throw MappingException::invalidOverrideFieldName($this->className, $fieldName);
853
        }
854
855 10
        $originalProperty          = $this->getProperty($fieldName);
856 10
        $originalPropertyClassName = get_class($originalProperty);
857
858
        // If moving from transient to persistent, assume it's a new property
859 10
        if ($originalPropertyClassName === TransientMetadata::class) {
860 1
            unset($this->properties[$fieldName]);
861
862 1
            $this->addProperty($property);
863
864 1
            return;
865
        }
866
867
        // Do not allow to change property type
868 9
        if ($originalPropertyClassName !== get_class($property)) {
869
            throw MappingException::invalidOverridePropertyType($this->className, $fieldName);
870
        }
871
872
        // Do not allow to change version property
873 9
        if ($originalProperty instanceof FieldMetadata && $originalProperty->isVersioned()) {
874
            throw MappingException::invalidOverrideVersionField($this->className, $fieldName);
875
        }
876
877 9
        unset($this->properties[$fieldName]);
878
879 9
        if ($property instanceof FieldMetadata) {
880
            // Unset defined fieldName prior to override
881 5
            unset($this->fieldNames[$originalProperty->getColumnName()]);
0 ignored issues
show
Bug introduced by
The method getColumnName() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as 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

881
            unset($this->fieldNames[$originalProperty->/** @scrutinizer ignore-call */ getColumnName()]);
Loading history...
882
883
            // Revert what should not be allowed to change
884 5
            $property->setDeclaringClass($originalProperty->getDeclaringClass());
885 5
            $property->setPrimaryKey($originalProperty->isPrimaryKey());
886 9
        } elseif ($property instanceof AssociationMetadata) {
887
            // Unset all defined fieldNames prior to override
888 9
            if ($originalProperty instanceof ToOneAssociationMetadata && $originalProperty->isOwningSide()) {
889 5
                foreach ($originalProperty->getJoinColumns() as $joinColumn) {
890 5
                    unset($this->fieldNames[$joinColumn->getColumnName()]);
891
                }
892
            }
893
894
            // Override what it should be allowed to change
895 9
            if ($property->getInversedBy()) {
896 2
                $originalProperty->setInversedBy($property->getInversedBy());
0 ignored issues
show
Bug introduced by
The method setInversedBy() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\AssociationMetadata. ( Ignorable by Annotation )

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

896
                $originalProperty->/** @scrutinizer ignore-call */ 
897
                                   setInversedBy($property->getInversedBy());
Loading history...
897
            }
898
899 9
            if ($property->getFetchMode() !== $originalProperty->getFetchMode()) {
0 ignored issues
show
Bug introduced by
The method getFetchMode() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\AssociationMetadata. ( Ignorable by Annotation )

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

899
            if ($property->getFetchMode() !== $originalProperty->/** @scrutinizer ignore-call */ getFetchMode()) {
Loading history...
900 2
                $originalProperty->setFetchMode($property->getFetchMode());
0 ignored issues
show
Bug introduced by
The method setFetchMode() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\AssociationMetadata. ( Ignorable by Annotation )

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

900
                $originalProperty->/** @scrutinizer ignore-call */ 
901
                                   setFetchMode($property->getFetchMode());
Loading history...
901
            }
902
903 9
            if ($originalProperty instanceof ToOneAssociationMetadata && $property->getJoinColumns()) {
0 ignored issues
show
Bug introduced by
The method getJoinColumns() does not exist on Doctrine\ORM\Mapping\AssociationMetadata. It seems like you code against a sub-type of Doctrine\ORM\Mapping\AssociationMetadata such as Doctrine\ORM\Mapping\ToOneAssociationMetadata. ( Ignorable by Annotation )

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

903
            if ($originalProperty instanceof ToOneAssociationMetadata && $property->/** @scrutinizer ignore-call */ getJoinColumns()) {
Loading history...
904 5
                $originalProperty->setJoinColumns($property->getJoinColumns());
905 8
            } elseif ($originalProperty instanceof ManyToManyAssociationMetadata && $property->getJoinTable()) {
0 ignored issues
show
Bug introduced by
The method getJoinTable() does not exist on Doctrine\ORM\Mapping\AssociationMetadata. It seems like you code against a sub-type of Doctrine\ORM\Mapping\AssociationMetadata such as Doctrine\ORM\Mapping\ManyToManyAssociationMetadata. ( Ignorable by Annotation )

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

905
            } elseif ($originalProperty instanceof ManyToManyAssociationMetadata && $property->/** @scrutinizer ignore-call */ getJoinTable()) {
Loading history...
906 4
                $originalProperty->setJoinTable($property->getJoinTable());
907
            }
908
909 9
            $property = $originalProperty;
910
        }
911
912 9
        $this->addProperty($property);
0 ignored issues
show
Bug introduced by
It seems like $property can also be of type null; however, parameter $property of Doctrine\ORM\Mapping\ClassMetadata::addProperty() does only seem to accept Doctrine\ORM\Mapping\Property, 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

912
        $this->addProperty(/** @scrutinizer ignore-type */ $property);
Loading history...
913 9
    }
914
915
    /**
916
     * Checks if this entity is the root in any entity-inheritance-hierarchy.
917
     *
918
     * @return bool
919
     */
920 336
    public function isRootEntity()
921
    {
922 336
        return $this->className === $this->getRootClassName();
923
    }
924
925
    /**
926
     * Checks whether a mapped field is inherited from a superclass.
927
     *
928
     * @param string $fieldName
929
     *
930
     * @return bool TRUE if the field is inherited, FALSE otherwise.
931
     */
932 622
    public function isInheritedProperty($fieldName)
933
    {
934 622
        $declaringClass = $this->properties[$fieldName]->getDeclaringClass();
935
936 622
        return $declaringClass->className !== $this->className;
937
    }
938
939
    /**
940
     * {@inheritdoc}
941
     */
942 431
    public function setTable(TableMetadata $table) : void
943
    {
944 431
        $this->table = $table;
945
946
        // Make sure inherited and declared properties reflect newly defined table
947 431
        foreach ($this->properties as $property) {
948
            switch (true) {
949 95
                case $property instanceof FieldMetadata:
950 95
                    $property->setTableName($property->getTableName() ?? $table->getName());
951 95
                    break;
952
953 42
                case $property instanceof ToOneAssociationMetadata:
954
                    // Resolve association join column table names
955 34
                    foreach ($property->getJoinColumns() as $joinColumn) {
956
                        /** @var JoinColumnMetadata $joinColumn */
957 34
                        $joinColumn->setTableName($joinColumn->getTableName() ?? $table->getName());
958
                    }
959
960 34
                    break;
961
            }
962
        }
963 431
    }
964
965
    /**
966
     * Checks whether the given type identifies an inheritance type.
967
     *
968
     * @param int $type
969
     *
970
     * @return bool TRUE if the given type identifies an inheritance type, FALSe otherwise.
971
     */
972 120
    private function isInheritanceType($type)
973
    {
974 120
        return $type === InheritanceType::NONE
975 93
            || $type === InheritanceType::SINGLE_TABLE
976 51
            || $type === InheritanceType::JOINED
977 120
            || $type === InheritanceType::TABLE_PER_CLASS;
978
    }
979
980 916
    public function getColumn(string $columnName) : ?LocalColumnMetadata
981
    {
982 916
        foreach ($this->properties as $property) {
983 916
            if ($property instanceof LocalColumnMetadata && $property->getColumnName() === $columnName) {
984 916
                return $property;
985
            }
986
        }
987
988
        return null;
989
    }
990
991
    /**
992
     * Add a property mapping.
993
     *
994
     * @throws RuntimeException
995
     * @throws MappingException
996
     * @throws CacheException
997
     * @throws ReflectionException
998
     */
999 421
    public function addProperty(Property $property) : void
1000
    {
1001 421
        $fieldName = $property->getName();
1002
1003
        // Check for empty field name
1004 421
        if (empty($fieldName)) {
1005 1
            throw MappingException::missingFieldName($this->className);
1006
        }
1007
1008 420
        $property->setDeclaringClass($this);
1009
1010
        switch (true) {
1011 420
            case $property instanceof FieldMetadata:
1012 406
                $this->fieldNames[$property->getColumnName()] = $property->getName();
1013
1014 406
                if ($property->isVersioned()) {
1015 20
                    $this->versionProperty = $property;
1016
                }
1017
1018 406
                break;
1019
1020 279
            case $property instanceof OneToOneAssociationMetadata:
1021 125
                $this->validateAndCompleteAssociationMapping($property);
1022 124
                $this->validateAndCompleteToOneAssociationMetadata($property);
1023 123
                break;
1024
1025 219
            case $property instanceof OneToManyAssociationMetadata:
1026 118
                $this->validateAndCompleteAssociationMapping($property);
1027 118
                $this->validateAndCompleteOneToManyMapping($property);
1028 118
                break;
1029
1030 214
            case $property instanceof ManyToOneAssociationMetadata:
1031 149
                $this->validateAndCompleteAssociationMapping($property);
1032 148
                $this->validateAndCompleteToOneAssociationMetadata($property);
1033 148
                break;
1034
1035 119
            case $property instanceof ManyToManyAssociationMetadata:
1036 104
                $this->validateAndCompleteAssociationMapping($property);
1037 103
                break;
1038
1039
            default:
1040
                // Transient properties are ignored on purpose here! =)
1041 29
                break;
1042
        }
1043
1044 416
        if ($property->isPrimaryKey() && ! in_array($fieldName, $this->identifier, true)) {
1045 394
            $this->identifier[] = $fieldName;
1046
        }
1047
1048 416
        parent::addProperty($property);
1049 416
    }
1050
1051
    /**
1052
     * INTERNAL:
1053
     * Adds a property mapping without completing/validating it.
1054
     * This is mainly used to add inherited property mappings to derived classes.
1055
     */
1056 96
    public function addInheritedProperty(Property $property)
1057
    {
1058 96
        if (isset($this->properties[$property->getName()])) {
1059 1
            throw MappingException::duplicateProperty($this->className, $this->getProperty($property->getName()));
0 ignored issues
show
Bug introduced by
It seems like $this->getProperty($property->getName()) can also be of type null; however, parameter $property of Doctrine\ORM\Mapping\Map...on::duplicateProperty() does only seem to accept Doctrine\ORM\Mapping\Property, 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

1059
            throw MappingException::duplicateProperty($this->className, /** @scrutinizer ignore-type */ $this->getProperty($property->getName()));
Loading history...
1060
        }
1061
1062 96
        $declaringClass    = $property->getDeclaringClass();
1063 96
        $inheritedProperty = $declaringClass->isMappedSuperclass ? clone $property : $property;
1064
1065 96
        if ($inheritedProperty instanceof FieldMetadata) {
1066 95
            if (! $declaringClass->isMappedSuperclass) {
1067 73
                $inheritedProperty->setTableName($property->getTableName());
0 ignored issues
show
Bug introduced by
The method getTableName() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as 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

1067
                $inheritedProperty->setTableName($property->/** @scrutinizer ignore-call */ getTableName());
Loading history...
1068
            }
1069
1070 95
            if ($inheritedProperty->isVersioned()) {
1071 4
                $this->versionProperty = $inheritedProperty;
1072
            }
1073
1074 95
            $this->fieldNames[$property->getColumnName()] = $property->getName();
1075 43
        } elseif ($inheritedProperty instanceof AssociationMetadata) {
1076 42
            if ($declaringClass->isMappedSuperclass) {
1077 10
                $inheritedProperty->setSourceEntity($this->className);
1078
            }
1079
1080
            // Need to add inherited fieldNames
1081 42
            if ($inheritedProperty instanceof ToOneAssociationMetadata && $inheritedProperty->isOwningSide()) {
1082 35
                foreach ($inheritedProperty->getJoinColumns() as $joinColumn) {
1083
                    /** @var JoinColumnMetadata $joinColumn */
1084 34
                    $this->fieldNames[$joinColumn->getColumnName()] = $property->getName();
1085
                }
1086
            }
1087
        }
1088
1089 96
        $this->properties[$property->getName()] = $inheritedProperty;
1090 96
    }
1091
1092
    /**
1093
     * Registers a custom repository class for the entity class.
1094
     *
1095
     * @param string|null $repositoryClassName The class name of the custom mapper.
1096
     */
1097 30
    public function setCustomRepositoryClassName(?string $repositoryClassName)
1098
    {
1099 30
        $this->customRepositoryClassName = $repositoryClassName;
1100 30
    }
1101
1102 163
    public function getCustomRepositoryClassName() : ?string
1103
    {
1104 163
        return $this->customRepositoryClassName;
1105
    }
1106
1107
    /**
1108
     * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
1109
     *
1110
     * @param string $lifecycleEvent
1111
     *
1112
     * @return bool
1113
     */
1114
    public function hasLifecycleCallbacks($lifecycleEvent)
1115
    {
1116
        return isset($this->lifecycleCallbacks[$lifecycleEvent]);
1117
    }
1118
1119
    /**
1120
     * Gets the registered lifecycle callbacks for an event.
1121
     *
1122
     * @param string $event
1123
     *
1124
     * @return string[]
1125
     */
1126
    public function getLifecycleCallbacks($event) : array
1127
    {
1128
        return $this->lifecycleCallbacks[$event] ?? [];
1129
    }
1130
1131
    /**
1132
     * Adds a lifecycle callback for entities of this class.
1133
     */
1134 16
    public function addLifecycleCallback(string $eventName, string $methodName)
1135
    {
1136 16
        if (in_array($methodName, $this->lifecycleCallbacks[$eventName] ?? [], true)) {
1137 3
            return;
1138
        }
1139
1140 16
        $this->lifecycleCallbacks[$eventName][] = $methodName;
1141 16
    }
1142
1143
    /**
1144
     * Adds a entity listener for entities of this class.
1145
     *
1146
     * @param string $eventName The entity lifecycle event.
1147
     * @param string $class     The listener class.
1148
     * @param string $method    The listener callback method.
1149
     *
1150
     * @throws MappingException
1151
     */
1152 11
    public function addEntityListener(string $eventName, string $class, string $methodName) : void
1153
    {
1154
        $listener = [
1155 11
            'class'  => $class,
1156 11
            'method' => $methodName,
1157
        ];
1158
1159 11
        if (! class_exists($class)) {
1160
            throw MappingException::entityListenerClassNotFound($class, $this->className);
1161
        }
1162
1163 11
        if (! method_exists($class, $methodName)) {
1164
            throw MappingException::entityListenerMethodNotFound($class, $methodName, $this->className);
1165
        }
1166
1167
        // Check if entity listener already got registered and ignore it if positive
1168 11
        if (in_array($listener, $this->entityListeners[$eventName] ?? [], true)) {
1169 5
            return;
1170
        }
1171
1172 11
        $this->entityListeners[$eventName][] = $listener;
1173 11
    }
1174
1175
    /**
1176
     * Sets the discriminator column definition.
1177
     *
1178
     * @see getDiscriminatorColumn()
1179
     *
1180
     * @throws MappingException
1181
     */
1182 94
    public function setDiscriminatorColumn(DiscriminatorColumnMetadata $discriminatorColumn) : void
1183
    {
1184 94
        if (isset($this->fieldNames[$discriminatorColumn->getColumnName()])) {
1185
            throw MappingException::duplicateColumnName($this->className, $discriminatorColumn->getColumnName());
1186
        }
1187
1188 94
        $discriminatorColumn->setTableName($discriminatorColumn->getTableName() ?? $this->getTableName());
1189
1190 94
        $allowedTypeList = ['boolean', 'array', 'object', 'datetime', 'time', 'date'];
1191
1192 94
        if (in_array($discriminatorColumn->getTypeName(), $allowedTypeList, true)) {
1193
            throw MappingException::invalidDiscriminatorColumnType($discriminatorColumn->getTypeName());
1194
        }
1195
1196 94
        $this->discriminatorColumn = $discriminatorColumn;
1197 94
    }
1198
1199
    /**
1200
     * Sets the discriminator values used by this class.
1201
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
1202
     *
1203
     * @param string[] $map
1204
     *
1205
     * @throws MappingException
1206
     */
1207 89
    public function setDiscriminatorMap(array $map) : void
1208
    {
1209 89
        foreach ($map as $value => $className) {
1210 89
            $this->addDiscriminatorMapClass($value, $className);
1211
        }
1212 89
    }
1213
1214
    /**
1215
     * Adds one entry of the discriminator map with a new class and corresponding name.
1216
     *
1217
     * @param string|int $name
1218
     *
1219
     * @throws MappingException
1220
     */
1221 89
    public function addDiscriminatorMapClass($name, string $className) : void
1222
    {
1223 89
        $this->discriminatorMap[$name] = $className;
1224
1225 89
        if ($this->className === $className) {
1226 75
            $this->discriminatorValue = $name;
1227
1228 75
            return;
1229
        }
1230
1231 88
        if (! (class_exists($className) || interface_exists($className))) {
1232
            throw MappingException::invalidClassInDiscriminatorMap($className, $this->className);
1233
        }
1234
1235 88
        if (is_subclass_of($className, $this->className) && ! in_array($className, $this->subClasses, true)) {
1236 83
            $this->subClasses[] = $className;
1237
        }
1238 88
    }
1239
1240 1031
    public function getValueGenerationPlan() : ValueGenerationPlan
1241
    {
1242 1031
        return $this->valueGenerationPlan;
1243
    }
1244
1245 367
    public function setValueGenerationPlan(ValueGenerationPlan $valueGenerationPlan) : void
1246
    {
1247 367
        $this->valueGenerationPlan = $valueGenerationPlan;
1248 367
    }
1249
1250 399
    public function checkPropertyDuplication(string $columnName) : bool
1251
    {
1252 399
        return isset($this->fieldNames[$columnName])
1253 399
            || ($this->discriminatorColumn !== null && $this->discriminatorColumn->getColumnName() === $columnName);
1254
    }
1255
1256
    /**
1257
     * Marks this class as read only, no change tracking is applied to it.
1258
     */
1259 2
    public function asReadOnly() : void
1260
    {
1261 2
        $this->readOnly = true;
1262 2
    }
1263
1264
    /**
1265
     * Whether this class is read only or not.
1266
     */
1267 446
    public function isReadOnly() : bool
1268
    {
1269 446
        return $this->readOnly;
1270
    }
1271
1272 1091
    public function isVersioned() : bool
1273
    {
1274 1091
        return $this->versionProperty !== null;
1275
    }
1276
1277
    /**
1278
     * Map Embedded Class
1279
     *
1280
     * @param mixed[] $mapping
1281
     *
1282
     * @throws MappingException
1283
     */
1284
    public function mapEmbedded(array $mapping) : void
0 ignored issues
show
Unused Code introduced by
The parameter $mapping is not used and could be removed. ( Ignorable by Annotation )

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

1284
    public function mapEmbedded(/** @scrutinizer ignore-unused */ array $mapping) : void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1285
    {
1286
        /*if (isset($this->properties[$mapping['fieldName']])) {
1287
            throw MappingException::duplicateProperty($this->className, $this->getProperty($mapping['fieldName']));
1288
        }
1289
1290
        $this->embeddedClasses[$mapping['fieldName']] = [
1291
            'class'          => $this->fullyQualifiedClassName($mapping['class']),
1292
            'columnPrefix'   => $mapping['columnPrefix'],
1293
            'declaredField'  => $mapping['declaredField'] ?? null,
1294
            'originalField'  => $mapping['originalField'] ?? null,
1295
            'declaringClass' => $this,
1296
        ];*/
1297
    }
1298
1299
    /**
1300
     * Inline the embeddable class
1301
     *
1302
     * @param string $property
1303
     */
1304
    public function inlineEmbeddable($property, ClassMetadata $embeddable) : void
0 ignored issues
show
Unused Code introduced by
The parameter $embeddable is not used and could be removed. ( Ignorable by Annotation )

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

1304
    public function inlineEmbeddable($property, /** @scrutinizer ignore-unused */ ClassMetadata $embeddable) : void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $property is not used and could be removed. ( Ignorable by Annotation )

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

1304
    public function inlineEmbeddable(/** @scrutinizer ignore-unused */ $property, ClassMetadata $embeddable) : void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1305
    {
1306
        /*foreach ($embeddable->fieldMappings as $fieldName => $fieldMapping) {
1307
            $fieldMapping['fieldName']     = $property . "." . $fieldName;
1308
            $fieldMapping['originalClass'] = $fieldMapping['originalClass'] ?? $embeddable->getClassName();
1309
            $fieldMapping['originalField'] = $fieldMapping['originalField'] ?? $fieldName;
1310
            $fieldMapping['declaredField'] = isset($fieldMapping['declaredField'])
1311
                ? $property . '.' . $fieldMapping['declaredField']
1312
                : $property;
1313
1314
            if (! empty($this->embeddedClasses[$property]['columnPrefix'])) {
1315
                $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName'];
1316
            } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) {
1317
                $fieldMapping['columnName'] = $this->namingStrategy->embeddedFieldToColumnName(
1318
                    $property,
1319
                    $fieldMapping['columnName'],
1320
                    $this->reflectionClass->getName(),
1321
                    $embeddable->reflectionClass->getName()
1322
                );
1323
            }
1324
1325
            $this->mapField($fieldMapping);
1326
        }*/
1327
    }
1328
}
1329