Failed Conditions
Push — master ( 62de42...7c9ab7 )
by Marco
29s queued 18s
created

lib/Doctrine/ORM/Mapping/ClassMetadata.php (1 issue)

Labels
Severity
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 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
     * NamingStrategy determining the default column and table names.
176
     *
177
     * @var NamingStrategy
178
     */
179
    protected $namingStrategy;
180
181
    /**
182
     * Value generation plan is responsible for generating values for auto-generated fields.
183
     *
184
     * @var ValueGenerationPlan
185
     */
186
    protected $valueGenerationPlan;
187
188
    /**
189
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
190
     * metadata of the class with the given name.
191
     *
192
     * @param string $entityName The name of the entity class.
193
     */
194 466
    public function __construct(
195
        string $entityName,
196
        ClassMetadataBuildingContext $metadataBuildingContext
197
    ) {
198 466
        parent::__construct($entityName, $metadataBuildingContext);
199
200 466
        $this->namingStrategy = $metadataBuildingContext->getNamingStrategy();
201 466
    }
202
203 2
    public function setClassName(string $className)
204
    {
205 2
        $this->className = $className;
206 2
    }
207
208
    public function getColumnsIterator() : ArrayIterator
209
    {
210
        $iterator = parent::getColumnsIterator();
211
212
        if ($this->discriminatorColumn) {
213
            $iterator->offsetSet($this->discriminatorColumn->getColumnName(), $this->discriminatorColumn);
214
        }
215
216
        return $iterator;
217
    }
218
219 11
    public function getAncestorsIterator() : ArrayIterator
220
    {
221 11
        $ancestors = new ArrayIterator();
222 11
        $parent    = $this;
223
224 11
        while (($parent = $parent->parent) !== null) {
225 8
            if ($parent instanceof ClassMetadata && $parent->isMappedSuperclass) {
226 1
                continue;
227
            }
228
229 7
            $ancestors->append($parent);
230
        }
231
232 11
        return $ancestors;
233
    }
234
235 1259
    public function getRootClassName() : string
236
    {
237 1259
        return $this->parent instanceof ClassMetadata && ! $this->parent->isMappedSuperclass
238 402
            ? $this->parent->getRootClassName()
239 1259
            : $this->className;
240
    }
241
242
    /**
243
     * Handles metadata cloning nicely.
244
     */
245 13
    public function __clone()
246
    {
247 13
        if ($this->cache) {
248 12
            $this->cache = clone $this->cache;
249
        }
250
251 13
        foreach ($this->declaredProperties as $name => $property) {
252 13
            $this->declaredProperties[$name] = clone $property;
253
        }
254 13
    }
255
256
    /**
257
     * Creates a string representation of this instance.
258
     *
259
     * @return string The string representation of this instance.
260
     *
261
     * @todo Construct meaningful string representation.
262
     */
263
    public function __toString()
264
    {
265
        return self::class . '@' . spl_object_id($this);
266
    }
267
268
    /**
269
     * Determines which fields get serialized.
270
     *
271
     * It is only serialized what is necessary for best unserialization performance.
272
     * That means any metadata properties that are not set or empty or simply have
273
     * their default value are NOT serialized.
274
     *
275
     * Parts that are also NOT serialized because they can not be properly unserialized:
276
     * - reflectionClass
277
     *
278
     * @return string[] The names of all the fields that should be serialized.
279
     */
280 5
    public function __sleep()
281
    {
282 5
        $serialized = [];
283
284
        // This metadata is always serialized/cached.
285 5
        $serialized = array_merge($serialized, [
286 5
            'declaredProperties',
287
            'fieldNames',
288
            //'embeddedClasses',
289
            'identifier',
290
            'className',
291
            'parent',
292
            'table',
293
            'valueGenerationPlan',
294
        ]);
295
296
        // The rest of the metadata is only serialized if necessary.
297 5
        if ($this->changeTrackingPolicy !== ChangeTrackingPolicy::DEFERRED_IMPLICIT) {
298
            $serialized[] = 'changeTrackingPolicy';
299
        }
300
301 5
        if ($this->customRepositoryClassName) {
302 1
            $serialized[] = 'customRepositoryClassName';
303
        }
304
305 5
        if ($this->inheritanceType !== InheritanceType::NONE) {
306 1
            $serialized[] = 'inheritanceType';
307 1
            $serialized[] = 'discriminatorColumn';
308 1
            $serialized[] = 'discriminatorValue';
309 1
            $serialized[] = 'discriminatorMap';
310 1
            $serialized[] = 'subClasses';
311
        }
312
313 5
        if ($this->isMappedSuperclass) {
314
            $serialized[] = 'isMappedSuperclass';
315
        }
316
317 5
        if ($this->isEmbeddedClass) {
318
            $serialized[] = 'isEmbeddedClass';
319
        }
320
321 5
        if ($this->isVersioned()) {
322
            $serialized[] = 'versionProperty';
323
        }
324
325 5
        if ($this->lifecycleCallbacks) {
326
            $serialized[] = 'lifecycleCallbacks';
327
        }
328
329 5
        if ($this->entityListeners) {
330 1
            $serialized[] = 'entityListeners';
331
        }
332
333 5
        if ($this->cache) {
334
            $serialized[] = 'cache';
335
        }
336
337 5
        if ($this->readOnly) {
338 1
            $serialized[] = 'readOnly';
339
        }
340
341 5
        return $serialized;
342
    }
343
344
    /**
345
     * Restores some state that can not be serialized/unserialized.
346
     */
347 1631
    public function wakeupReflection(ReflectionService $reflectionService) : void
348
    {
349
        // Restore ReflectionClass and properties
350 1631
        $this->reflectionClass = $reflectionService->getClass($this->className);
351
352 1631
        if (! $this->reflectionClass) {
353
            return;
354
        }
355
356 1631
        $this->className = $this->reflectionClass->getName();
357
358 1631
        foreach ($this->declaredProperties as $property) {
359
            /** @var Property $property */
360 1630
            $property->wakeupReflection($reflectionService);
361
        }
362 1631
    }
363
364
    /**
365
     * Validates Identifier.
366
     *
367
     * @throws MappingException
368
     */
369 369
    public function validateIdentifier() : void
370
    {
371 369
        if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
372 27
            return;
373
        }
374
375
        // Verify & complete identifier mapping
376 369
        if (! $this->identifier) {
377 4
            throw MappingException::identifierRequired($this->className);
378
        }
379
380
        $explicitlyGeneratedProperties = array_filter($this->declaredProperties, static function (Property $property) : bool {
381 365
            return $property instanceof FieldMetadata
382 365
                && $property->isPrimaryKey()
383 365
                && $property->hasValueGenerator();
384 365
        });
385
386 365
        if ($explicitlyGeneratedProperties && $this->isIdentifierComposite()) {
387
            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->className);
388
        }
389 365
    }
390
391
    /**
392
     * Validates association targets actually exist.
393
     *
394
     * @throws MappingException
395
     */
396 368
    public function validateAssociations() : void
397
    {
398 368
        array_map(
399
            function (Property $property) {
400 368
                if (! ($property instanceof AssociationMetadata)) {
401 365
                    return;
402
                }
403
404 250
                $targetEntity = $property->getTargetEntity();
405
406 250
                if (! class_exists($targetEntity)) {
407 1
                    throw MappingException::invalidTargetEntityClass($targetEntity, $this->className, $property->getName());
408
                }
409 368
            },
410 368
            $this->declaredProperties
411
        );
412 367
    }
413
414
    /**
415
     * Validates lifecycle callbacks.
416
     *
417
     * @throws MappingException
418
     */
419 368
    public function validateLifecycleCallbacks(ReflectionService $reflectionService) : void
420
    {
421 368
        foreach ($this->lifecycleCallbacks as $callbacks) {
422
            /** @var array $callbacks */
423 11
            foreach ($callbacks as $callbackFuncName) {
424 11
                if (! $reflectionService->hasPublicMethod($this->className, $callbackFuncName)) {
425 11
                    throw MappingException::lifecycleCallbackMethodNotFound($this->className, $callbackFuncName);
426
                }
427
            }
428
        }
429 367
    }
430
431
    /**
432
     * Sets the change tracking policy used by this class.
433
     */
434 102
    public function setChangeTrackingPolicy(string $policy) : void
435
    {
436 102
        $this->changeTrackingPolicy = $policy;
437 102
    }
438
439
    /**
440
     * Checks whether a field is part of the identifier/primary key field(s).
441
     *
442
     * @param string $fieldName The field name.
443
     *
444
     * @return bool TRUE if the field is part of the table identifier/primary key field(s), FALSE otherwise.
445
     */
446 1026
    public function isIdentifier(string $fieldName) : bool
447
    {
448 1026
        if (! $this->identifier) {
449 1
            return false;
450
        }
451
452 1025
        if (! $this->isIdentifierComposite()) {
453 1021
            return $fieldName === $this->identifier[0];
454
        }
455
456 93
        return in_array($fieldName, $this->identifier, true);
457
    }
458
459 1212
    public function isIdentifierComposite() : bool
460
    {
461 1212
        return isset($this->identifier[1]);
462
    }
463
464
    /**
465
     * Validates & completes the basic mapping information for field mapping.
466
     *
467
     * @throws MappingException If something is wrong with the mapping.
468
     */
469 413
    protected function validateAndCompleteFieldMapping(FieldMetadata $property)
470
    {
471 413
        $fieldName  = $property->getName();
472 413
        $columnName = $property->getColumnName();
473
474 413
        if (empty($columnName)) {
475 349
            $columnName = $this->namingStrategy->propertyToColumnName($fieldName, $this->className);
476
477 349
            $property->setColumnName($columnName);
478
        }
479
480 413
        if (! $this->isMappedSuperclass) {
481 406
            $property->setTableName($this->getTableName());
482
        }
483
484
        // Check for already declared column
485 413
        if (isset($this->fieldNames[$columnName]) ||
486 413
            ($this->discriminatorColumn !== null && $this->discriminatorColumn->getColumnName() === $columnName)) {
487 2
            throw MappingException::duplicateColumnName($this->className, $columnName);
488
        }
489
490
        // Complete id mapping
491 412
        if ($property->isPrimaryKey()) {
492 397
            if ($this->versionProperty !== null && $this->versionProperty->getName() === $fieldName) {
493
                throw MappingException::cannotVersionIdField($this->className, $fieldName);
494
            }
495
496 397
            if ($property->getType()->canRequireSQLConversion()) {
497
                throw MappingException::sqlConversionNotAllowedForPrimaryKeyProperties($this->className, $property);
498
            }
499
500 397
            if (! in_array($fieldName, $this->identifier, true)) {
501 397
                $this->identifier[] = $fieldName;
502
            }
503
        }
504
505 412
        $this->fieldNames[$columnName] = $fieldName;
506 412
    }
507
508
    /**
509
     * Validates & completes the basic mapping information for field mapping.
510
     *
511
     * @throws MappingException If something is wrong with the mapping.
512
     */
513 21
    protected function validateAndCompleteVersionFieldMapping(VersionFieldMetadata $property)
514
    {
515 21
        $this->versionProperty = $property;
516
517 21
        $options = $property->getOptions();
518
519 21
        if (isset($options['default'])) {
520
            return;
521
        }
522
523 21
        if (in_array($property->getTypeName(), ['integer', 'bigint', 'smallint'], true)) {
524 19
            $property->setOptions(array_merge($options, ['default' => 1]));
525
526 19
            return;
527
        }
528
529 3
        if ($property->getTypeName() === 'datetime') {
530 2
            $property->setOptions(array_merge($options, ['default' => 'CURRENT_TIMESTAMP']));
531
532 2
            return;
533
        }
534
535 1
        throw MappingException::unsupportedOptimisticLockingType($property->getType());
536
    }
537
538
    /**
539
     * Validates & completes the basic mapping information that is common to all
540
     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
541
     *
542
     * @throws MappingException If something is wrong with the mapping.
543
     * @throws CacheException   If entity is not cacheable.
544
     */
545 288
    protected function validateAndCompleteAssociationMapping(AssociationMetadata $property)
546
    {
547 288
        $fieldName    = $property->getName();
548 288
        $targetEntity = $property->getTargetEntity();
549
550 288
        if (! $targetEntity) {
551
            throw MappingException::missingTargetEntity($fieldName);
552
        }
553
554 288
        $property->setSourceEntity($this->className);
555 288
        $property->setTargetEntity($targetEntity);
556
557
        // Complete id mapping
558 288
        if ($property->isPrimaryKey()) {
559 47
            if ($property->isOrphanRemoval()) {
560 1
                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->className, $fieldName);
561
            }
562
563 46
            if (! in_array($property->getName(), $this->identifier, true)) {
564 46
                if ($property instanceof ToOneAssociationMetadata && count($property->getJoinColumns()) >= 2) {
565
                    throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
566
                        $property->getTargetEntity(),
567
                        $this->className,
568
                        $fieldName
569
                    );
570
                }
571
572 46
                $this->identifier[] = $property->getName();
573
            }
574
575 46
            if ($this->cache && ! $property->getCache()) {
576 2
                throw NonCacheableEntityAssociation::fromEntityAndField($this->className, $fieldName);
577
            }
578
579 44
            if ($property instanceof ToManyAssociationMetadata) {
580 1
                throw MappingException::illegalToManyIdentifierAssociation($this->className, $property->getName());
581
            }
582
        }
583
584
        // Cascades
585 284
        $cascadeTypes = ['remove', 'persist', 'refresh'];
586 284
        $cascades     = array_map('strtolower', $property->getCascade());
587
588 284
        if (in_array('all', $cascades, true)) {
589 6
            $cascades = $cascadeTypes;
590
        }
591
592 284
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
593 1
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
594
595 1
            throw MappingException::invalidCascadeOption($diffCascades, $this->className, $fieldName);
596
        }
597
598 283
        $property->setCascade($cascades);
599 283
    }
600
601
    /**
602
     * Validates & completes a to-one association mapping.
603
     *
604
     * @param ToOneAssociationMetadata $property The association mapping to validate & complete.
605
     *
606
     * @throws RuntimeException
607
     * @throws MappingException
608
     */
609 248
    protected function validateAndCompleteToOneAssociationMetadata(ToOneAssociationMetadata $property)
610
    {
611 248
        $fieldName = $property->getName();
612
613 248
        if ($property->isOwningSide()) {
614 244
            if (empty($property->getJoinColumns())) {
615
                // Apply default join column
616 86
                $property->addJoinColumn(new JoinColumnMetadata());
617
            }
618
619 244
            $uniqueConstraintColumns = [];
620
621 244
            foreach ($property->getJoinColumns() as $joinColumn) {
622
                /** @var JoinColumnMetadata $joinColumn */
623 244
                if ($property instanceof OneToOneAssociationMetadata && $this->inheritanceType !== InheritanceType::SINGLE_TABLE) {
624 112
                    if (count($property->getJoinColumns()) === 1) {
625 110
                        if (! $property->isPrimaryKey()) {
626 110
                            $joinColumn->setUnique(true);
627
                        }
628
                    } else {
629 2
                        $uniqueConstraintColumns[] = $joinColumn->getColumnName();
630
                    }
631
                }
632
633 244
                $joinColumn->setTableName(! $this->isMappedSuperclass ? $this->getTableName() : null);
634
635 244
                if (! $joinColumn->getColumnName()) {
636 100
                    $joinColumn->setColumnName($this->namingStrategy->joinColumnName($fieldName, $this->className));
637
                }
638
639 244
                if (! $joinColumn->getReferencedColumnName()) {
640 86
                    $joinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName());
641
                }
642
643 244
                $this->fieldNames[$joinColumn->getColumnName()] = $fieldName;
644
            }
645
646 244
            if ($uniqueConstraintColumns) {
647 2
                if (! $this->table) {
648
                    throw new RuntimeException(
649
                        'ClassMetadata::setTable() has to be called before defining a one to one relationship.'
650
                    );
651
                }
652
653 2
                $this->table->addUniqueConstraint(
654
                    [
655 2
                        'name'    => sprintf('%s_uniq', $fieldName),
656 2
                        'columns' => $uniqueConstraintColumns,
657
                        'options' => [],
658
                        'flags'   => [],
659
                    ]
660
                );
661
            }
662
        }
663
664 248
        if ($property->isOrphanRemoval()) {
665 7
            $cascades = $property->getCascade();
666
667 7
            if (! in_array('remove', $cascades, true)) {
668 6
                $cascades[] = 'remove';
669
670 6
                $property->setCascade($cascades);
671
            }
672
673
            // @todo guilhermeblanco where is this used?
674
            // @todo guilhermeblanco Shouldn￿'t we iterate through JoinColumns to set non-uniqueness?
675
            //$property->setUnique(false);
676
        }
677
678 248
        if ($property->isPrimaryKey() && ! $property->isOwningSide()) {
679 1
            throw MappingException::illegalInverseIdentifierAssociation($this->className, $fieldName);
680
        }
681 247
    }
682
683
    /**
684
     * Validates & completes a to-many association mapping.
685
     *
686
     * @param ToManyAssociationMetadata $property The association mapping to validate & complete.
687
     *
688
     * @throws MappingException
689
     */
690 172
    protected function validateAndCompleteToManyAssociationMetadata(ToManyAssociationMetadata $property)
691
    {
692
        // Do nothing
693 172
    }
694
695
    /**
696
     * Validates & completes a one-to-one association mapping.
697
     *
698
     * @param OneToOneAssociationMetadata $property The association mapping to validate & complete.
699
     */
700 129
    protected function validateAndCompleteOneToOneMapping(OneToOneAssociationMetadata $property)
701
    {
702
        // Do nothing
703 129
    }
704
705
    /**
706
     * Validates & completes a many-to-one association mapping.
707
     *
708
     * @param ManyToOneAssociationMetadata $property The association mapping to validate & complete.
709
     *
710
     * @throws MappingException
711
     */
712 148
    protected function validateAndCompleteManyToOneMapping(ManyToOneAssociationMetadata $property)
713
    {
714
        // A many-to-one mapping is essentially a one-one backreference
715 148
        if ($property->isOrphanRemoval()) {
716
            throw MappingException::illegalOrphanRemoval($this->className, $property->getName());
717
        }
718 148
    }
719
720
    /**
721
     * Validates & completes a one-to-many association mapping.
722
     *
723
     * @param OneToManyAssociationMetadata $property The association mapping to validate & complete.
724
     *
725
     * @throws MappingException
726
     */
727 116
    protected function validateAndCompleteOneToManyMapping(OneToManyAssociationMetadata $property)
728
    {
729
        // OneToMany MUST have mappedBy
730 116
        if (! $property->getMappedBy()) {
731
            throw MappingException::oneToManyRequiresMappedBy($property->getName());
732
        }
733
734 116
        if ($property->isOrphanRemoval()) {
735 19
            $cascades = $property->getCascade();
736
737 19
            if (! in_array('remove', $cascades, true)) {
738 16
                $cascades[] = 'remove';
739
740 16
                $property->setCascade($cascades);
741
            }
742
        }
743 116
    }
744
745
    /**
746
     * Validates & completes a many-to-many association mapping.
747
     *
748
     * @param ManyToManyAssociationMetadata $property The association mapping to validate & complete.
749
     *
750
     * @throws MappingException
751
     */
752 109
    protected function validateAndCompleteManyToManyMapping(ManyToManyAssociationMetadata $property)
753
    {
754 109
        if ($property->isOwningSide()) {
755
            // owning side MUST have a join table
756 97
            $joinTable = $property->getJoinTable() ?: new JoinTableMetadata();
757
758 97
            $property->setJoinTable($joinTable);
759
760 97
            if (! $joinTable->getName()) {
761 18
                $joinTableName = $this->namingStrategy->joinTableName(
762 18
                    $property->getSourceEntity(),
763 18
                    $property->getTargetEntity(),
764 18
                    $property->getName()
765
                );
766
767 18
                $joinTable->setName($joinTableName);
768
            }
769
770 97
            $selfReferencingEntityWithoutJoinColumns = $property->getSourceEntity() === $property->getTargetEntity() && ! $joinTable->hasColumns();
771
772 97
            if (! $joinTable->getJoinColumns()) {
773 16
                $referencedColumnName = $this->namingStrategy->referenceColumnName();
774 16
                $sourceReferenceName  = $selfReferencingEntityWithoutJoinColumns ? 'source' : $referencedColumnName;
775 16
                $columnName           = $this->namingStrategy->joinKeyColumnName($property->getSourceEntity(), $sourceReferenceName);
776 16
                $joinColumn           = new JoinColumnMetadata();
777
778 16
                $joinColumn->setColumnName($columnName);
779 16
                $joinColumn->setReferencedColumnName($referencedColumnName);
780 16
                $joinColumn->setOnDelete('CASCADE');
781
782 16
                $joinTable->addJoinColumn($joinColumn);
783
            }
784
785 97
            if (! $joinTable->getInverseJoinColumns()) {
786 16
                $referencedColumnName = $this->namingStrategy->referenceColumnName();
787 16
                $targetReferenceName  = $selfReferencingEntityWithoutJoinColumns ? 'target' : $referencedColumnName;
788 16
                $columnName           = $this->namingStrategy->joinKeyColumnName($property->getTargetEntity(), $targetReferenceName);
789 16
                $joinColumn           = new JoinColumnMetadata();
790
791 16
                $joinColumn->setColumnName($columnName);
792 16
                $joinColumn->setReferencedColumnName($referencedColumnName);
793 16
                $joinColumn->setOnDelete('CASCADE');
794
795 16
                $joinTable->addInverseJoinColumn($joinColumn);
796
            }
797
798 97
            foreach ($joinTable->getJoinColumns() as $joinColumn) {
799
                /** @var JoinColumnMetadata $joinColumn */
800 97
                if (! $joinColumn->getReferencedColumnName()) {
801 2
                    $joinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName());
802
                }
803
804 97
                $referencedColumnName = $joinColumn->getReferencedColumnName();
805
806 97
                if (! $joinColumn->getColumnName()) {
807 2
                    $columnName = $this->namingStrategy->joinKeyColumnName(
808 2
                        $property->getSourceEntity(),
809 2
                        $referencedColumnName
810
                    );
811
812 97
                    $joinColumn->setColumnName($columnName);
813
                }
814
            }
815
816 97
            foreach ($joinTable->getInverseJoinColumns() as $inverseJoinColumn) {
817
                /** @var JoinColumnMetadata $inverseJoinColumn */
818 97
                if (! $inverseJoinColumn->getReferencedColumnName()) {
819 2
                    $inverseJoinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName());
820
                }
821
822 97
                $referencedColumnName = $inverseJoinColumn->getReferencedColumnName();
823
824 97
                if (! $inverseJoinColumn->getColumnName()) {
825 2
                    $columnName = $this->namingStrategy->joinKeyColumnName(
826 2
                        $property->getTargetEntity(),
827 2
                        $referencedColumnName
828
                    );
829
830 97
                    $inverseJoinColumn->setColumnName($columnName);
831
                }
832
            }
833
        }
834 109
    }
835
836
    /**
837
     * {@inheritDoc}
838
     */
839 400
    public function getIdentifierFieldNames()
840
    {
841 400
        return $this->identifier;
842
    }
843
844
    /**
845
     * Gets the name of the single id field. Note that this only works on
846
     * entity classes that have a single-field pk.
847
     *
848
     * @return string
849
     *
850
     * @throws MappingException If the class has a composite primary key.
851
     */
852 152
    public function getSingleIdentifierFieldName()
853
    {
854 152
        if ($this->isIdentifierComposite()) {
855 1
            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->className);
856
        }
857
858 151
        if (! isset($this->identifier[0])) {
859 1
            throw MappingException::noIdDefined($this->className);
860
        }
861
862 150
        return $this->identifier[0];
863
    }
864
865
    /**
866
     * INTERNAL:
867
     * Sets the mapped identifier/primary key fields of this class.
868
     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
869
     *
870
     * @param mixed[] $identifier
871
     */
872 99
    public function setIdentifier(array $identifier)
873
    {
874 99
        $this->identifier = $identifier;
875 99
    }
876
877
    /**
878
     * {@inheritDoc}
879
     */
880 1051
    public function getIdentifier()
881
    {
882 1051
        return $this->identifier;
883
    }
884
885
    /**
886
     * {@inheritDoc}
887
     */
888 188
    public function hasField($fieldName)
889
    {
890 188
        return isset($this->declaredProperties[$fieldName])
891 188
            && $this->declaredProperties[$fieldName] instanceof FieldMetadata;
892
    }
893
894
    /**
895
     * Returns an array with identifier column names and their corresponding ColumnMetadata.
896
     *
897
     * @return ColumnMetadata[]
898
     */
899 441
    public function getIdentifierColumns(EntityManagerInterface $em) : array
900
    {
901 441
        $columns = [];
902
903 441
        foreach ($this->identifier as $idProperty) {
904 441
            $property = $this->getProperty($idProperty);
905
906 441
            if ($property instanceof FieldMetadata) {
907 435
                $columns[$property->getColumnName()] = $property;
908
909 435
                continue;
910
            }
911
912
            /** @var AssociationMetadata $property */
913
914
            // Association defined as Id field
915 25
            $targetClass = $em->getClassMetadata($property->getTargetEntity());
916
917 25
            if (! $property->isOwningSide()) {
918
                $property    = $targetClass->getProperty($property->getMappedBy());
919
                $targetClass = $em->getClassMetadata($property->getTargetEntity());
920
            }
921
922 25
            $joinColumns = $property instanceof ManyToManyAssociationMetadata
923
                ? $property->getJoinTable()->getInverseJoinColumns()
924 25
                : $property->getJoinColumns();
925
926 25
            foreach ($joinColumns as $joinColumn) {
927
                /** @var JoinColumnMetadata $joinColumn */
928 25
                $columnName           = $joinColumn->getColumnName();
929 25
                $referencedColumnName = $joinColumn->getReferencedColumnName();
930
931 25
                if (! $joinColumn->getType()) {
932 13
                    $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, $targetClass, $em));
0 ignored issues
show
$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

932
                    $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, /** @scrutinizer ignore-type */ $targetClass, $em));
Loading history...
933
                }
934
935 25
                $columns[$columnName] = $joinColumn;
936
            }
937
        }
938
939 441
        return $columns;
940
    }
941
942
    /**
943
     * Gets the name of the primary table.
944
     */
945 1594
    public function getTableName() : ?string
946
    {
947 1594
        return $this->table->getName();
948
    }
949
950
    /**
951
     * Gets primary table's schema name.
952
     */
953 14
    public function getSchemaName() : ?string
954
    {
955 14
        return $this->table->getSchema();
956
    }
957
958
    /**
959
     * Gets the table name to use for temporary identifier tables of this class.
960
     */
961 7
    public function getTemporaryIdTableName() : string
962
    {
963 7
        $schema = $this->getSchemaName() === null
964 6
            ? ''
965 7
            : $this->getSchemaName() . '_';
966
967
        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
968 7
        return $schema . $this->getTableName() . '_id_tmp';
969
    }
970
971
    /**
972
     * Sets the mapped subclasses of this class.
973
     *
974
     * @param string[] $subclasses The names of all mapped subclasses.
975
     *
976
     * @todo guilhermeblanco Only used for ClassMetadataTest. Remove if possible!
977
     */
978 4
    public function setSubclasses(array $subclasses) : void
979
    {
980 4
        foreach ($subclasses as $subclass) {
981 3
            $this->subClasses[] = $subclass;
982
        }
983 4
    }
984
985
    /**
986
     * @return string[]
987
     */
988 1080
    public function getSubClasses() : array
989
    {
990 1080
        return $this->subClasses;
991
    }
992
993
    /**
994
     * Sets the inheritance type used by the class and its subclasses.
995
     *
996
     * @param int $type
997
     *
998
     * @throws MappingException
999
     */
1000 120
    public function setInheritanceType($type) : void
1001
    {
1002 120
        if (! $this->isInheritanceType($type)) {
1003
            throw MappingException::invalidInheritanceType($this->className, $type);
1004
        }
1005
1006 120
        $this->inheritanceType = $type;
1007 120
    }
1008
1009
    /**
1010
     * Sets the override property mapping for an entity relationship.
1011
     *
1012
     * @throws RuntimeException
1013
     * @throws MappingException
1014
     * @throws CacheException
1015
     */
1016 12
    public function setPropertyOverride(Property $property) : void
1017
    {
1018 12
        $fieldName = $property->getName();
1019
1020 12
        if (! isset($this->declaredProperties[$fieldName])) {
1021 2
            throw MappingException::invalidOverrideFieldName($this->className, $fieldName);
1022
        }
1023
1024 10
        $originalProperty          = $this->getProperty($fieldName);
1025 10
        $originalPropertyClassName = get_class($originalProperty);
1026
1027
        // If moving from transient to persistent, assume it's a new property
1028 10
        if ($originalPropertyClassName === TransientMetadata::class) {
1029 1
            unset($this->declaredProperties[$fieldName]);
1030
1031 1
            $this->addProperty($property);
1032
1033 1
            return;
1034
        }
1035
1036
        // Do not allow to change property type
1037 9
        if ($originalPropertyClassName !== get_class($property)) {
1038
            throw MappingException::invalidOverridePropertyType($this->className, $fieldName);
1039
        }
1040
1041
        // Do not allow to change version property
1042 9
        if ($originalProperty instanceof VersionFieldMetadata) {
1043
            throw MappingException::invalidOverrideVersionField($this->className, $fieldName);
1044
        }
1045
1046 9
        unset($this->declaredProperties[$fieldName]);
1047
1048 9
        if ($property instanceof FieldMetadata) {
1049
            // Unset defined fieldName prior to override
1050 5
            unset($this->fieldNames[$originalProperty->getColumnName()]);
1051
1052
            // Revert what should not be allowed to change
1053 5
            $property->setDeclaringClass($originalProperty->getDeclaringClass());
1054 5
            $property->setPrimaryKey($originalProperty->isPrimaryKey());
1055 9
        } elseif ($property instanceof AssociationMetadata) {
1056
            // Unset all defined fieldNames prior to override
1057 9
            if ($originalProperty instanceof ToOneAssociationMetadata && $originalProperty->isOwningSide()) {
1058 5
                foreach ($originalProperty->getJoinColumns() as $joinColumn) {
1059 5
                    unset($this->fieldNames[$joinColumn->getColumnName()]);
1060
                }
1061
            }
1062
1063
            // Override what it should be allowed to change
1064 9
            if ($property->getInversedBy()) {
1065 2
                $originalProperty->setInversedBy($property->getInversedBy());
1066
            }
1067
1068 9
            if ($property->getFetchMode() !== $originalProperty->getFetchMode()) {
1069 2
                $originalProperty->setFetchMode($property->getFetchMode());
1070
            }
1071
1072 9
            if ($originalProperty instanceof ToOneAssociationMetadata && $property->getJoinColumns()) {
1073 5
                $originalProperty->setJoinColumns($property->getJoinColumns());
1074 8
            } elseif ($originalProperty instanceof ManyToManyAssociationMetadata && $property->getJoinTable()) {
1075 4
                $originalProperty->setJoinTable($property->getJoinTable());
1076
            }
1077
1078 9
            $property = $originalProperty;
1079
        }
1080
1081 9
        $this->addProperty($property);
1082 9
    }
1083
1084
    /**
1085
     * Checks if this entity is the root in any entity-inheritance-hierarchy.
1086
     *
1087
     * @return bool
1088
     */
1089 336
    public function isRootEntity()
1090
    {
1091 336
        return $this->className === $this->getRootClassName();
1092
    }
1093
1094
    /**
1095
     * Checks whether a mapped field is inherited from a superclass.
1096
     *
1097
     * @param string $fieldName
1098
     *
1099
     * @return bool TRUE if the field is inherited, FALSE otherwise.
1100
     */
1101 622
    public function isInheritedProperty($fieldName)
1102
    {
1103 622
        $declaringClass = $this->declaredProperties[$fieldName]->getDeclaringClass();
1104
1105 622
        return $declaringClass->className !== $this->className;
1106
    }
1107
1108
    /**
1109
     * {@inheritdoc}
1110
     */
1111 447
    public function setTable(TableMetadata $table) : void
1112
    {
1113 447
        $this->table = $table;
1114
1115 447
        if (empty($table->getName())) {
1116
            $table->setName($this->namingStrategy->classToTableName($this->className));
1117
        }
1118 447
    }
1119
1120
    /**
1121
     * Checks whether the given type identifies an inheritance type.
1122
     *
1123
     * @param int $type
1124
     *
1125
     * @return bool TRUE if the given type identifies an inheritance type, FALSe otherwise.
1126
     */
1127 120
    private function isInheritanceType($type)
1128
    {
1129 120
        return $type === InheritanceType::NONE
1130 94
            || $type === InheritanceType::SINGLE_TABLE
1131 52
            || $type === InheritanceType::JOINED
1132 120
            || $type === InheritanceType::TABLE_PER_CLASS;
1133
    }
1134
1135 915
    public function getColumn(string $columnName) : ?LocalColumnMetadata
1136
    {
1137 915
        foreach ($this->declaredProperties as $property) {
1138 915
            if ($property instanceof LocalColumnMetadata && $property->getColumnName() === $columnName) {
1139 915
                return $property;
1140
            }
1141
        }
1142
1143
        return null;
1144
    }
1145
1146
    /**
1147
     * Add a property mapping.
1148
     *
1149
     * @throws RuntimeException
1150
     * @throws MappingException
1151
     * @throws CacheException
1152
     */
1153 437
    public function addProperty(Property $property)
1154
    {
1155 437
        $fieldName = $property->getName();
1156
1157
        // Check for empty field name
1158 437
        if (empty($fieldName)) {
1159 1
            throw MappingException::missingFieldName($this->className);
1160
        }
1161
1162 436
        $property->setDeclaringClass($this);
1163
1164
        switch (true) {
1165 436
            case $property instanceof VersionFieldMetadata:
1166 21
                $this->validateAndCompleteFieldMapping($property);
1167 21
                $this->validateAndCompleteVersionFieldMapping($property);
1168 20
                break;
1169
1170 435
            case $property instanceof FieldMetadata:
1171 412
                $this->validateAndCompleteFieldMapping($property);
1172 411
                break;
1173
1174 291
            case $property instanceof OneToOneAssociationMetadata:
1175 131
                $this->validateAndCompleteAssociationMapping($property);
1176 130
                $this->validateAndCompleteToOneAssociationMetadata($property);
1177 129
                $this->validateAndCompleteOneToOneMapping($property);
1178 129
                break;
1179
1180 227
            case $property instanceof OneToManyAssociationMetadata:
1181 116
                $this->validateAndCompleteAssociationMapping($property);
1182 116
                $this->validateAndCompleteToManyAssociationMetadata($property);
1183 116
                $this->validateAndCompleteOneToManyMapping($property);
1184 116
                break;
1185
1186 223
            case $property instanceof ManyToOneAssociationMetadata:
1187 151
                $this->validateAndCompleteAssociationMapping($property);
1188 148
                $this->validateAndCompleteToOneAssociationMetadata($property);
1189 148
                $this->validateAndCompleteManyToOneMapping($property);
1190 148
                break;
1191
1192 126
            case $property instanceof ManyToManyAssociationMetadata:
1193 110
                $this->validateAndCompleteAssociationMapping($property);
1194 109
                $this->validateAndCompleteToManyAssociationMetadata($property);
1195 109
                $this->validateAndCompleteManyToManyMapping($property);
1196 109
                break;
1197
1198
            default:
1199
                // Transient properties are ignored on purpose here! =)
1200 30
                break;
1201
        }
1202
1203 428
        $this->addDeclaredProperty($property);
1204 428
    }
1205
1206
    /**
1207
     * INTERNAL:
1208
     * Adds a property mapping without completing/validating it.
1209
     * This is mainly used to add inherited property mappings to derived classes.
1210
     */
1211 97
    public function addInheritedProperty(Property $property)
1212
    {
1213 97
        $inheritedProperty = clone $property;
1214 97
        $declaringClass    = $property->getDeclaringClass();
1215
1216 97
        if ($inheritedProperty instanceof FieldMetadata) {
1217 96
            if (! $declaringClass->isMappedSuperclass) {
1218 74
                $inheritedProperty->setTableName($property->getTableName());
1219
            }
1220
1221 96
            $this->fieldNames[$property->getColumnName()] = $property->getName();
1222 43
        } elseif ($inheritedProperty instanceof AssociationMetadata) {
1223 42
            if ($declaringClass->isMappedSuperclass) {
1224 10
                $inheritedProperty->setSourceEntity($this->className);
1225
            }
1226
1227
            // Need to add inherited fieldNames
1228 42
            if ($inheritedProperty instanceof ToOneAssociationMetadata && $inheritedProperty->isOwningSide()) {
1229 35
                foreach ($inheritedProperty->getJoinColumns() as $joinColumn) {
1230
                    /** @var JoinColumnMetadata $joinColumn */
1231 34
                    $this->fieldNames[$joinColumn->getColumnName()] = $property->getName();
1232
                }
1233
            }
1234
        }
1235
1236 97
        if (isset($this->declaredProperties[$property->getName()])) {
1237 1
            throw MappingException::duplicateProperty($this->className, $this->getProperty($property->getName()));
1238
        }
1239
1240 97
        $this->declaredProperties[$property->getName()] = $inheritedProperty;
1241
1242 97
        if ($inheritedProperty instanceof VersionFieldMetadata) {
1243 4
            $this->versionProperty = $inheritedProperty;
1244
        }
1245 97
    }
1246
1247
    /**
1248
     * Registers a custom repository class for the entity class.
1249
     *
1250
     * @param string|null $repositoryClassName The class name of the custom mapper.
1251
     */
1252 30
    public function setCustomRepositoryClassName(?string $repositoryClassName)
1253
    {
1254 30
        $this->customRepositoryClassName = $repositoryClassName;
1255 30
    }
1256
1257 163
    public function getCustomRepositoryClassName() : ?string
1258
    {
1259 163
        return $this->customRepositoryClassName;
1260
    }
1261
1262
    /**
1263
     * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
1264
     *
1265
     * @param string $lifecycleEvent
1266
     *
1267
     * @return bool
1268
     */
1269
    public function hasLifecycleCallbacks($lifecycleEvent)
1270
    {
1271
        return isset($this->lifecycleCallbacks[$lifecycleEvent]);
1272
    }
1273
1274
    /**
1275
     * Gets the registered lifecycle callbacks for an event.
1276
     *
1277
     * @param string $event
1278
     *
1279
     * @return string[]
1280
     */
1281
    public function getLifecycleCallbacks($event)
1282
    {
1283
        return $this->lifecycleCallbacks[$event] ?? [];
1284
    }
1285
1286
    /**
1287
     * Adds a lifecycle callback for entities of this class.
1288
     *
1289
     * @param string $callback
1290
     * @param string $event
1291
     */
1292 17
    public function addLifecycleCallback($callback, $event)
1293
    {
1294 17
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event], true)) {
1295 3
            return;
1296
        }
1297
1298 17
        $this->lifecycleCallbacks[$event][] = $callback;
1299 17
    }
1300
1301
    /**
1302
     * Sets the lifecycle callbacks for entities of this class.
1303
     * Any previously registered callbacks are overwritten.
1304
     *
1305
     * @param string[][] $callbacks
1306
     */
1307 97
    public function setLifecycleCallbacks(array $callbacks) : void
1308
    {
1309 97
        $this->lifecycleCallbacks = $callbacks;
1310 97
    }
1311
1312
    /**
1313
     * Adds a entity listener for entities of this class.
1314
     *
1315
     * @param string $eventName The entity lifecycle event.
1316
     * @param string $class     The listener class.
1317
     * @param string $method    The listener callback method.
1318
     *
1319
     * @throws MappingException
1320
     */
1321 17
    public function addEntityListener(string $eventName, string $class, string $method) : void
1322
    {
1323
        $listener = [
1324 17
            'class'  => $class,
1325 17
            'method' => $method,
1326
        ];
1327
1328 17
        if (! class_exists($class)) {
1329 1
            throw MappingException::entityListenerClassNotFound($class, $this->className);
1330
        }
1331
1332 16
        if (! method_exists($class, $method)) {
1333 1
            throw MappingException::entityListenerMethodNotFound($class, $method, $this->className);
1334
        }
1335
1336 15
        if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName], true)) {
1337 1
            throw MappingException::duplicateEntityListener($class, $method, $this->className);
1338
        }
1339
1340 15
        $this->entityListeners[$eventName][] = $listener;
1341 15
    }
1342
1343
    /**
1344
     * Sets the discriminator column definition.
1345
     *
1346
     * @see getDiscriminatorColumn()
1347
     *
1348
     * @throws MappingException
1349
     */
1350 96
    public function setDiscriminatorColumn(DiscriminatorColumnMetadata $discriminatorColumn) : void
1351
    {
1352 96
        if (isset($this->fieldNames[$discriminatorColumn->getColumnName()])) {
1353 1
            throw MappingException::duplicateColumnName($this->className, $discriminatorColumn->getColumnName());
1354
        }
1355
1356 95
        $discriminatorColumn->setTableName($discriminatorColumn->getTableName() ?? $this->getTableName());
1357
1358 95
        $allowedTypeList = ['boolean', 'array', 'object', 'datetime', 'time', 'date'];
1359
1360 95
        if (in_array($discriminatorColumn->getTypeName(), $allowedTypeList, true)) {
1361
            throw MappingException::invalidDiscriminatorColumnType($discriminatorColumn->getTypeName());
1362
        }
1363
1364 95
        $this->discriminatorColumn = $discriminatorColumn;
1365 95
    }
1366
1367
    /**
1368
     * Sets the discriminator values used by this class.
1369
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
1370
     *
1371
     * @param string[] $map
1372
     *
1373
     * @throws MappingException
1374
     */
1375 90
    public function setDiscriminatorMap(array $map) : void
1376
    {
1377 90
        foreach ($map as $value => $className) {
1378 90
            $this->addDiscriminatorMapClass($value, $className);
1379
        }
1380 90
    }
1381
1382
    /**
1383
     * Adds one entry of the discriminator map with a new class and corresponding name.
1384
     *
1385
     * @param string|int $name
1386
     *
1387
     * @throws MappingException
1388
     */
1389 90
    public function addDiscriminatorMapClass($name, string $className) : void
1390
    {
1391 90
        $this->discriminatorMap[$name] = $className;
1392
1393 90
        if ($this->className === $className) {
1394 76
            $this->discriminatorValue = $name;
1395
1396 76
            return;
1397
        }
1398
1399 89
        if (! (class_exists($className) || interface_exists($className))) {
1400
            throw MappingException::invalidClassInDiscriminatorMap($className, $this->className);
1401
        }
1402
1403 89
        if (is_subclass_of($className, $this->className) && ! in_array($className, $this->subClasses, true)) {
1404 84
            $this->subClasses[] = $className;
1405
        }
1406 89
    }
1407
1408 1030
    public function getValueGenerationPlan() : ValueGenerationPlan
1409
    {
1410 1030
        return $this->valueGenerationPlan;
1411
    }
1412
1413 369
    public function setValueGenerationPlan(ValueGenerationPlan $valueGenerationPlan) : void
1414
    {
1415 369
        $this->valueGenerationPlan = $valueGenerationPlan;
1416 369
    }
1417
1418
    /**
1419
     * Marks this class as read only, no change tracking is applied to it.
1420
     */
1421 2
    public function asReadOnly() : void
1422
    {
1423 2
        $this->readOnly = true;
1424 2
    }
1425
1426
    /**
1427
     * Whether this class is read only or not.
1428
     */
1429 446
    public function isReadOnly() : bool
1430
    {
1431 446
        return $this->readOnly;
1432
    }
1433
1434 1090
    public function isVersioned() : bool
1435
    {
1436 1090
        return $this->versionProperty !== null;
1437
    }
1438
1439
    /**
1440
     * Map Embedded Class
1441
     *
1442
     * @param mixed[] $mapping
1443
     *
1444
     * @throws MappingException
1445
     */
1446
    public function mapEmbedded(array $mapping) : void
1447
    {
1448
        /*if (isset($this->declaredProperties[$mapping['fieldName']])) {
1449
            throw MappingException::duplicateProperty($this->className, $this->getProperty($mapping['fieldName']));
1450
        }
1451
1452
        $this->embeddedClasses[$mapping['fieldName']] = [
1453
            'class'          => $this->fullyQualifiedClassName($mapping['class']),
1454
            'columnPrefix'   => $mapping['columnPrefix'],
1455
            'declaredField'  => $mapping['declaredField'] ?? null,
1456
            'originalField'  => $mapping['originalField'] ?? null,
1457
            'declaringClass' => $this,
1458
        ];*/
1459
    }
1460
1461
    /**
1462
     * Inline the embeddable class
1463
     *
1464
     * @param string $property
1465
     */
1466
    public function inlineEmbeddable($property, ClassMetadata $embeddable) : void
1467
    {
1468
        /*foreach ($embeddable->fieldMappings as $fieldName => $fieldMapping) {
1469
            $fieldMapping['fieldName']     = $property . "." . $fieldName;
1470
            $fieldMapping['originalClass'] = $fieldMapping['originalClass'] ?? $embeddable->getClassName();
1471
            $fieldMapping['originalField'] = $fieldMapping['originalField'] ?? $fieldName;
1472
            $fieldMapping['declaredField'] = isset($fieldMapping['declaredField'])
1473
                ? $property . '.' . $fieldMapping['declaredField']
1474
                : $property;
1475
1476
            if (! empty($this->embeddedClasses[$property]['columnPrefix'])) {
1477
                $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName'];
1478
            } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) {
1479
                $fieldMapping['columnName'] = $this->namingStrategy->embeddedFieldToColumnName(
1480
                    $property,
1481
                    $fieldMapping['columnName'],
1482
                    $this->reflectionClass->getName(),
1483
                    $embeddable->reflectionClass->getName()
1484
                );
1485
            }
1486
1487
            $this->mapField($fieldMapping);
1488
        }*/
1489
    }
1490
}
1491