Failed Conditions
Pull Request — master (#6743)
by Grégoire
18:17 queued 12:33
created

ClassMetadata::getIdentifierFieldNames()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
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 Doctrine\ORM\Cache\CacheException;
8
use Doctrine\ORM\EntityManagerInterface;
9
use Doctrine\ORM\Mapping\Factory\NamingStrategy;
10
use Doctrine\ORM\Cache\NonCacheableEntityAssociation;
11
use Doctrine\ORM\Reflection\ReflectionService;
12
use Doctrine\ORM\Sequencing\Planning\ValueGenerationPlan;
13
use Doctrine\ORM\Utility\PersisterHelper;
14
15
/**
16
 * A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
17
 * of an entity and its associations.
18
 *
19
 */
20
class ClassMetadata extends ComponentMetadata implements TableOwner
21
{
22
    /**
23
     * The name of the custom repository class used for the entity class.
24
     * (Optional).
25
     *
26
     * @var string
27
     */
28
    protected $customRepositoryClassName;
29
30
    /**
31
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
32
     *
33
     * @var bool
34
     */
35
    public $isMappedSuperclass = false;
36
37
    /**
38
     * READ-ONLY: Whether this class describes the mapping of an embeddable class.
39
     *
40
     * @var bool
41
     */
42
    public $isEmbeddedClass = false;
43
44
    /**
45
     * Whether this class describes the mapping of a read-only class.
46
     * That means it is never considered for change-tracking in the UnitOfWork.
47
     * It is a very helpful performance optimization for entities that are immutable,
48
     * either in your domain or through the relation database (coming from a view,
49
     * or a history table for example).
50
     *
51
     * @var bool
52
     */
53
    private $readOnly = false;
54
55
    /**
56
     * The names of all subclasses (descendants).
57
     *
58
     * @var string[]
59
     */
60
    protected $subClasses = [];
61
62
    /**
63
     * READ-ONLY: The names of all embedded classes based on properties.
64
     *
65
     * @var string[]
66
     */
67
    //public $embeddedClasses = [];
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
68
69
    /**
70
     * The named queries allowed to be called directly from Repository.
71
     *
72
     * @var string[]
73
     */
74
    protected $namedQueries = [];
75
76
    /**
77
     * READ-ONLY: The named native queries allowed to be called directly from Repository.
78
     *
79
     * A native SQL named query definition has the following structure:
80
     * <pre>
81
     * array(
82
     *     'name'               => <query name>,
83
     *     'query'              => <sql query>,
84
     *     'resultClass'        => <class of the result>,
85
     *     'resultSetMapping'   => <name of a SqlResultSetMapping>
86
     * )
87
     * </pre>
88
     *
89
     * @var string[][]
90
     */
91
    public $namedNativeQueries = [];
92
93
    /**
94
     * READ-ONLY: The mappings of the results of native SQL queries.
95
     *
96
     * A native result mapping definition has the following structure:
97
     * <pre>
98
     * array(
99
     *     'name'               => <result name>,
100
     *     'entities'           => array(<entity result mapping>),
101
     *     'columns'            => array(<column result mapping>)
102
     * )
103
     * </pre>
104
     *
105
     * @var mixed[][]
106
     */
107
    public $sqlResultSetMappings = [];
108
109
    /**
110
     * READ-ONLY: The registered lifecycle callbacks for entities of this class.
111
     *
112
     * @var string[][]
113
     */
114
    public $lifecycleCallbacks = [];
115
116
    /**
117
     * READ-ONLY: The registered entity listeners.
118
     *
119
     * @var mixed[][]
120
     */
121
    public $entityListeners = [];
122
123
    /**
124
     * READ-ONLY: The field names of all fields that are part of the identifier/primary key
125
     * of the mapped entity class.
126
     *
127
     * @var string[]
128
     */
129
    public $identifier = [];
130
131
    /**
132
     * READ-ONLY: The inheritance mapping type used by the class.
133
     *
134
     * @var string
135
     */
136
    public $inheritanceType = InheritanceType::NONE;
137
138
    /**
139
     * READ-ONLY: The policy used for change-tracking on entities of this class.
140
     *
141
     * @var string
142
     */
143
    public $changeTrackingPolicy = ChangeTrackingPolicy::DEFERRED_IMPLICIT;
144
145
    /**
146
     * READ-ONLY: The discriminator value of this class.
147
     *
148
     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
149
     * where a discriminator column is used.</b>
150
     *
151
     * @var mixed
152
     *
153
     * @see discriminatorColumn
154
     */
155
    public $discriminatorValue;
156
157
    /**
158
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
159
     *
160
     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
161
     * where a discriminator column is used.</b>
162
     *
163
     * @var string[]
164
     *
165
     * @see discriminatorColumn
166
     */
167
    public $discriminatorMap = [];
168
169
    /**
170
     * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
171
     * inheritance mappings.
172
     *
173
     * @var DiscriminatorColumnMetadata
174
     */
175
    public $discriminatorColumn;
176
177
    /**
178
     * READ-ONLY: The primary table metadata.
179
     *
180
     * @var TableMetadata
181
     */
182
    public $table;
183
184
    /**
185
     * READ-ONLY: An array of field names. Used to look up field names from column names.
186
     * Keys are column names and values are field names.
187
     *
188
     * @var string[]
189
     */
190
    public $fieldNames = [];
191
192
    /**
193
     * READ-ONLY: The field which is used for versioning in optimistic locking (if any).
194
     *
195
     * @var FieldMetadata|null
196
     */
197
    public $versionProperty;
198
199
    /**
200
     * NamingStrategy determining the default column and table names.
201
     *
202
     * @var NamingStrategy
203
     */
204
    protected $namingStrategy;
205
206
    /**
207
     * Value generation plan is responsible for generating values for auto-generated fields.
208
     *
209
     * @var ValueGenerationPlan
210
     */
211
    protected $valueGenerationPlan;
212
213
    /**
214
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
215
     * metadata of the class with the given name.
216
     *
217
     * @param string $entityName The name of the entity class.
218 428
     */
219
    public function __construct(
220
        string $entityName,
221
        ClassMetadataBuildingContext $metadataBuildingContext
222 428
    ) {
223
        parent::__construct($entityName, $metadataBuildingContext);
224 428
225 428
        $this->namingStrategy = $metadataBuildingContext->getNamingStrategy();
226
    }
227 2
228
    public function setClassName(string $className)
229 2
    {
230 2
        $this->className = $className;
231
    }
232
233
    public function getColumnsIterator() : \ArrayIterator
234
    {
235
        $iterator = parent::getColumnsIterator();
236
237
        if ($this->discriminatorColumn) {
238
            $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

238
            $iterator->offsetSet($this->discriminatorColumn->getColumnName(), /** @scrutinizer ignore-type */ $this->discriminatorColumn);
Loading history...
239
        }
240
241
        return $iterator;
242
    }
243 11
244
    public function getAncestorsIterator() : \ArrayIterator
245 11
    {
246 11
        $ancestors = new \ArrayIterator();
247
        $parent    = $this;
248 11
249 8
        while (($parent = $parent->parent) !== null) {
250 1
            if ($parent instanceof ClassMetadata && $parent->isMappedSuperclass) {
251
                continue;
252
            }
253 7
254
            $ancestors->append($parent);
255
        }
256 11
257
        return $ancestors;
258
    }
259 1165
260
    public function getRootClassName() : string
261 1165
    {
262 382
        return ($this->parent instanceof ClassMetadata && ! $this->parent->isMappedSuperclass)
263 1165
            ? $this->parent->getRootClassName()
264
            : $this->className
265
        ;
266
    }
267
268
    /**
269
     * Handles metadata cloning nicely.
270 13
     */
271
    public function __clone()
272 13
    {
273 12
        if ($this->cache) {
274
            $this->cache = clone $this->cache;
275
        }
276 13
277 13
        foreach ($this->declaredProperties as $name => $property) {
278
            $this->declaredProperties[$name] = clone $property;
279 13
        }
280
    }
281
282
    /**
283
     * Creates a string representation of this instance.
284
     *
285
     * @return string The string representation of this instance.
286
     *
287
     * @todo Construct meaningful string representation.
288
     */
289
    public function __toString()
290
    {
291
        return __CLASS__ . '@' . spl_object_id($this);
292
    }
293
294
    /**
295
     * Determines which fields get serialized.
296
     *
297
     * It is only serialized what is necessary for best unserialization performance.
298
     * That means any metadata properties that are not set or empty or simply have
299
     * their default value are NOT serialized.
300
     *
301
     * Parts that are also NOT serialized because they can not be properly unserialized:
302
     * - reflectionClass
303
     *
304
     * @return string[] The names of all the fields that should be serialized.
305 5
     */
306
    public function __sleep()
307 5
    {
308
        $serialized = [];
309
310 5
        // This metadata is always serialized/cached.
311 5
        $serialized = array_merge($serialized, [
312
            'declaredProperties',
313
            'fieldNames',
314
            //'embeddedClasses',
315
            'identifier',
316
            'className',
317
            'parent',
318
            'table',
319
            'valueGenerationPlan',
320
        ]);
321
322 5
        // The rest of the metadata is only serialized if necessary.
323
        if ($this->changeTrackingPolicy !== ChangeTrackingPolicy::DEFERRED_IMPLICIT) {
324
            $serialized[] = 'changeTrackingPolicy';
325
        }
326 5
327 1
        if ($this->customRepositoryClassName) {
328
            $serialized[] = 'customRepositoryClassName';
329
        }
330 5
331 1
        if ($this->inheritanceType !== InheritanceType::NONE) {
332 1
            $serialized[] = 'inheritanceType';
333 1
            $serialized[] = 'discriminatorColumn';
334 1
            $serialized[] = 'discriminatorValue';
335 1
            $serialized[] = 'discriminatorMap';
336
            $serialized[] = 'subClasses';
337
        }
338 5
339
        if ($this->isMappedSuperclass) {
340
            $serialized[] = 'isMappedSuperclass';
341
        }
342 5
343
        if ($this->isEmbeddedClass) {
344
            $serialized[] = 'isEmbeddedClass';
345
        }
346 5
347
        if ($this->isVersioned()) {
348
            $serialized[] = 'versionProperty';
349
        }
350 5
351
        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...
352
            $serialized[] = 'lifecycleCallbacks';
353
        }
354 5
355 1
        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...
356
            $serialized[] = 'entityListeners';
357
        }
358 5
359 1
        if ($this->namedQueries) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->namedQueries 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...
360
            $serialized[] = 'namedQueries';
361
        }
362 5
363
        if ($this->namedNativeQueries) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->namedNativeQueries 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[] = 'namedNativeQueries';
365
        }
366 5
367
        if ($this->sqlResultSetMappings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->sqlResultSetMappings 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
            $serialized[] = 'sqlResultSetMappings';
369
        }
370 5
371
        if ($this->cache) {
372
            $serialized[] = 'cache';
373
        }
374 5
375 1
        if ($this->readOnly) {
376
            $serialized[] = 'readOnly';
377
        }
378 5
379
        return $serialized;
380
    }
381
382
    /**
383
     * Restores some state that can not be serialized/unserialized.
384 1495
     */
385
    public function wakeupReflection(ReflectionService $reflectionService) : void
386
    {
387 1495
        // Restore ReflectionClass and properties
388
        $this->reflectionClass = $reflectionService->getClass($this->className);
389 1495
390
        if (! $this->reflectionClass) {
391
            return;
392
        }
393 1495
394
        $this->className = $this->reflectionClass->getName();
395 1495
396
        foreach ($this->declaredProperties as $property) {
397 1494
            /** @var Property $property */
398
            $property->wakeupReflection($reflectionService);
399 1495
        }
400
    }
401
402
    /**
403
     * Validates Identifier.
404
     *
405
     * @throws MappingException
406 351
     */
407
    public function validateIdentifier() : void
408 351
    {
409 25
        if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
410
            return;
411
        }
412
413 351
        // Verify & complete identifier mapping
414
        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...
415
            throw MappingException::identifierRequired($this->className);
416
        }
417 351
418 351
        $explicitlyGeneratedProperties = array_filter($this->declaredProperties, function (Property $property) : bool {
419 351
            return $property instanceof FieldMetadata
420 351
                && $property->isPrimaryKey()
421 351
                && $property->hasValueGenerator();
422
        });
423 351
424
        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...
introduced by
The condition $explicitlyGeneratedProp...isIdentifierComposite() can never be true.
Loading history...
425
            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->className);
426 351
        }
427
    }
428
429
    /**
430
     * Validates association targets actually exist.
431
     *
432
     * @throws MappingException
433 351
     */
434
    public function validateAssociations() : void
435 351
    {
436 351
        array_map(
437 351
            function (Property $property) {
438 349
                if (! ($property instanceof AssociationMetadata)) {
439
                    return;
440
                }
441 242
442
                $targetEntity = $property->getTargetEntity();
443 242
444
                if (! class_exists($targetEntity)) {
445
                    throw MappingException::invalidTargetEntityClass($targetEntity, $this->className, $property->getName());
446 351
                }
447 351
            },
448
            $this->declaredProperties
449 351
        );
450
    }
451
452
    /**
453
     * Validates lifecycle callbacks.
454
     *
455
     * @throws MappingException
456 351
     */
457
    public function validateLifecycleCallbacks(ReflectionService $reflectionService) : void
458 351
    {
459
        foreach ($this->lifecycleCallbacks as $callbacks) {
460 9
            /** @var array $callbacks */
461 9
            foreach ($callbacks as $callbackFuncName) {
462 9
                if (! $reflectionService->hasPublicMethod($this->className, $callbackFuncName)) {
463
                    throw MappingException::lifecycleCallbackMethodNotFound($this->className, $callbackFuncName);
464
                }
465
            }
466 351
        }
467
    }
468
469
    /**
470
     * Sets the change tracking policy used by this class.
471 99
     */
472
    public function setChangeTrackingPolicy(string $policy) : void
473 99
    {
474 99
        $this->changeTrackingPolicy = $policy;
475
    }
476
477
    /**
478
     * Checks whether a field is part of the identifier/primary key field(s).
479
     *
480
     * @param string $fieldName The field name.
481
     *
482
     * @return bool TRUE if the field is part of the table identifier/primary key field(s), FALSE otherwise.
483 949
     */
484
    public function isIdentifier(string $fieldName) : bool
485 949
    {
486 1
        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...
487
            return false;
488
        }
489 948
490 944
        if (! $this->isIdentifierComposite()) {
491
            return $fieldName === $this->identifier[0];
492
        }
493 88
494
        return in_array($fieldName, $this->identifier, true);
495
    }
496 1120
497
    public function isIdentifierComposite() : bool
498 1120
    {
499
        return isset($this->identifier[1]);
500
    }
501
502
    /**
503
     * Gets the named query.
504
     *
505
     * @see ClassMetadata::$namedQueries
506
     *
507
     * @param string $queryName The query name.
508
     *
509
     * @throws MappingException
510 3
     */
511
    public function getNamedQuery($queryName) : string
512 3
    {
513
        if (! isset($this->namedQueries[$queryName])) {
514
            throw MappingException::queryNotFound($this->className, $queryName);
515
        }
516 3
517
        return $this->namedQueries[$queryName];
518
    }
519
520
    /**
521
     * Gets all named queries of the class.
522
     *
523
     * @return string[]
524 98
     */
525
    public function getNamedQueries() : array
526 98
    {
527
        return $this->namedQueries;
528
    }
529
530
    /**
531
     * Gets the named native query.
532
     *
533
     * @see ClassMetadata::$namedNativeQueries
534
     *
535
     * @param string $queryName The query name.
536
     *
537
     * @return string[]
538
     *
539
     * @throws MappingException
540 15
     */
541
    public function getNamedNativeQuery($queryName) : array
542 15
    {
543
        if (! isset($this->namedNativeQueries[$queryName])) {
544
            throw MappingException::queryNotFound($this->className, $queryName);
545
        }
546 15
547
        return $this->namedNativeQueries[$queryName];
548
    }
549
550
    /**
551
     * Gets all named native queries of the class.
552
     *
553
     * @return string[]
554 3
     */
555
    public function getNamedNativeQueries() : array
556 3
    {
557
        return $this->namedNativeQueries;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->namedNativeQueries returns the type array<mixed,string[]> which is incompatible with the documented return type string[].
Loading history...
558
    }
559
560
    /**
561
     * Gets the result set mapping.
562
     *
563
     * @see ClassMetadata::$sqlResultSetMappings
564
     *
565
     * @param string $name The result set mapping name.
566
     *
567
     * @return mixed[]
568
     *
569
     * @throws MappingException
570 13
     */
571
    public function getSqlResultSetMapping($name)
572 13
    {
573
        if (! isset($this->sqlResultSetMappings[$name])) {
574
            throw MappingException::resultMappingNotFound($this->className, $name);
575
        }
576 13
577
        return $this->sqlResultSetMappings[$name];
578
    }
579
580
    /**
581
     * Gets all sql result set mappings of the class.
582
     *
583
     * @return mixed[][]
584 5
     */
585
    public function getSqlResultSetMappings()
586 5
    {
587
        return $this->sqlResultSetMappings;
588
    }
589
590
    /**
591
     * Validates & completes the basic mapping information for field mapping.
592
     *
593
     * @throws MappingException If something is wrong with the mapping.
594 385
     */
595
    protected function validateAndCompleteFieldMapping(FieldMetadata $property)
596 385
    {
597 385
        $fieldName  = $property->getName();
598
        $columnName = $property->getColumnName();
599 385
600 330
        if (empty($columnName)) {
601
            $columnName = $this->namingStrategy->propertyToColumnName($fieldName, $this->className);
602 330
603
            $property->setColumnName($columnName);
604
        }
605 385
606 380
        if (! $this->isMappedSuperclass) {
607
            $property->setTableName($this->getTableName());
608
        }
609
610 385
        // Check for already declared column
611 385
        if (isset($this->fieldNames[$columnName]) ||
612
            ($this->discriminatorColumn !== null && $this->discriminatorColumn->getColumnName() === $columnName)) {
613
            throw MappingException::duplicateColumnName($this->className, $columnName);
614
        }
615
616 385
        // Complete id mapping
617 378
        if ($property->isPrimaryKey()) {
618
            if ($this->versionProperty !== null && $this->versionProperty->getName() === $fieldName) {
619
                throw MappingException::cannotVersionIdField($this->className, $fieldName);
620
            }
621 378
622
            if ($property->getType()->canRequireSQLConversion()) {
623
                throw MappingException::sqlConversionNotAllowedForPrimaryKeyProperties($property);
0 ignored issues
show
Bug introduced by
$property of type Doctrine\ORM\Mapping\FieldMetadata is incompatible with the type string expected by parameter $className of Doctrine\ORM\Mapping\Map...rPrimaryKeyProperties(). ( Ignorable by Annotation )

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

623
                throw MappingException::sqlConversionNotAllowedForPrimaryKeyProperties(/** @scrutinizer ignore-type */ $property);
Loading history...
Bug introduced by
The call to Doctrine\ORM\Mapping\Map...rPrimaryKeyProperties() has too few arguments starting with property. ( Ignorable by Annotation )

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

623
                throw MappingException::/** @scrutinizer ignore-call */ sqlConversionNotAllowedForPrimaryKeyProperties($property);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
624
            }
625 378
626 378
            if (! in_array($fieldName, $this->identifier, true)) {
627
                $this->identifier[] = $fieldName;
628
            }
629
        }
630 385
631 385
        $this->fieldNames[$columnName] = $fieldName;
632
    }
633
634
    /**
635
     * Validates & completes the basic mapping information for field mapping.
636
     *
637
     * @throws MappingException If something is wrong with the mapping.
638 20
     */
639
    protected function validateAndCompleteVersionFieldMapping(VersionFieldMetadata $property)
640 20
    {
641
        $this->versionProperty = $property;
642 20
643
        $options = $property->getOptions();
644 20
645
        if (isset($options['default'])) {
646
            return;
647
        }
648 20
649 20
        if (in_array($property->getTypeName(), ['integer', 'bigint', 'smallint'], true)) {
650
            $property->setOptions(array_merge($options, ['default' => 1]));
651 20
652
            return;
653
        }
654 1
655 1
        if ($property->getTypeName() === 'datetime') {
656
            $property->setOptions(array_merge($options, ['default' => 'CURRENT_TIMESTAMP']));
657 1
658
            return;
659
        }
660
661
        throw MappingException::unsupportedOptimisticLockingType($property->getType());
662
    }
663
664
    /**
665
     * Validates & completes the basic mapping information that is common to all
666
     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
667
     *
668
     * @throws MappingException If something is wrong with the mapping.
669
     * @throws CacheException   If entity is not cacheable.
670 265
     */
671
    protected function validateAndCompleteAssociationMapping(AssociationMetadata $property)
672 265
    {
673 265
        $fieldName    = $property->getName();
674
        $targetEntity = $property->getTargetEntity();
675 265
676
        if (! $targetEntity) {
677
            throw MappingException::missingTargetEntity($fieldName);
678
        }
679 265
680 265
        $property->setSourceEntity($this->className);
681 265
        $property->setOwningSide($property->getMappedBy() === null);
682
        $property->setTargetEntity($targetEntity);
683
684 265
        // Complete id mapping
685 39
        if ($property->isPrimaryKey()) {
686
            if ($property->isOrphanRemoval()) {
687
                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->className, $fieldName);
688
            }
689 39
690 39
            if (! in_array($property->getName(), $this->identifier, true)) {
691
                if ($property instanceof ToOneAssociationMetadata && count($property->getJoinColumns()) >= 2) {
692
                    throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
693
                        $property->getTargetEntity(),
694
                        $this->className,
695
                        $fieldName
696
                    );
697
                }
698 39
699
                $this->identifier[] = $property->getName();
700
            }
701 39
702
            if ($this->cache && ! $property->getCache()) {
703
                throw NonCacheableEntityAssociation::fromEntityAndField($this->className, $fieldName);
704
            }
705 39
706
            if ($property instanceof ToManyAssociationMetadata) {
707
                throw MappingException::illegalToManyIdentifierAssociation($this->className, $property->getName());
708
            }
709
        }
710
711 265
        // Cascades
712 265
        $cascadeTypes = ['remove', 'persist', 'refresh'];
713
        $cascades     = array_map('strtolower', $property->getCascade());
714 265
715 4
        if (in_array('all', $cascades, true)) {
716
            $cascades = $cascadeTypes;
717
        }
718 265
719
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
720
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
721
722
            throw MappingException::invalidCascadeOption($diffCascades, $this->className, $fieldName);
723
        }
724 265
725 265
        $property->setCascade($cascades);
726
    }
727
728
    /**
729
     * Validates & completes a to-one association mapping.
730
     *
731
     * @param ToOneAssociationMetadata $property The association mapping to validate & complete.
732
     *
733
     * @throws \RuntimeException
734
     * @throws MappingException
735 231
     */
736
    protected function validateAndCompleteToOneAssociationMetadata(ToOneAssociationMetadata $property)
737 231
    {
738
        $fieldName = $property->getName();
739 231
740 164
        if ($property->getJoinColumns()) {
741
            $property->setOwningSide(true);
742
        }
743 231
744 229
        if ($property->isOwningSide()) {
745
            if (empty($property->getJoinColumns())) {
746 77
                // Apply default join column
747
                $property->addJoinColumn(new JoinColumnMetadata());
748
            }
749 229
750
            $uniqueConstraintColumns = [];
751 229
752
            foreach ($property->getJoinColumns() as $joinColumn) {
753 229
                /** @var JoinColumnMetadata $joinColumn */
754 112
                if ($property instanceof OneToOneAssociationMetadata && $this->inheritanceType !== InheritanceType::SINGLE_TABLE) {
755 110
                    if (count($property->getJoinColumns()) === 1) {
756 110
                        if (! $property->isPrimaryKey()) {
757
                            $joinColumn->setUnique(true);
758
                        }
759 2
                    } else {
760
                        $uniqueConstraintColumns[] = $joinColumn->getColumnName();
761
                    }
762
                }
763 229
764
                $joinColumn->setTableName(! $this->isMappedSuperclass ? $this->getTableName() : null);
765 229
766 96
                if (! $joinColumn->getColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $joinColumn->getColumnName() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
767
                    $joinColumn->setColumnName($this->namingStrategy->joinColumnName($fieldName, $this->className));
768
                }
769 229
770 77
                if (! $joinColumn->getReferencedColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $joinColumn->getReferencedColumnName() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
771
                    $joinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName());
772
                }
773 229
774
                $this->fieldNames[$joinColumn->getColumnName()] = $fieldName;
775
            }
776 229
777 2
            if ($uniqueConstraintColumns) {
0 ignored issues
show
introduced by
The condition $uniqueConstraintColumns can never be true.
Loading history...
778
                if (! $this->table) {
779
                    throw new \RuntimeException(
780
                        'ClassMetadata::setTable() has to be called before defining a one to one relationship.'
781
                    );
782
                }
783 2
784
                $this->table->addUniqueConstraint(
785 2
                    [
786 2
                        'name'    => sprintf('%s_uniq', $fieldName),
787
                        'columns' => $uniqueConstraintColumns,
788
                        'options' => [],
789
                        'flags'   => [],
790
                    ]
791
                );
792
            }
793
        }
794 231
795 9
        if ($property->isOrphanRemoval()) {
796
            $cascades = $property->getCascade();
797 9
798 8
            if (! in_array('remove', $cascades, true)) {
799
                $cascades[] = 'remove';
800 8
801
                $property->setCascade($cascades);
802
            }
803
804
            // @todo guilhermeblanco where is this used?
805
            // @todo guilhermeblanco Shouldn￿'t we iterate through JoinColumns to set non-uniqueness?
806
            //$property->setUnique(false);
0 ignored issues
show
Unused Code Comprehensibility introduced by
86% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
807
        }
808 231
809
        if ($property->isPrimaryKey() && ! $property->isOwningSide()) {
810
            throw MappingException::illegalInverseIdentifierAssociation($this->className, $fieldName);
811 231
        }
812
    }
813
814
    /**
815
     * Validates & completes a to-many association mapping.
816
     *
817
     * @param ToManyAssociationMetadata $property The association mapping to validate & complete.
818
     *
819
     * @throws MappingException
820 164
     */
821
    protected function validateAndCompleteToManyAssociationMetadata(ToManyAssociationMetadata $property)
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

821
    protected function validateAndCompleteToManyAssociationMetadata(/** @scrutinizer ignore-unused */ ToManyAssociationMetadata $property)

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...
822
    {
823 164
        // Do nothing
824
    }
825
826
    /**
827
     * Validates & completes a one-to-one association mapping.
828
     *
829
     * @param OneToOneAssociationMetadata $property The association mapping to validate & complete.
830 119
     */
831
    protected function validateAndCompleteOneToOneMapping(OneToOneAssociationMetadata $property)
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

831
    protected function validateAndCompleteOneToOneMapping(/** @scrutinizer ignore-unused */ OneToOneAssociationMetadata $property)

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...
832
    {
833 119
        // Do nothing
834
    }
835
836
    /**
837
     * Validates & completes a many-to-one association mapping.
838
     *
839
     * @param ManyToOneAssociationMetadata $property The association mapping to validate & complete.
840
     *
841
     * @throws MappingException
842 135
     */
843
    protected function validateAndCompleteManyToOneMapping(ManyToOneAssociationMetadata $property)
844
    {
845 135
        // A many-to-one mapping is essentially a one-one backreference
846
        if ($property->isOrphanRemoval()) {
847
            throw MappingException::illegalOrphanRemoval($this->className, $property->getName());
848 135
        }
849
    }
850
851
    /**
852
     * Validates & completes a one-to-many association mapping.
853
     *
854
     * @param OneToManyAssociationMetadata $property The association mapping to validate & complete.
855
     *
856
     * @throws MappingException
857 112
     */
858
    protected function validateAndCompleteOneToManyMapping(OneToManyAssociationMetadata $property)
859
    {
860 112
        // OneToMany MUST be inverse side
861
        $property->setOwningSide(false);
862
863 112
        // OneToMany MUST have mappedBy
864
        if (! $property->getMappedBy()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $property->getMappedBy() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
865
            throw MappingException::oneToManyRequiresMappedBy($property->getName());
866
        }
867 112
868 23
        if ($property->isOrphanRemoval()) {
869
            $cascades = $property->getCascade();
870 23
871 20
            if (! in_array('remove', $cascades, true)) {
872
                $cascades[] = 'remove';
873 20
874
                $property->setCascade($cascades);
875
            }
876 112
        }
877
    }
878
879
    /**
880
     * Validates & completes a many-to-many association mapping.
881
     *
882
     * @param ManyToManyAssociationMetadata $property The association mapping to validate & complete.
883
     *
884
     * @throws MappingException
885 103
     */
886
    protected function validateAndCompleteManyToManyMapping(ManyToManyAssociationMetadata $property)
887 103
    {
888
        if ($property->isOwningSide()) {
889 92
            // owning side MUST have a join table
890
            $joinTable = $property->getJoinTable() ?: new JoinTableMetadata();
891 92
892
            $property->setJoinTable($joinTable);
893 92
894 18
            if (! $joinTable->getName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $joinTable->getName() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
895 18
                $joinTableName = $this->namingStrategy->joinTableName(
896 18
                    $property->getSourceEntity(),
897 18
                    $property->getTargetEntity(),
898
                    $property->getName()
899
                );
900 18
901
                $joinTable->setName($joinTableName);
902
            }
903 92
904
            $selfReferencingEntityWithoutJoinColumns = $property->getSourceEntity() === $property->getTargetEntity() && ! $joinTable->hasColumns();
905 92
906 16
            if (! $joinTable->getJoinColumns()) {
907 16
                $referencedColumnName = $this->namingStrategy->referenceColumnName();
908 16
                $sourceReferenceName  = $selfReferencingEntityWithoutJoinColumns ? 'source' : $referencedColumnName;
909 16
                $columnName           = $this->namingStrategy->joinKeyColumnName($property->getSourceEntity(), $sourceReferenceName);
910
                $joinColumn           = new JoinColumnMetadata();
911 16
912 16
                $joinColumn->setColumnName($columnName);
913 16
                $joinColumn->setReferencedColumnName($referencedColumnName);
914
                $joinColumn->setOnDelete('CASCADE');
915 16
916
                $joinTable->addJoinColumn($joinColumn);
917
            }
918 92
919 16
            if (! $joinTable->getInverseJoinColumns()) {
920 16
                $referencedColumnName = $this->namingStrategy->referenceColumnName();
921 16
                $targetReferenceName  = $selfReferencingEntityWithoutJoinColumns ? 'target' : $referencedColumnName;
922 16
                $columnName           = $this->namingStrategy->joinKeyColumnName($property->getTargetEntity(), $targetReferenceName);
923
                $joinColumn           = new JoinColumnMetadata();
924 16
925 16
                $joinColumn->setColumnName($columnName);
926 16
                $joinColumn->setReferencedColumnName($referencedColumnName);
927
                $joinColumn->setOnDelete('CASCADE');
928 16
929
                $joinTable->addInverseJoinColumn($joinColumn);
930
            }
931 92
932
            foreach ($joinTable->getJoinColumns() as $joinColumn) {
933 92
                /** @var JoinColumnMetadata $joinColumn */
934 2
                if (! $joinColumn->getReferencedColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $joinColumn->getReferencedColumnName() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
935
                    $joinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName());
936
                }
937 92
938
                $referencedColumnName = $joinColumn->getReferencedColumnName();
939 92
940 2
                if (! $joinColumn->getColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $joinColumn->getColumnName() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
941 2
                    $columnName = $this->namingStrategy->joinKeyColumnName(
942 2
                        $property->getSourceEntity(),
943
                        $referencedColumnName
944
                    );
945 92
946
                    $joinColumn->setColumnName($columnName);
947
                }
948
            }
949 92
950
            foreach ($joinTable->getInverseJoinColumns() as $inverseJoinColumn) {
951 92
                /** @var JoinColumnMetadata $inverseJoinColumn */
952 2
                if (! $inverseJoinColumn->getReferencedColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $inverseJoinColumn->getReferencedColumnName() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
953
                    $inverseJoinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName());
954
                }
955 92
956
                $referencedColumnName = $inverseJoinColumn->getReferencedColumnName();
957 92
958 2
                if (! $inverseJoinColumn->getColumnName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $inverseJoinColumn->getColumnName() of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
959 2
                    $columnName = $this->namingStrategy->joinKeyColumnName(
960 2
                        $property->getTargetEntity(),
961
                        $referencedColumnName
962
                    );
963 92
964
                    $inverseJoinColumn->setColumnName($columnName);
965
                }
966
            }
967 103
        }
968
    }
969
970
    /**
971
     * {@inheritDoc}
972 326
     */
973
    public function getIdentifierFieldNames()
974 326
    {
975
        return $this->identifier;
976
    }
977
978
    /**
979
     * Gets the name of the single id field. Note that this only works on
980
     * entity classes that have a single-field pk.
981
     *
982
     * @return string
983
     *
984
     * @throws MappingException If the class has a composite primary key.
985 142
     */
986
    public function getSingleIdentifierFieldName()
987 142
    {
988
        if ($this->isIdentifierComposite()) {
989
            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->className);
990
        }
991 142
992
        if (! isset($this->identifier[0])) {
993
            throw MappingException::noIdDefined($this->className);
994
        }
995 142
996
        return $this->identifier[0];
997
    }
998
999
    /**
1000
     * INTERNAL:
1001
     * Sets the mapped identifier/primary key fields of this class.
1002
     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1003
     *
1004
     * @param mixed[] $identifier
1005 95
     */
1006
    public function setIdentifier(array $identifier)
1007 95
    {
1008 95
        $this->identifier = $identifier;
1009
    }
1010
1011
    /**
1012
     * {@inheritDoc}
1013 972
     */
1014
    public function getIdentifier()
1015 972
    {
1016
        return $this->identifier;
1017
    }
1018
1019
    /**
1020
     * {@inheritDoc}
1021 134
     */
1022
    public function hasField($fieldName)
1023 134
    {
1024 134
        return isset($this->declaredProperties[$fieldName])
1025
            && $this->declaredProperties[$fieldName] instanceof FieldMetadata;
1026
    }
1027
1028
    /**
1029
     * Returns an array with identifier column names and their corresponding ColumnMetadata.
1030
     *
1031
     * @return ColumnMetadata[]
1032 419
     */
1033
    public function getIdentifierColumns(EntityManagerInterface $em) : array
1034 419
    {
1035
        $columns = [];
1036 419
1037 419
        foreach ($this->identifier as $idProperty) {
1038
            $property = $this->getProperty($idProperty);
1039 419
1040 414
            if ($property instanceof FieldMetadata) {
1041
                $columns[$property->getColumnName()] = $property;
1042 414
1043
                continue;
1044
            }
1045
1046
            /** @var AssociationMetadata $property */
1047
1048 24
            // Association defined as Id field
1049
            $targetClass = $em->getClassMetadata($property->getTargetEntity());
1050 24
1051
            if (! $property->isOwningSide()) {
1052
                $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

1052
                /** @scrutinizer ignore-call */ 
1053
                $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...
1053
                $targetClass = $em->getClassMetadata($property->getTargetEntity());
1054
            }
1055 24
1056
            $joinColumns = $property instanceof ManyToManyAssociationMetadata
1057 24
                ? $property->getJoinTable()->getInverseJoinColumns()
1058
                : $property->getJoinColumns()
1059
            ;
1060 24
1061
            foreach ($joinColumns as $joinColumn) {
1062 24
                /** @var JoinColumnMetadata $joinColumn */
1063 24
                $columnName           = $joinColumn->getColumnName();
1064
                $referencedColumnName = $joinColumn->getReferencedColumnName();
1065 24
1066 12
                if (! $joinColumn->getType()) {
1067
                    $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

1067
                    $joinColumn->setType(PersisterHelper::getTypeOfColumn($referencedColumnName, /** @scrutinizer ignore-type */ $targetClass, $em));
Loading history...
1068
                }
1069 24
1070
                $columns[$columnName] = $joinColumn;
1071
            }
1072
        }
1073 419
1074
        return $columns;
1075
    }
1076
1077
    /**
1078
     * Gets the name of the primary table.
1079 1463
     */
1080
    public function getTableName() : ?string
1081 1463
    {
1082
        return $this->table->getName();
1083
    }
1084
1085
    /**
1086
     * Gets primary table's schema name.
1087 14
     */
1088
    public function getSchemaName() : ?string
1089 14
    {
1090
        return $this->table->getSchema();
1091
    }
1092
1093
    /**
1094
     * Gets the table name to use for temporary identifier tables of this class.
1095 7
     */
1096
    public function getTemporaryIdTableName() : string
1097 7
    {
1098 6
        $schema = $this->getSchemaName() === null
1099 7
            ? ''
1100
            : $this->getSchemaName() . '_'
1101
        ;
1102
1103 7
        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
1104
        return $schema . $this->getTableName() . '_id_tmp';
1105
    }
1106
1107
    /**
1108
     * Sets the mapped subclasses of this class.
1109
     *
1110
     * @todo guilhermeblanco Only used for ClassMetadataTest. Remove if possible!
1111
     *
1112
     * @param string[] $subclasses The names of all mapped subclasses.
1113 4
     */
1114
    public function setSubclasses(array $subclasses) : void
1115 4
    {
1116 3
        foreach ($subclasses as $subclass) {
1117
            $this->subClasses[] = $subclass;
1118 4
        }
1119
    }
1120
1121
    /**
1122
     * @return string[]
1123 999
     */
1124
    public function getSubClasses() : array
1125 999
    {
1126
        return $this->subClasses;
1127
    }
1128
1129
    /**
1130
     * Sets the inheritance type used by the class and its subclasses.
1131
     *
1132
     * @param int $type
1133
     *
1134
     * @throws MappingException
1135 114
     */
1136
    public function setInheritanceType($type) : void
1137 114
    {
1138
        if (! $this->isInheritanceType($type)) {
1139
            throw MappingException::invalidInheritanceType($this->className, $type);
1140
        }
1141 114
1142 114
        $this->inheritanceType = $type;
1143
    }
1144
1145
    /**
1146
     * Sets the override property mapping for an entity relationship.
1147
     *
1148
     * @throws \RuntimeException
1149
     * @throws MappingException
1150
     * @throws CacheException
1151 10
     */
1152
    public function setPropertyOverride(Property $property) : void
1153 10
    {
1154
        $fieldName = $property->getName();
1155 10
1156
        if (! isset($this->declaredProperties[$fieldName])) {
1157
            throw MappingException::invalidOverrideFieldName($this->className, $fieldName);
1158
        }
1159 10
1160 10
        $originalProperty          = $this->getProperty($fieldName);
1161
        $originalPropertyClassName = get_class($originalProperty);
1162
1163 10
        // If moving from transient to persistent, assume it's a new property
1164 1
        if ($originalPropertyClassName === TransientMetadata::class) {
1165
            unset($this->declaredProperties[$fieldName]);
1166 1
1167
            $this->addProperty($property);
1168 1
1169
            return;
1170
        }
1171
1172 9
        // Do not allow to change property type
1173
        if ($originalPropertyClassName !== get_class($property)) {
1174
            throw MappingException::invalidOverridePropertyType($this->className, $fieldName);
1175
        }
1176
1177 9
        // Do not allow to change version property
1178
        if ($originalProperty instanceof VersionFieldMetadata) {
1179
            throw MappingException::invalidOverrideVersionField($this->className, $fieldName);
1180
        }
1181 9
1182
        unset($this->declaredProperties[$fieldName]);
1183 9
1184
        if ($property instanceof FieldMetadata) {
1185 5
            // Unset defined fieldName prior to override
1186
            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

1186
            unset($this->fieldNames[$originalProperty->/** @scrutinizer ignore-call */ getColumnName()]);
Loading history...
1187
1188 5
            // Revert what should not be allowed to change
1189 5
            $property->setDeclaringClass($originalProperty->getDeclaringClass());
1190 9
            $property->setPrimaryKey($originalProperty->isPrimaryKey());
1191
        } elseif ($property instanceof AssociationMetadata) {
1192 9
            // Unset all defined fieldNames prior to override
1193 5
            if ($originalProperty instanceof ToOneAssociationMetadata && $originalProperty->isOwningSide()) {
1194 5
                foreach ($originalProperty->getJoinColumns() as $joinColumn) {
1195
                    unset($this->fieldNames[$joinColumn->getColumnName()]);
1196
                }
1197
            }
1198
1199 9
            // Override what it should be allowed to change
1200 2
            if ($property->getInversedBy()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $property->getInversedBy() of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1201
                $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

1201
                $originalProperty->/** @scrutinizer ignore-call */ 
1202
                                   setInversedBy($property->getInversedBy());
Loading history...
1202
            }
1203 9
1204 2
            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

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

1205
                $originalProperty->/** @scrutinizer ignore-call */ 
1206
                                   setFetchMode($property->getFetchMode());
Loading history...
1206
            }
1207 9
1208 5
            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

1208
            if ($originalProperty instanceof ToOneAssociationMetadata && $property->/** @scrutinizer ignore-call */ getJoinColumns()) {
Loading history...
1209 8
                $originalProperty->setJoinColumns($property->getJoinColumns());
1210 4
            } 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

1210
            } elseif ($originalProperty instanceof ManyToManyAssociationMetadata && $property->/** @scrutinizer ignore-call */ getJoinTable()) {
Loading history...
1211
                $originalProperty->setJoinTable($property->getJoinTable());
1212
            }
1213 9
1214
            $property = $originalProperty;
1215
        }
1216 9
1217 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

1217
        $this->addProperty(/** @scrutinizer ignore-type */ $property);
Loading history...
1218
    }
1219
1220
    /**
1221
     * Checks if this entity is the root in any entity-inheritance-hierarchy.
1222
     *
1223
     * @return bool
1224 317
     */
1225
    public function isRootEntity()
1226 317
    {
1227
        return $this->className === $this->getRootClassName();
1228
    }
1229
1230
    /**
1231
     * Checks whether a mapped field is inherited from a superclass.
1232
     *
1233
     * @param string $fieldName
1234
     *
1235
     * @return bool TRUE if the field is inherited, FALSE otherwise.
1236 547
     */
1237
    public function isInheritedProperty($fieldName)
1238 547
    {
1239
        $declaringClass = $this->declaredProperties[$fieldName]->getDeclaringClass();
1240 547
1241
        return ! ($declaringClass->className === $this->className);
1242
    }
1243
1244
    /**
1245
     * {@inheritdoc}
1246 412
     */
1247
    public function setTable(TableMetadata $table) : void
1248 412
    {
1249
        $this->table = $table;
1250 412
1251
        if (empty($table->getName())) {
1252
            $table->setName($this->namingStrategy->classToTableName($this->className));
1253 412
        }
1254
    }
1255
1256
    /**
1257
     * Checks whether the given type identifies an inheritance type.
1258
     *
1259
     * @param int $type
1260
     *
1261
     * @return bool TRUE if the given type identifies an inheritance type, FALSe otherwise.
1262 114
     */
1263
    private function isInheritanceType($type)
1264 114
    {
1265 90
        return $type === InheritanceType::NONE
1266 51
            || $type === InheritanceType::SINGLE_TABLE
1267 114
            || $type === InheritanceType::JOINED
1268
            || $type === InheritanceType::TABLE_PER_CLASS;
1269
    }
1270 842
1271
    public function getColumn(string $columnName) : ?LocalColumnMetadata
1272 842
    {
1273 842
        foreach ($this->declaredProperties as $property) {
1274 842
            if ($property instanceof LocalColumnMetadata && $property->getColumnName() === $columnName) {
1275
                return $property;
1276
            }
1277
        }
1278
1279
        return null;
1280
    }
1281
1282
    /**
1283
     * Add a property mapping.
1284
     *
1285
     * @throws \RuntimeException
1286
     * @throws MappingException
1287
     * @throws CacheException
1288 398
     */
1289
    public function addProperty(Property $property)
1290 398
    {
1291
        $fieldName = $property->getName();
1292
1293 398
        // Check for empty field name
1294
        if (empty($fieldName)) {
1295
            throw MappingException::missingFieldName($this->className);
1296
        }
1297 398
1298
        $property->setDeclaringClass($this);
1299
1300 398
        switch (true) {
1301 20
            case ($property instanceof VersionFieldMetadata):
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
1302 20
                $this->validateAndCompleteFieldMapping($property);
1303 20
                $this->validateAndCompleteVersionFieldMapping($property);
1304
                break;
1305 398
1306 385
            case ($property instanceof FieldMetadata):
1307 385
                $this->validateAndCompleteFieldMapping($property);
1308
                break;
1309 268
1310 119
            case ($property instanceof OneToOneAssociationMetadata):
1311 119
                $this->validateAndCompleteAssociationMapping($property);
1312 119
                $this->validateAndCompleteToOneAssociationMetadata($property);
1313 119
                $this->validateAndCompleteOneToOneMapping($property);
1314
                break;
1315 212
1316 112
            case ($property instanceof OneToManyAssociationMetadata):
1317 112
                $this->validateAndCompleteAssociationMapping($property);
1318 112
                $this->validateAndCompleteToManyAssociationMetadata($property);
1319 112
                $this->validateAndCompleteOneToManyMapping($property);
1320
                break;
1321 208
1322 135
            case ($property instanceof ManyToOneAssociationMetadata):
1323 135
                $this->validateAndCompleteAssociationMapping($property);
1324 135
                $this->validateAndCompleteToOneAssociationMetadata($property);
1325 135
                $this->validateAndCompleteManyToOneMapping($property);
1326
                break;
1327 120
1328 103
            case ($property instanceof ManyToManyAssociationMetadata):
1329 103
                $this->validateAndCompleteAssociationMapping($property);
1330 103
                $this->validateAndCompleteToManyAssociationMetadata($property);
1331 103
                $this->validateAndCompleteManyToManyMapping($property);
1332
                break;
1333
1334
            default:
1335 32
                // Transient properties are ignored on purpose here! =)
1336
                break;
1337
        }
1338 398
1339 398
        $this->addDeclaredProperty($property);
1340
    }
1341
1342
    /**
1343
     * INTERNAL:
1344
     * Adds a property mapping without completing/validating it.
1345
     * This is mainly used to add inherited property mappings to derived classes.
1346 94
     */
1347
    public function addInheritedProperty(Property $property)
1348 94
    {
1349 94
        $inheritedProperty = clone $property;
1350
        $declaringClass    = $property->getDeclaringClass();
1351 94
1352 94
        if ($inheritedProperty instanceof FieldMetadata) {
1353 73
            if (! $declaringClass->isMappedSuperclass) {
1354
                $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

1354
                $inheritedProperty->setTableName($property->/** @scrutinizer ignore-call */ getTableName());
Loading history...
1355
            }
1356 94
1357 41
            $this->fieldNames[$property->getColumnName()] = $property->getName();
1358 40
        } elseif ($inheritedProperty instanceof AssociationMetadata) {
1359 10
            if ($declaringClass->isMappedSuperclass) {
1360
                $inheritedProperty->setSourceEntity($this->className);
1361
            }
1362
1363 40
            // Need to add inherited fieldNames
1364 33
            if ($inheritedProperty instanceof ToOneAssociationMetadata && $inheritedProperty->isOwningSide()) {
1365
                foreach ($inheritedProperty->getJoinColumns() as $joinColumn) {
1366 33
                    /** @var JoinColumnMetadata $joinColumn */
1367
                    $this->fieldNames[$joinColumn->getColumnName()] = $property->getName();
1368
                }
1369
            }
1370
        }
1371 94
1372
        if (isset($this->declaredProperties[$property->getName()])) {
1373
            throw MappingException::duplicateProperty($this->className, $this->getProperty($property->getName()));
1374
        }
1375 94
1376
        $this->declaredProperties[$property->getName()] = $inheritedProperty;
1377 94
1378 4
        if ($inheritedProperty instanceof VersionFieldMetadata) {
1379
            $this->versionProperty = $inheritedProperty;
1380 94
        }
1381
    }
1382
1383
    /**
1384
     * INTERNAL:
1385
     * Adds a named query to this class.
1386
     *
1387
     * @throws MappingException
1388 19
     */
1389
    public function addNamedQuery(string $name, string $query)
1390 19
    {
1391
        if (isset($this->namedQueries[$name])) {
1392
            throw MappingException::duplicateQueryMapping($this->className, $name);
1393
        }
1394 19
1395 19
        $this->namedQueries[$name] = $query;
1396
    }
1397
1398
    /**
1399
     * INTERNAL:
1400
     * Adds a named native query to this class.
1401
     *
1402
     * @param mixed[] $queryMapping
1403
     *
1404
     * @throws MappingException
1405 23
     */
1406
    public function addNamedNativeQuery(string $name, string $query, array $queryMapping)
1407 23
    {
1408
        if (isset($this->namedNativeQueries[$name])) {
1409
            throw MappingException::duplicateQueryMapping($this->className, $name);
1410
        }
1411 23
1412
        if (! isset($queryMapping['resultClass']) && ! isset($queryMapping['resultSetMapping'])) {
1413
            throw MappingException::missingQueryMapping($this->className, $name);
1414
        }
1415 23
1416 23
        $this->namedNativeQueries[$name] = array_merge(['query' => $query], $queryMapping);
1417
    }
1418
1419
    /**
1420
     * INTERNAL:
1421
     * Adds a sql result set mapping to this class.
1422
     *
1423
     * @param mixed[] $resultMapping
1424
     *
1425
     * @throws MappingException
1426 22
     */
1427
    public function addSqlResultSetMapping(array $resultMapping)
1428 22
    {
1429
        if (! isset($resultMapping['name'])) {
1430
            throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->className);
1431
        }
1432 22
1433
        if (isset($this->sqlResultSetMappings[$resultMapping['name']])) {
1434
            throw MappingException::duplicateResultSetMapping($this->className, $resultMapping['name']);
1435
        }
1436 22
1437 22
        if (isset($resultMapping['entities'])) {
1438 22
            foreach ($resultMapping['entities'] as $key => $entityResult) {
1439
                if (! isset($entityResult['entityClass'])) {
1440
                    throw MappingException::missingResultSetMappingEntity($this->className, $resultMapping['name']);
1441
                }
1442 22
1443 22
                $entityClassName                                = $entityResult['entityClass'];
1444
                $resultMapping['entities'][$key]['entityClass'] = $entityClassName;
1445 22
1446 19
                if (isset($entityResult['fields'])) {
1447 19
                    foreach ($entityResult['fields'] as $k => $field) {
1448
                        if (! isset($field['name'])) {
1449
                            throw MappingException::missingResultSetMappingFieldName($this->className, $resultMapping['name']);
1450
                        }
1451 19
1452 11
                        if (! isset($field['column'])) {
1453
                            $fieldName = $field['name'];
1454 11
1455 6
                            if (strpos($fieldName, '.')) {
1456
                                list(, $fieldName) = explode('.', $fieldName);
1457
                            }
1458 22
1459
                            $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName;
1460
                        }
1461
                    }
1462
                }
1463
            }
1464
        }
1465 22
1466 22
        $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping;
1467
    }
1468
1469
    /**
1470
     * Registers a custom repository class for the entity class.
1471
     *
1472
     * @param string|null $repositoryClassName The class name of the custom mapper.
1473 28
     */
1474
    public function setCustomRepositoryClassName(?string $repositoryClassName)
1475 28
    {
1476 28
        $this->customRepositoryClassName = $repositoryClassName;
1477
    }
1478 155
1479
    public function getCustomRepositoryClassName() : ?string
1480 155
    {
1481
        return $this->customRepositoryClassName;
1482
    }
1483
1484
    /**
1485
     * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
1486
     *
1487
     * @param string $lifecycleEvent
1488
     *
1489
     * @return bool
1490
     */
1491
    public function hasLifecycleCallbacks($lifecycleEvent)
1492
    {
1493
        return isset($this->lifecycleCallbacks[$lifecycleEvent]);
1494
    }
1495
1496
    /**
1497
     * Gets the registered lifecycle callbacks for an event.
1498
     *
1499
     * @param string $event
1500
     *
1501
     * @return string[]
1502
     */
1503
    public function getLifecycleCallbacks($event)
1504
    {
1505
        return $this->lifecycleCallbacks[$event] ?? [];
1506
    }
1507
1508
    /**
1509
     * Adds a lifecycle callback for entities of this class.
1510
     *
1511
     * @param string $callback
1512
     * @param string $event
1513 17
     */
1514
    public function addLifecycleCallback($callback, $event)
1515 17
    {
1516 3
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event], true)) {
1517
            return;
1518
        }
1519 17
1520 17
        $this->lifecycleCallbacks[$event][] = $callback;
1521
    }
1522
1523
    /**
1524
     * Sets the lifecycle callbacks for entities of this class.
1525
     * Any previously registered callbacks are overwritten.
1526
     *
1527
     * @param string[][] $callbacks
1528 94
     */
1529
    public function setLifecycleCallbacks(array $callbacks) : void
1530 94
    {
1531 94
        $this->lifecycleCallbacks = $callbacks;
1532
    }
1533
1534
    /**
1535
     * Adds a entity listener for entities of this class.
1536
     *
1537
     * @param string $eventName The entity lifecycle event.
1538
     * @param string $class     The listener class.
1539
     * @param string $method    The listener callback method.
1540
     *
1541
     * @throws \Doctrine\ORM\Mapping\MappingException
1542 15
     */
1543
    public function addEntityListener(string $eventName, string $class, string $method) : void
1544
    {
1545 15
        $listener = [
1546 15
            'class'  => $class,
1547
            'method' => $method,
1548
        ];
1549 15
1550
        if (! class_exists($class)) {
1551
            throw MappingException::entityListenerClassNotFound($class, $this->className);
1552
        }
1553 15
1554
        if (! method_exists($class, $method)) {
1555
            throw MappingException::entityListenerMethodNotFound($class, $method, $this->className);
1556
        }
1557 15
1558
        if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName], true)) {
1559
            throw MappingException::duplicateEntityListener($class, $method, $this->className);
1560
        }
1561 15
1562 15
        $this->entityListeners[$eventName][] = $listener;
1563
    }
1564
1565
    /**
1566
     * Sets the discriminator column definition.
1567
     *
1568
     * @throws MappingException
1569
     *
1570
     * @see getDiscriminatorColumn()
1571 90
     */
1572
    public function setDiscriminatorColumn(DiscriminatorColumnMetadata $discriminatorColumn) : void
1573 90
    {
1574
        if (isset($this->fieldNames[$discriminatorColumn->getColumnName()])) {
1575
            throw MappingException::duplicateColumnName($this->className, $discriminatorColumn->getColumnName());
1576
        }
1577 90
1578
        $discriminatorColumn->setTableName($discriminatorColumn->getTableName() ?? $this->getTableName());
1579 90
1580
        $allowedTypeList = ['boolean', 'array', 'object', 'datetime', 'time', 'date'];
1581 90
1582
        if (in_array($discriminatorColumn->getTypeName(), $allowedTypeList, true)) {
1583
            throw MappingException::invalidDiscriminatorColumnType($discriminatorColumn->getTypeName());
1584
        }
1585 90
1586 90
        $this->discriminatorColumn = $discriminatorColumn;
1587
    }
1588
1589
    /**
1590
     * Sets the discriminator values used by this class.
1591
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
1592
     *
1593
     * @param string[] $map
1594
     *
1595
     * @throws MappingException
1596 89
     */
1597
    public function setDiscriminatorMap(array $map) : void
1598 89
    {
1599 89
        foreach ($map as $value => $className) {
1600
            $this->addDiscriminatorMapClass($value, $className);
1601 89
        }
1602
    }
1603
1604
    /**
1605
     * Adds one entry of the discriminator map with a new class and corresponding name.
1606
     *
1607
     * @param string|int $name
1608
     *
1609
     * @throws MappingException
1610 89
     */
1611
    public function addDiscriminatorMapClass($name, string $className) : void
1612 89
    {
1613
        $this->discriminatorMap[$name] = $className;
1614 89
1615 75
        if ($this->className === $className) {
1616
            $this->discriminatorValue = $name;
1617 75
1618
            return;
1619
        }
1620 88
1621
        if (! (class_exists($className) || interface_exists($className))) {
1622
            throw MappingException::invalidClassInDiscriminatorMap($className, $this->className);
1623
        }
1624 88
1625 83
        if (is_subclass_of($className, $this->className) && ! in_array($className, $this->subClasses, true)) {
1626
            $this->subClasses[] = $className;
1627 88
        }
1628
    }
1629 958
1630
    public function getValueGenerationPlan() : ValueGenerationPlan
1631 958
    {
1632
        return $this->valueGenerationPlan;
1633
    }
1634 351
1635
    public function setValueGenerationPlan(ValueGenerationPlan $valueGenerationPlan) : void
1636 351
    {
1637 351
        $this->valueGenerationPlan = $valueGenerationPlan;
1638
    }
1639
1640
    /**
1641
     * Checks whether the class has a named query with the given query name.
1642
     *
1643
     * @param string $queryName
1644 2
     */
1645
    public function hasNamedQuery($queryName) : bool
1646 2
    {
1647
        return isset($this->namedQueries[$queryName]);
1648
    }
1649
1650
    /**
1651
     * Checks whether the class has a named native query with the given query name.
1652
     *
1653
     * @param string $queryName
1654 1
     */
1655
    public function hasNamedNativeQuery($queryName) : bool
1656 1
    {
1657
        return isset($this->namedNativeQueries[$queryName]);
1658
    }
1659
1660
    /**
1661
     * Checks whether the class has a named native query with the given query name.
1662
     *
1663
     * @param string $name
1664 1
     */
1665
    public function hasSqlResultSetMapping($name) : bool
1666 1
    {
1667
        return isset($this->sqlResultSetMappings[$name]);
1668
    }
1669
1670
    /**
1671
     * Marks this class as read only, no change tracking is applied to it.
1672 2
     */
1673
    public function asReadOnly() : void
1674 2
    {
1675 2
        $this->readOnly = true;
1676
    }
1677
1678
    /**
1679
     * Whether this class is read only or not.
1680 422
     */
1681
    public function isReadOnly() : bool
1682 422
    {
1683
        return $this->readOnly;
1684
    }
1685 976
1686
    public function isVersioned() : bool
1687 976
    {
1688
        return $this->versionProperty !== null;
1689
    }
1690
1691
    /**
1692
     * Map Embedded Class
1693
     *
1694
     * @param mixed[] $mapping
1695
     *
1696
     * @throws MappingException
1697
     */
1698
    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

1698
    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...
1699
    {
1700
        /*if (isset($this->declaredProperties[$mapping['fieldName']])) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1701
            throw MappingException::duplicateProperty($this->className, $this->getProperty($mapping['fieldName']));
1702
        }
1703
1704
        $this->embeddedClasses[$mapping['fieldName']] = [
1705
            'class'          => $this->fullyQualifiedClassName($mapping['class']),
1706
            'columnPrefix'   => $mapping['columnPrefix'],
1707
            'declaredField'  => $mapping['declaredField'] ?? null,
1708
            'originalField'  => $mapping['originalField'] ?? null,
1709
            'declaringClass' => $this,
1710
        ];*/
1711
    }
1712
1713
    /**
1714
     * Inline the embeddable class
1715
     *
1716
     * @param string $property
1717
     */
1718
    public function inlineEmbeddable($property, ClassMetadata $embeddable) : void
0 ignored issues
show
Unused Code introduced by
The parameter $embeddable is not used and could be removed. ( Ignorable by Annotation )

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

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

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

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

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

1718
    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...
1719
    {
1720
        /*foreach ($embeddable->fieldMappings as $fieldName => $fieldMapping) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
64% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1721
            $fieldMapping['fieldName']     = $property . "." . $fieldName;
1722
            $fieldMapping['originalClass'] = $fieldMapping['originalClass'] ?? $embeddable->getClassName();
1723
            $fieldMapping['originalField'] = $fieldMapping['originalField'] ?? $fieldName;
1724
            $fieldMapping['declaredField'] = isset($fieldMapping['declaredField'])
1725
                ? $property . '.' . $fieldMapping['declaredField']
1726
                : $property;
1727
1728
            if (! empty($this->embeddedClasses[$property]['columnPrefix'])) {
1729
                $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName'];
1730
            } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) {
1731
                $fieldMapping['columnName'] = $this->namingStrategy->embeddedFieldToColumnName(
1732
                    $property,
1733
                    $fieldMapping['columnName'],
1734
                    $this->reflectionClass->getName(),
1735
                    $embeddable->reflectionClass->getName()
1736
                );
1737
            }
1738
1739
            $this->mapField($fieldMapping);
1740
        }*/
1741
    }
1742
}
1743