Failed Conditions
Push — master ( d60694...8601d9 )
by Guilherme
09:39
created

ClassMetadata::__toString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

215
        $this->setInheritanceType(/** @scrutinizer ignore-type */ $parent->inheritanceType);
Loading history...
216 98
        $this->setIdentifier($parent->identifier);
217 98
        $this->setChangeTrackingPolicy($parent->changeTrackingPolicy);
218
219 98
        if ($parent->discriminatorColumn) {
220 70
            $this->setDiscriminatorColumn($parent->discriminatorColumn);
221 70
            $this->setDiscriminatorMap($parent->discriminatorMap);
222
        }
223
224 98
        if ($parent->isMappedSuperclass) {
225 27
            $this->setCustomRepositoryClassName($parent->getCustomRepositoryClassName());
226
        }
227
228 98
        if ($parent->cache) {
229 3
            $this->setCache(clone $parent->cache);
230
        }
231
232 98
        if (! empty($parent->lifecycleCallbacks)) {
233 5
            $this->lifecycleCallbacks = $parent->lifecycleCallbacks;
234
        }
235
236 98
        if (! empty($parent->entityListeners)) {
237 7
            $this->entityListeners = $parent->entityListeners;
238
        }
239 98
    }
240
241
    public function setClassName(string $className)
242
    {
243
        $this->className = $className;
244
    }
245
246
    public function getColumnsIterator() : ArrayIterator
247
    {
248
        $iterator = parent::getColumnsIterator();
249
250
        if ($this->discriminatorColumn) {
251
            $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

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

739
                /** @scrutinizer ignore-call */ 
740
                $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...
740
                $targetClass = $em->getClassMetadata($property->getTargetEntity());
741
            }
742
743 25
            $joinColumns = $property instanceof ManyToManyAssociationMetadata
744
                ? $property->getJoinTable()->getInverseJoinColumns()
745 25
                : $property->getJoinColumns();
746
747 25
            foreach ($joinColumns as $joinColumn) {
748
                /** @var JoinColumnMetadata $joinColumn */
749 25
                $columnName           = $joinColumn->getColumnName();
750 25
                $referencedColumnName = $joinColumn->getReferencedColumnName();
751
752 25
                if (! $joinColumn->getType()) {
753 13
                    $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $em));
0 ignored issues
show
Bug introduced by
$targetClass of type Doctrine\Common\Persistence\Mapping\ClassMetadata is incompatible with the type Doctrine\ORM\Mapping\ClassMetadata expected by parameter $class of Doctrine\ORM\Utility\Per...lper::getTypeOfColumn(). ( Ignorable by Annotation )

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

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

871
            unset($this->fieldNames[$originalProperty->/** @scrutinizer ignore-call */ getColumnName()]);
Loading history...
872
873
            // Revert what should not be allowed to change
874 5
            $property->setDeclaringClass($originalProperty->getDeclaringClass());
875 5
            $property->setPrimaryKey($originalProperty->isPrimaryKey());
876 9
        } elseif ($property instanceof AssociationMetadata) {
877
            // Unset all defined fieldNames prior to override
878 9
            if ($originalProperty instanceof ToOneAssociationMetadata && $originalProperty->isOwningSide()) {
879 5
                foreach ($originalProperty->getJoinColumns() as $joinColumn) {
880 5
                    unset($this->fieldNames[$joinColumn->getColumnName()]);
881
                }
882
            }
883
884
            // Override what it should be allowed to change
885 9
            if ($property->getInversedBy()) {
886 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

886
                $originalProperty->/** @scrutinizer ignore-call */ 
887
                                   setInversedBy($property->getInversedBy());
Loading history...
887
            }
888
889 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

889
            if ($property->getFetchMode() !== $originalProperty->/** @scrutinizer ignore-call */ getFetchMode()) {
Loading history...
890 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

890
                $originalProperty->/** @scrutinizer ignore-call */ 
891
                                   setFetchMode($property->getFetchMode());
Loading history...
891
            }
892
893 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

893
            if ($originalProperty instanceof ToOneAssociationMetadata && $property->/** @scrutinizer ignore-call */ getJoinColumns()) {
Loading history...
894 5
                $originalProperty->setJoinColumns($property->getJoinColumns());
895 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

895
            } elseif ($originalProperty instanceof ManyToManyAssociationMetadata && $property->/** @scrutinizer ignore-call */ getJoinTable()) {
Loading history...
896 4
                $originalProperty->setJoinTable($property->getJoinTable());
897
            }
898
899 9
            $property = $originalProperty;
900
        }
901
902 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

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

1049
            throw MappingException::duplicateProperty($this->className, /** @scrutinizer ignore-type */ $this->getProperty($property->getName()));
Loading history...
1050
        }
1051
1052 96
        $declaringClass    = $property->getDeclaringClass();
1053 96
        $inheritedProperty = $declaringClass->isMappedSuperclass ? clone $property : $property;
1054
1055 96
        if ($inheritedProperty instanceof FieldMetadata) {
1056 95
            if (! $declaringClass->isMappedSuperclass) {
1057 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

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

1274
    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...
1275
    {
1276
        /*if (isset($this->properties[$mapping['fieldName']])) {
1277
            throw MappingException::duplicateProperty($this->className, $this->getProperty($mapping['fieldName']));
1278
        }
1279
1280
        $this->embeddedClasses[$mapping['fieldName']] = [
1281
            'class'          => $this->fullyQualifiedClassName($mapping['class']),
1282
            'columnPrefix'   => $mapping['columnPrefix'],
1283
            'declaredField'  => $mapping['declaredField'] ?? null,
1284
            'originalField'  => $mapping['originalField'] ?? null,
1285
            'declaringClass' => $this,
1286
        ];*/
1287
    }
1288
1289
    /**
1290
     * Inline the embeddable class
1291
     *
1292
     * @param string $property
1293
     */
1294
    public function inlineEmbeddable($property, ClassMetadata $embeddable) : void
0 ignored issues
show
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

1294
    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...
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

1294
    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...
1295
    {
1296
        /*foreach ($embeddable->fieldMappings as $fieldName => $fieldMapping) {
1297
            $fieldMapping['fieldName']     = $property . "." . $fieldName;
1298
            $fieldMapping['originalClass'] = $fieldMapping['originalClass'] ?? $embeddable->getClassName();
1299
            $fieldMapping['originalField'] = $fieldMapping['originalField'] ?? $fieldName;
1300
            $fieldMapping['declaredField'] = isset($fieldMapping['declaredField'])
1301
                ? $property . '.' . $fieldMapping['declaredField']
1302
                : $property;
1303
1304
            if (! empty($this->embeddedClasses[$property]['columnPrefix'])) {
1305
                $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName'];
1306
            } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) {
1307
                $fieldMapping['columnName'] = $this->namingStrategy->embeddedFieldToColumnName(
1308
                    $property,
1309
                    $fieldMapping['columnName'],
1310
                    $this->reflectionClass->getName(),
1311
                    $embeddable->reflectionClass->getName()
1312
                );
1313
            }
1314
1315
            $this->mapField($fieldMapping);
1316
        }*/
1317
    }
1318
}
1319