Failed Conditions
Push — master ( 2ccf23...d791f7 )
by Michael
10:40
created

ClassMetadata::addProperty()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 51
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 34
CRAP Score 8

Importance

Changes 0
Metric Value
cc 8
eloc 35
nc 8
nop 1
dl 0
loc 51
ccs 34
cts 34
cp 1
crap 8
rs 6.5978
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

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

622
                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

622
                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...
623
            }
624
625 388
            if (! in_array($fieldName, $this->identifier, true)) {
626 388
                $this->identifier[] = $fieldName;
627
            }
628
        }
629
630 404
        $this->fieldNames[$columnName] = $fieldName;
631 404
    }
632
633
    /**
634
     * Validates & completes the basic mapping information for field mapping.
635
     *
636
     * @throws MappingException If something is wrong with the mapping.
637
     */
638 21
    protected function validateAndCompleteVersionFieldMapping(VersionFieldMetadata $property)
639
    {
640 21
        $this->versionProperty = $property;
641
642 21
        $options = $property->getOptions();
643
644 21
        if (isset($options['default'])) {
645
            return;
646
        }
647
648 21
        if (in_array($property->getTypeName(), ['integer', 'bigint', 'smallint'], true)) {
649 20
            $property->setOptions(array_merge($options, ['default' => 1]));
650
651 20
            return;
652
        }
653
654 2
        if ($property->getTypeName() === 'datetime') {
655 1
            $property->setOptions(array_merge($options, ['default' => 'CURRENT_TIMESTAMP']));
656
657 1
            return;
658
        }
659
660 1
        throw MappingException::unsupportedOptimisticLockingType($property->getType());
661
    }
662
663
    /**
664
     * Validates & completes the basic mapping information that is common to all
665
     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
666
     *
667
     * @throws MappingException If something is wrong with the mapping.
668
     * @throws CacheException   If entity is not cacheable.
669
     */
670 282
    protected function validateAndCompleteAssociationMapping(AssociationMetadata $property)
671
    {
672 282
        $fieldName    = $property->getName();
673 282
        $targetEntity = $property->getTargetEntity();
674
675 282
        if (! $targetEntity) {
676
            throw MappingException::missingTargetEntity($fieldName);
677
        }
678
679 282
        $property->setSourceEntity($this->className);
680 282
        $property->setOwningSide($property->getMappedBy() === null);
681 282
        $property->setTargetEntity($targetEntity);
682
683
        // Complete id mapping
684 282
        if ($property->isPrimaryKey()) {
685 44
            if ($property->isOrphanRemoval()) {
686 1
                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->className, $fieldName);
687
            }
688
689 43
            if (! in_array($property->getName(), $this->identifier, true)) {
690 43
                if ($property instanceof ToOneAssociationMetadata && count($property->getJoinColumns()) >= 2) {
691
                    throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
692
                        $property->getTargetEntity(),
693
                        $this->className,
694
                        $fieldName
695
                    );
696
                }
697
698 43
                $this->identifier[] = $property->getName();
699
            }
700
701 43
            if ($this->cache && ! $property->getCache()) {
702 2
                throw CacheException::nonCacheableEntityAssociation($this->className, $fieldName);
703
            }
704
705 41
            if ($property instanceof ToManyAssociationMetadata) {
706 1
                throw MappingException::illegalToManyIdentifierAssociation($this->className, $property->getName());
707
            }
708
        }
709
710
        // Cascades
711 278
        $cascadeTypes = ['remove', 'persist', 'refresh'];
712 278
        $cascades     = array_map('strtolower', $property->getCascade());
713
714 278
        if (in_array('all', $cascades, true)) {
715 4
            $cascades = $cascadeTypes;
716
        }
717
718 278
        if (count($cascades) !== count(array_intersect($cascades, $cascadeTypes))) {
719 1
            $diffCascades = array_diff($cascades, array_intersect($cascades, $cascadeTypes));
720
721 1
            throw MappingException::invalidCascadeOption($diffCascades, $this->className, $fieldName);
722
        }
723
724 277
        $property->setCascade($cascades);
725 277
    }
726
727
    /**
728
     * Validates & completes a to-one association mapping.
729
     *
730
     * @param ToOneAssociationMetadata $property The association mapping to validate & complete.
731
     *
732
     * @throws \RuntimeException
733
     * @throws MappingException
734
     */
735 242
    protected function validateAndCompleteToOneAssociationMetadata(ToOneAssociationMetadata $property)
736
    {
737 242
        $fieldName = $property->getName();
738
739 242
        if ($property->getJoinColumns()) {
740 170
            $property->setOwningSide(true);
741
        }
742
743 242
        if ($property->isOwningSide()) {
744 239
            if (empty($property->getJoinColumns())) {
745
                // Apply default join column
746 82
                $property->addJoinColumn(new JoinColumnMetadata());
747
            }
748
749 239
            $uniqueConstraintColumns = [];
750
751 239
            foreach ($property->getJoinColumns() as $joinColumn) {
752
                /** @var JoinColumnMetadata $joinColumn */
753 239
                if ($property instanceof OneToOneAssociationMetadata && $this->inheritanceType !== InheritanceType::SINGLE_TABLE) {
754 118
                    if (count($property->getJoinColumns()) === 1) {
755 116
                        if (! $property->isPrimaryKey()) {
756 116
                            $joinColumn->setUnique(true);
757
                        }
758
                    } else {
759 2
                        $uniqueConstraintColumns[] = $joinColumn->getColumnName();
760
                    }
761
                }
762
763 239
                $joinColumn->setTableName(! $this->isMappedSuperclass ? $this->getTableName() : null);
764
765 239
                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...
766 101
                    $joinColumn->setColumnName($this->namingStrategy->joinColumnName($fieldName, $this->className));
767
                }
768
769 239
                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...
770 82
                    $joinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName());
771
                }
772
773 239
                $this->fieldNames[$joinColumn->getColumnName()] = $fieldName;
774
            }
775
776 239
            if ($uniqueConstraintColumns) {
0 ignored issues
show
introduced by
The condition $uniqueConstraintColumns can never be true.
Loading history...
777 2
                if (! $this->table) {
778
                    throw new \RuntimeException(
779
                        'ClassMetadata::setTable() has to be called before defining a one to one relationship.'
780
                    );
781
                }
782
783 2
                $this->table->addUniqueConstraint(
784
                    [
785 2
                        'name'    => sprintf('%s_uniq', $fieldName),
786 2
                        'columns' => $uniqueConstraintColumns,
787
                        'options' => [],
788
                        'flags'   => [],
789
                    ]
790
                );
791
            }
792
        }
793
794 242
        if ($property->isOrphanRemoval()) {
795 9
            $cascades = $property->getCascade();
796
797 9
            if (! in_array('remove', $cascades, true)) {
798 8
                $cascades[] = 'remove';
799
800 8
                $property->setCascade($cascades);
801
            }
802
803
            // @todo guilhermeblanco where is this used?
804
            // @todo guilhermeblanco Shouldn￿'t we iterate through JoinColumns to set non-uniqueness?
805
            //$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...
806
        }
807
808 242
        if ($property->isPrimaryKey() && ! $property->isOwningSide()) {
809 1
            throw MappingException::illegalInverseIdentifierAssociation($this->className, $fieldName);
810
        }
811 241
    }
812
813
    /**
814
     * Validates & completes a to-many association mapping.
815
     *
816
     * @param ToManyAssociationMetadata $property The association mapping to validate & complete.
817
     *
818
     * @throws MappingException
819
     */
820 166
    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

820
    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...
821
    {
822
        // Do nothing
823 166
    }
824
825
    /**
826
     * Validates & completes a one-to-one association mapping.
827
     *
828
     * @param OneToOneAssociationMetadata $property The association mapping to validate & complete.
829
     */
830 125
    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

830
    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...
831
    {
832
        // Do nothing
833 125
    }
834
835
    /**
836
     * Validates & completes a many-to-one association mapping.
837
     *
838
     * @param ManyToOneAssociationMetadata $property The association mapping to validate & complete.
839
     *
840
     * @throws MappingException
841
     */
842 140
    protected function validateAndCompleteManyToOneMapping(ManyToOneAssociationMetadata $property)
843
    {
844
        // A many-to-one mapping is essentially a one-one backreference
845 140
        if ($property->isOrphanRemoval()) {
846
            throw MappingException::illegalOrphanRemoval($this->className, $property->getName());
847
        }
848 140
    }
849
850
    /**
851
     * Validates & completes a one-to-many association mapping.
852
     *
853
     * @param OneToManyAssociationMetadata $property The association mapping to validate & complete.
854
     *
855
     * @throws MappingException
856
     */
857 113
    protected function validateAndCompleteOneToManyMapping(OneToManyAssociationMetadata $property)
858
    {
859
        // OneToMany MUST be inverse side
860 113
        $property->setOwningSide(false);
861
862
        // OneToMany MUST have mappedBy
863 113
        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...
864
            throw MappingException::oneToManyRequiresMappedBy($property->getName());
865
        }
866
867 113
        if ($property->isOrphanRemoval()) {
868 23
            $cascades = $property->getCascade();
869
870 23
            if (! in_array('remove', $cascades, true)) {
871 20
                $cascades[] = 'remove';
872
873 20
                $property->setCascade($cascades);
874
            }
875
        }
876 113
    }
877
878
    /**
879
     * Validates & completes a many-to-many association mapping.
880
     *
881
     * @param ManyToManyAssociationMetadata $property The association mapping to validate & complete.
882
     *
883
     * @throws MappingException
884
     */
885 105
    protected function validateAndCompleteManyToManyMapping(ManyToManyAssociationMetadata $property)
886
    {
887 105
        if ($property->isOwningSide()) {
888
            // owning side MUST have a join table
889 93
            $joinTable = $property->getJoinTable() ?: new JoinTableMetadata();
890
891 93
            $property->setJoinTable($joinTable);
892
893 93
            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...
894 18
                $joinTableName = $this->namingStrategy->joinTableName(
895 18
                    $property->getSourceEntity(),
896 18
                    $property->getTargetEntity(),
897 18
                    $property->getName()
898
                );
899
900 18
                $joinTable->setName($joinTableName);
901
            }
902
903 93
            $selfReferencingEntityWithoutJoinColumns = $property->getSourceEntity() === $property->getTargetEntity() && ! $joinTable->hasColumns();
904
905 93
            if (! $joinTable->getJoinColumns()) {
906 16
                $referencedColumnName = $this->namingStrategy->referenceColumnName();
907 16
                $sourceReferenceName  = $selfReferencingEntityWithoutJoinColumns ? 'source' : $referencedColumnName;
908 16
                $columnName           = $this->namingStrategy->joinKeyColumnName($property->getSourceEntity(), $sourceReferenceName);
909 16
                $joinColumn           = new JoinColumnMetadata();
910
911 16
                $joinColumn->setColumnName($columnName);
912 16
                $joinColumn->setReferencedColumnName($referencedColumnName);
913 16
                $joinColumn->setOnDelete('CASCADE');
914
915 16
                $joinTable->addJoinColumn($joinColumn);
916
            }
917
918 93
            if (! $joinTable->getInverseJoinColumns()) {
919 16
                $referencedColumnName = $this->namingStrategy->referenceColumnName();
920 16
                $targetReferenceName  = $selfReferencingEntityWithoutJoinColumns ? 'target' : $referencedColumnName;
921 16
                $columnName           = $this->namingStrategy->joinKeyColumnName($property->getTargetEntity(), $targetReferenceName);
922 16
                $joinColumn           = new JoinColumnMetadata();
923
924 16
                $joinColumn->setColumnName($columnName);
925 16
                $joinColumn->setReferencedColumnName($referencedColumnName);
926 16
                $joinColumn->setOnDelete('CASCADE');
927
928 16
                $joinTable->addInverseJoinColumn($joinColumn);
929
            }
930
931 93
            foreach ($joinTable->getJoinColumns() as $joinColumn) {
932
                /** @var JoinColumnMetadata $joinColumn */
933 93
                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...
934 2
                    $joinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName());
935
                }
936
937 93
                $referencedColumnName = $joinColumn->getReferencedColumnName();
938
939 93
                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...
940 2
                    $columnName = $this->namingStrategy->joinKeyColumnName(
941 2
                        $property->getSourceEntity(),
942 2
                        $referencedColumnName
943
                    );
944
945 93
                    $joinColumn->setColumnName($columnName);
946
                }
947
            }
948
949 93
            foreach ($joinTable->getInverseJoinColumns() as $inverseJoinColumn) {
950
                /** @var JoinColumnMetadata $inverseJoinColumn */
951 93
                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...
952 2
                    $inverseJoinColumn->setReferencedColumnName($this->namingStrategy->referenceColumnName());
953
                }
954
955 93
                $referencedColumnName = $inverseJoinColumn->getReferencedColumnName();
956
957 93
                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...
958 2
                    $columnName = $this->namingStrategy->joinKeyColumnName(
959 2
                        $property->getTargetEntity(),
960 2
                        $referencedColumnName
961
                    );
962
963 93
                    $inverseJoinColumn->setColumnName($columnName);
964
                }
965
            }
966
        }
967 105
    }
968
969
    /**
970
     * {@inheritDoc}
971
     */
972 396
    public function getIdentifierFieldNames()
973
    {
974 396
        return $this->identifier;
975
    }
976
977
    /**
978
     * Gets the name of the single id field. Note that this only works on
979
     * entity classes that have a single-field pk.
980
     *
981
     * @return string
982
     *
983
     * @throws MappingException If the class has a composite primary key.
984
     */
985 149
    public function getSingleIdentifierFieldName()
986
    {
987 149
        if ($this->isIdentifierComposite()) {
988 1
            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->className);
989
        }
990
991 148
        if (! isset($this->identifier[0])) {
992 1
            throw MappingException::noIdDefined($this->className);
993
        }
994
995 147
        return $this->identifier[0];
996
    }
997
998
    /**
999
     * INTERNAL:
1000
     * Sets the mapped identifier/primary key fields of this class.
1001
     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1002
     *
1003
     * @param mixed[] $identifier
1004
     */
1005 101
    public function setIdentifier(array $identifier)
1006
    {
1007 101
        $this->identifier = $identifier;
1008 101
    }
1009
1010
    /**
1011
     * {@inheritDoc}
1012
     */
1013 1045
    public function getIdentifier()
1014
    {
1015 1045
        return $this->identifier;
1016
    }
1017
1018
    /**
1019
     * {@inheritDoc}
1020
     */
1021 185
    public function hasField($fieldName)
1022
    {
1023 185
        return isset($this->declaredProperties[$fieldName])
1024 185
            && $this->declaredProperties[$fieldName] instanceof FieldMetadata;
1025
    }
1026
1027
    /**
1028
     * Returns an array with identifier column names and their corresponding ColumnMetadata.
1029
     *
1030
     * @return ColumnMetadata[]
1031
     */
1032 434
    public function getIdentifierColumns(EntityManagerInterface $em) : array
1033
    {
1034 434
        $columns = [];
1035
1036 434
        foreach ($this->identifier as $idProperty) {
1037 434
            $property = $this->getProperty($idProperty);
1038
1039 434
            if ($property instanceof FieldMetadata) {
1040 429
                $columns[$property->getColumnName()] = $property;
1041
1042 429
                continue;
1043
            }
1044
1045
            /** @var AssociationMetadata $property */
1046
1047
            // Association defined as Id field
1048 24
            $targetClass = $em->getClassMetadata($property->getTargetEntity());
1049
1050 24
            if (! $property->isOwningSide()) {
1051
                $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

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

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

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

1185
            unset($this->fieldNames[$originalProperty->/** @scrutinizer ignore-call */ getColumnName()]);
Loading history...
1186
1187
            // Revert what should not be allowed to change
1188 5
            $property->setDeclaringClass($originalProperty->getDeclaringClass());
1189 5
            $property->setPrimaryKey($originalProperty->isPrimaryKey());
1190 9
        } elseif ($property instanceof AssociationMetadata) {
1191
            // Unset all defined fieldNames prior to override
1192 9
            if ($originalProperty instanceof ToOneAssociationMetadata && $originalProperty->isOwningSide()) {
1193 5
                foreach ($originalProperty->getJoinColumns() as $joinColumn) {
1194 5
                    unset($this->fieldNames[$joinColumn->getColumnName()]);
1195
                }
1196
            }
1197
1198
            // Override what it should be allowed to change
1199 9
            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...
1200 2
                $originalProperty->setInversedBy($property->getInversedBy());
0 ignored issues
show
Bug introduced by
The method setInversedBy() does not exist on Doctrine\ORM\Mapping\Property. It seems like you code against a sub-type of Doctrine\ORM\Mapping\Property such as Doctrine\ORM\Mapping\AssociationMetadata. ( Ignorable by Annotation )

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

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

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

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

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

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

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

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

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

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

1216
        $this->addProperty(/** @scrutinizer ignore-type */ $property);
Loading history...
1217 9
    }
1218
1219
    /**
1220
     * Checks if this entity is the root in any entity-inheritance-hierarchy.
1221
     *
1222
     * @return bool
1223
     */
1224 334
    public function isRootEntity()
1225
    {
1226 334
        return $this->className === $this->getRootClassName();
1227
    }
1228
1229
    /**
1230
     * Checks whether a mapped field is inherited from a superclass.
1231
     *
1232
     * @param string $fieldName
1233
     *
1234
     * @return bool TRUE if the field is inherited, FALSE otherwise.
1235
     */
1236 615
    public function isInheritedProperty($fieldName)
1237
    {
1238 615
        $declaringClass = $this->declaredProperties[$fieldName]->getDeclaringClass();
1239
1240 615
        return ! ($declaringClass->className === $this->className);
1241
    }
1242
1243
    /**
1244
     * {@inheritdoc}
1245
     */
1246 450
    public function setTable(TableMetadata $table) : void
1247
    {
1248 450
        $this->table = $table;
1249
1250 450
        if (empty($table->getName())) {
1251
            $table->setName($this->namingStrategy->classToTableName($this->className));
1252
        }
1253 450
    }
1254
1255
    /**
1256
     * Checks whether the given type identifies an inheritance type.
1257
     *
1258
     * @param int $type
1259
     *
1260
     * @return bool TRUE if the given type identifies an inheritance type, FALSe otherwise.
1261
     */
1262 119
    private function isInheritanceType($type)
1263
    {
1264 119
        return $type === InheritanceType::NONE
1265 92
            || $type === InheritanceType::SINGLE_TABLE
1266 52
            || $type === InheritanceType::JOINED
1267 119
            || $type === InheritanceType::TABLE_PER_CLASS;
1268
    }
1269
1270 907
    public function getColumn(string $columnName) : ?LocalColumnMetadata
1271
    {
1272 907
        foreach ($this->declaredProperties as $property) {
1273 907
            if ($property instanceof LocalColumnMetadata && $property->getColumnName() === $columnName) {
1274 907
                return $property;
1275
            }
1276
        }
1277
1278
        return null;
1279
    }
1280
1281
    /**
1282
     * Add a property mapping.
1283
     *
1284
     * @throws \RuntimeException
1285
     * @throws MappingException
1286
     * @throws CacheException
1287
     */
1288 428
    public function addProperty(Property $property)
1289
    {
1290 428
        $fieldName = $property->getName();
1291
1292
        // Check for empty field name
1293 428
        if (empty($fieldName)) {
1294 1
            throw MappingException::missingFieldName($this->className);
1295
        }
1296
1297 427
        $property->setDeclaringClass($this);
1298
1299
        switch (true) {
1300 427
            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...
1301 21
                $this->validateAndCompleteFieldMapping($property);
1302 21
                $this->validateAndCompleteVersionFieldMapping($property);
1303 20
                break;
1304
1305 426
            case ($property instanceof FieldMetadata):
1306 404
                $this->validateAndCompleteFieldMapping($property);
1307 403
                break;
1308
1309 285
            case ($property instanceof OneToOneAssociationMetadata):
1310 127
                $this->validateAndCompleteAssociationMapping($property);
1311 126
                $this->validateAndCompleteToOneAssociationMetadata($property);
1312 125
                $this->validateAndCompleteOneToOneMapping($property);
1313 125
                break;
1314
1315 222
            case ($property instanceof OneToManyAssociationMetadata):
1316 113
                $this->validateAndCompleteAssociationMapping($property);
1317 113
                $this->validateAndCompleteToManyAssociationMetadata($property);
1318 113
                $this->validateAndCompleteOneToManyMapping($property);
1319 113
                break;
1320
1321 218
            case ($property instanceof ManyToOneAssociationMetadata):
1322 143
                $this->validateAndCompleteAssociationMapping($property);
1323 140
                $this->validateAndCompleteToOneAssociationMetadata($property);
1324 140
                $this->validateAndCompleteManyToOneMapping($property);
1325 140
                break;
1326
1327 123
            case ($property instanceof ManyToManyAssociationMetadata):
1328 106
                $this->validateAndCompleteAssociationMapping($property);
1329 105
                $this->validateAndCompleteToManyAssociationMetadata($property);
1330 105
                $this->validateAndCompleteManyToManyMapping($property);
1331 105
                break;
1332
1333
            default:
1334
                // Transient properties are ignored on purpose here! =)
1335 32
                break;
1336
        }
1337
1338 419
        $this->addDeclaredProperty($property);
1339 419
    }
1340
1341
    /**
1342
     * INTERNAL:
1343
     * Adds a property mapping without completing/validating it.
1344
     * This is mainly used to add inherited property mappings to derived classes.
1345
     */
1346 99
    public function addInheritedProperty(Property $property)
1347
    {
1348 99
        $inheritedProperty = clone $property;
1349 99
        $declaringClass    = $property->getDeclaringClass();
1350
1351 99
        if ($inheritedProperty instanceof FieldMetadata) {
1352 98
            if (! $declaringClass->isMappedSuperclass) {
1353 75
                $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

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

1697
    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...
1698
    {
1699
        /*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...
1700
            throw MappingException::duplicateProperty($this->className, $this->getProperty($mapping['fieldName']));
1701
        }
1702
1703
        $this->embeddedClasses[$mapping['fieldName']] = [
1704
            'class'          => $this->fullyQualifiedClassName($mapping['class']),
1705
            'columnPrefix'   => $mapping['columnPrefix'],
1706
            'declaredField'  => $mapping['declaredField'] ?? null,
1707
            'originalField'  => $mapping['originalField'] ?? null,
1708
            'declaringClass' => $this,
1709
        ];*/
1710 1
    }
1711
1712
    /**
1713
     * Inline the embeddable class
1714
     *
1715
     * @param string $property
1716
     */
1717
    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

1717
    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

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