Failed Conditions
Push — 2.7 ( c036c0...266f0d )
by Jonathan
57:23 queued 50:07
created

ClassMetadataInfo::markReadOnly()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM\Mapping;
21
22
use BadMethodCallException;
23
use Doctrine\Instantiator\Instantiator;
24
use InvalidArgumentException;
25
use RuntimeException;
26
use Doctrine\DBAL\Types\Type;
27
use Doctrine\DBAL\Platforms\AbstractPlatform;
28
use ReflectionClass;
29
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Doctrine\ORM\Mapping\ClassMetadata. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
30
use Doctrine\ORM\Cache\CacheException;
31
32
/**
33
 * A <tt>ClassMetadata</tt> instance holds all the object-relational mapping metadata
34
 * of an entity and its associations.
35
 *
36
 * Once populated, ClassMetadata instances are usually cached in a serialized form.
37
 *
38
 * <b>IMPORTANT NOTE:</b>
39
 *
40
 * The fields of this class are only public for 2 reasons:
41
 * 1) To allow fast READ access.
42
 * 2) To drastically reduce the size of a serialized instance (private/protected members
43
 *    get the whole class name, namespace inclusive, prepended to every property in
44
 *    the serialized representation).
45
 *
46
 * @author Roman Borschel <[email protected]>
47
 * @author Jonathan H. Wage <[email protected]>
48
 * @since 2.0
49
 */
50
class ClassMetadataInfo implements ClassMetadata
51
{
52
    /* The inheritance mapping types */
53
    /**
54
     * NONE means the class does not participate in an inheritance hierarchy
55
     * and therefore does not need an inheritance mapping type.
56
     */
57
    const INHERITANCE_TYPE_NONE = 1;
58
59
    /**
60
     * JOINED means the class will be persisted according to the rules of
61
     * <tt>Class Table Inheritance</tt>.
62
     */
63
    const INHERITANCE_TYPE_JOINED = 2;
64
65
    /**
66
     * SINGLE_TABLE means the class will be persisted according to the rules of
67
     * <tt>Single Table Inheritance</tt>.
68
     */
69
    const INHERITANCE_TYPE_SINGLE_TABLE = 3;
70
71
    /**
72
     * TABLE_PER_CLASS means the class will be persisted according to the rules
73
     * of <tt>Concrete Table Inheritance</tt>.
74
     */
75
    const INHERITANCE_TYPE_TABLE_PER_CLASS = 4;
76
77
    /* The Id generator types. */
78
    /**
79
     * AUTO means the generator type will depend on what the used platform prefers.
80
     * Offers full portability.
81
     */
82
    const GENERATOR_TYPE_AUTO = 1;
83
84
    /**
85
     * SEQUENCE means a separate sequence object will be used. Platforms that do
86
     * not have native sequence support may emulate it. Full portability is currently
87
     * not guaranteed.
88
     */
89
    const GENERATOR_TYPE_SEQUENCE = 2;
90
91
    /**
92
     * TABLE means a separate table is used for id generation.
93
     * Offers full portability.
94
     */
95
    const GENERATOR_TYPE_TABLE = 3;
96
97
    /**
98
     * IDENTITY means an identity column is used for id generation. The database
99
     * will fill in the id column on insertion. Platforms that do not support
100
     * native identity columns may emulate them. Full portability is currently
101
     * not guaranteed.
102
     */
103
    const GENERATOR_TYPE_IDENTITY = 4;
104
105
    /**
106
     * NONE means the class does not have a generated id. That means the class
107
     * must have a natural, manually assigned id.
108
     */
109
    const GENERATOR_TYPE_NONE = 5;
110
111
    /**
112
     * UUID means that a UUID/GUID expression is used for id generation. Full
113
     * portability is currently not guaranteed.
114
     */
115
    const GENERATOR_TYPE_UUID = 6;
116
117
    /**
118
     * CUSTOM means that customer will use own ID generator that supposedly work
119
     */
120
    const GENERATOR_TYPE_CUSTOM = 7;
121
122
    /**
123
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
124
     * by doing a property-by-property comparison with the original data. This will
125
     * be done for all entities that are in MANAGED state at commit-time.
126
     *
127
     * This is the default change tracking policy.
128
     */
129
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
130
131
    /**
132
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
133
     * by doing a property-by-property comparison with the original data. This will
134
     * be done only for entities that were explicitly saved (through persist() or a cascade).
135
     */
136
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
137
138
    /**
139
     * NOTIFY means that Doctrine relies on the entities sending out notifications
140
     * when their properties change. Such entity classes must implement
141
     * the <tt>NotifyPropertyChanged</tt> interface.
142
     */
143
    const CHANGETRACKING_NOTIFY = 3;
144
145
    /**
146
     * Specifies that an association is to be fetched when it is first accessed.
147
     */
148
    const FETCH_LAZY = 2;
149
150
    /**
151
     * Specifies that an association is to be fetched when the owner of the
152
     * association is fetched.
153
     */
154
    const FETCH_EAGER = 3;
155
156
    /**
157
     * Specifies that an association is to be fetched lazy (on first access) and that
158
     * commands such as Collection#count, Collection#slice are issued directly against
159
     * the database if the collection is not yet initialized.
160
     */
161
    const FETCH_EXTRA_LAZY = 4;
162
163
    /**
164
     * Identifies a one-to-one association.
165
     */
166
    const ONE_TO_ONE = 1;
167
168
    /**
169
     * Identifies a many-to-one association.
170
     */
171
    const MANY_TO_ONE = 2;
172
173
    /**
174
     * Identifies a one-to-many association.
175
     */
176
    const ONE_TO_MANY = 4;
177
178
    /**
179
     * Identifies a many-to-many association.
180
     */
181
    const MANY_TO_MANY = 8;
182
183
    /**
184
     * Combined bitmask for to-one (single-valued) associations.
185
     */
186
    const TO_ONE = 3;
187
188
    /**
189
     * Combined bitmask for to-many (collection-valued) associations.
190
     */
191
    const TO_MANY = 12;
192
193
    /**
194
     * ReadOnly cache can do reads, inserts and deletes, cannot perform updates or employ any locks,
195
     */
196
    const CACHE_USAGE_READ_ONLY = 1;
197
198
    /**
199
     * Nonstrict Read Write Cache doesn’t employ any locks but can do inserts, update and deletes.
200
     */
201
    const CACHE_USAGE_NONSTRICT_READ_WRITE = 2;
202
203
    /**
204
     * Read Write Attempts to lock the entity before update/delete.
205
     */
206
    const CACHE_USAGE_READ_WRITE = 3;
207
208
    /**
209
     * READ-ONLY: The name of the entity class.
210
     *
211
     * @var string
212
     */
213
    public $name;
214
215
    /**
216
     * READ-ONLY: The namespace the entity class is contained in.
217
     *
218
     * @var string
219
     *
220
     * @todo Not really needed. Usage could be localized.
221
     */
222
    public $namespace;
223
224
    /**
225
     * READ-ONLY: The name of the entity class that is at the root of the mapped entity inheritance
226
     * hierarchy. If the entity is not part of a mapped inheritance hierarchy this is the same
227
     * as {@link $name}.
228
     *
229
     * @var string
230
     */
231
    public $rootEntityName;
232
233
    /**
234
     * READ-ONLY: The definition of custom generator. Only used for CUSTOM
235
     * generator type
236
     *
237
     * The definition has the following structure:
238
     * <code>
239
     * array(
240
     *     'class' => 'ClassName',
241
     * )
242
     * </code>
243
     *
244
     * @var array
245
     *
246
     * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
247
     */
248
    public $customGeneratorDefinition;
249
250
    /**
251
     * The name of the custom repository class used for the entity class.
252
     * (Optional).
253
     *
254
     * @var string
255
     */
256
    public $customRepositoryClassName;
257
258
    /**
259
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
260
     *
261
     * @var boolean
262
     */
263
    public $isMappedSuperclass = false;
264
265
    /**
266
     * READ-ONLY: Whether this class describes the mapping of an embeddable class.
267
     *
268
     * @var boolean
269
     */
270
    public $isEmbeddedClass = false;
271
272
    /**
273
     * READ-ONLY: The names of the parent classes (ancestors).
274
     *
275
     * @var array
276
     */
277
    public $parentClasses = [];
278
279
    /**
280
     * READ-ONLY: The names of all subclasses (descendants).
281
     *
282
     * @var array
283
     */
284
    public $subClasses = [];
285
286
    /**
287
     * READ-ONLY: The names of all embedded classes based on properties.
288
     *
289
     * @var array
290
     */
291
    public $embeddedClasses = [];
292
293
    /**
294
     * READ-ONLY: The named queries allowed to be called directly from Repository.
295
     *
296
     * @var array
297
     */
298
    public $namedQueries = [];
299
300
    /**
301
     * READ-ONLY: The named native queries allowed to be called directly from Repository.
302
     *
303
     * A native SQL named query definition has the following structure:
304
     * <pre>
305
     * array(
306
     *     'name'               => <query name>,
307
     *     'query'              => <sql query>,
308
     *     'resultClass'        => <class of the result>,
309
     *     'resultSetMapping'   => <name of a SqlResultSetMapping>
310
     * )
311
     * </pre>
312
     *
313
     * @var array
314
     */
315
    public $namedNativeQueries = [];
316
317
    /**
318
     * READ-ONLY: The mappings of the results of native SQL queries.
319
     *
320
     * A native result mapping definition has the following structure:
321
     * <pre>
322
     * array(
323
     *     'name'               => <result name>,
324
     *     'entities'           => array(<entity result mapping>),
325
     *     'columns'            => array(<column result mapping>)
326
     * )
327
     * </pre>
328
     *
329
     * @var array
330
     */
331
    public $sqlResultSetMappings = [];
332
333
    /**
334
     * READ-ONLY: The field names of all fields that are part of the identifier/primary key
335
     * of the mapped entity class.
336
     *
337
     * @var array
338
     */
339
    public $identifier = [];
340
341
    /**
342
     * READ-ONLY: The inheritance mapping type used by the class.
343
     *
344
     * @var integer
345
     */
346
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
347
348
    /**
349
     * READ-ONLY: The Id generator type used by the class.
350
     *
351
     * @var int
352
     */
353
    public $generatorType = self::GENERATOR_TYPE_NONE;
354
355
    /**
356
     * READ-ONLY: The field mappings of the class.
357
     * Keys are field names and values are mapping definitions.
358
     *
359
     * The mapping definition array has the following values:
360
     *
361
     * - <b>fieldName</b> (string)
362
     * The name of the field in the Entity.
363
     *
364
     * - <b>type</b> (string)
365
     * The type name of the mapped field. Can be one of Doctrine's mapping types
366
     * or a custom mapping type.
367
     *
368
     * - <b>columnName</b> (string, optional)
369
     * The column name. Optional. Defaults to the field name.
370
     *
371
     * - <b>length</b> (integer, optional)
372
     * The database length of the column. Optional. Default value taken from
373
     * the type.
374
     *
375
     * - <b>id</b> (boolean, optional)
376
     * Marks the field as the primary key of the entity. Multiple fields of an
377
     * entity can have the id attribute, forming a composite key.
378
     *
379
     * - <b>nullable</b> (boolean, optional)
380
     * Whether the column is nullable. Defaults to FALSE.
381
     *
382
     * - <b>columnDefinition</b> (string, optional, schema-only)
383
     * The SQL fragment that is used when generating the DDL for the column.
384
     *
385
     * - <b>precision</b> (integer, optional, schema-only)
386
     * The precision of a decimal column. Only valid if the column type is decimal.
387
     *
388
     * - <b>scale</b> (integer, optional, schema-only)
389
     * The scale of a decimal column. Only valid if the column type is decimal.
390
     *
391
     * - <b>'unique'</b> (string, optional, schema-only)
392
     * Whether a unique constraint should be generated for the column.
393
     *
394
     * @var array
395
     */
396
    public $fieldMappings = [];
397
398
    /**
399
     * READ-ONLY: An array of field names. Used to look up field names from column names.
400
     * Keys are column names and values are field names.
401
     *
402
     * @var array
403
     */
404
    public $fieldNames = [];
405
406
    /**
407
     * READ-ONLY: A map of field names to column names. Keys are field names and values column names.
408
     * Used to look up column names from field names.
409
     * This is the reverse lookup map of $_fieldNames.
410
     *
411
     * @var array
412
     *
413
     * @deprecated 3.0 Remove this.
414
     */
415
    public $columnNames = [];
416
417
    /**
418
     * READ-ONLY: The discriminator value of this class.
419
     *
420
     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
421
     * where a discriminator column is used.</b>
422
     *
423
     * @var mixed
424
     *
425
     * @see discriminatorColumn
426
     */
427
    public $discriminatorValue;
428
429
    /**
430
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
431
     *
432
     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
433
     * where a discriminator column is used.</b>
434
     *
435
     * @var mixed
436
     *
437
     * @see discriminatorColumn
438
     */
439
    public $discriminatorMap = [];
440
441
    /**
442
     * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
443
     * inheritance mappings.
444
     *
445
     * @var array
446
     */
447
    public $discriminatorColumn;
448
449
    /**
450
     * READ-ONLY: The primary table definition. The definition is an array with the
451
     * following entries:
452
     *
453
     * name => <tableName>
454
     * schema => <schemaName>
455
     * indexes => array
456
     * uniqueConstraints => array
457
     *
458
     * @var array
459
     */
460
    public $table;
461
462
    /**
463
     * READ-ONLY: The registered lifecycle callbacks for entities of this class.
464
     *
465
     * @var array[]
466
     */
467
    public $lifecycleCallbacks = [];
468
469
    /**
470
     * READ-ONLY: The registered entity listeners.
471
     *
472
     * @var array
473
     */
474
    public $entityListeners = [];
475
476
    /**
477
     * READ-ONLY: The association mappings of this class.
478
     *
479
     * The mapping definition array supports the following keys:
480
     *
481
     * - <b>fieldName</b> (string)
482
     * The name of the field in the entity the association is mapped to.
483
     *
484
     * - <b>targetEntity</b> (string)
485
     * The class name of the target entity. If it is fully-qualified it is used as is.
486
     * If it is a simple, unqualified class name the namespace is assumed to be the same
487
     * as the namespace of the source entity.
488
     *
489
     * - <b>mappedBy</b> (string, required for bidirectional associations)
490
     * The name of the field that completes the bidirectional association on the owning side.
491
     * This key must be specified on the inverse side of a bidirectional association.
492
     *
493
     * - <b>inversedBy</b> (string, required for bidirectional associations)
494
     * The name of the field that completes the bidirectional association on the inverse side.
495
     * This key must be specified on the owning side of a bidirectional association.
496
     *
497
     * - <b>cascade</b> (array, optional)
498
     * The names of persistence operations to cascade on the association. The set of possible
499
     * values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others).
500
     *
501
     * - <b>orderBy</b> (array, one-to-many/many-to-many only)
502
     * A map of field names (of the target entity) to sorting directions (ASC/DESC).
503
     * Example: array('priority' => 'desc')
504
     *
505
     * - <b>fetch</b> (integer, optional)
506
     * The fetching strategy to use for the association, usually defaults to FETCH_LAZY.
507
     * Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY.
508
     *
509
     * - <b>joinTable</b> (array, optional, many-to-many only)
510
     * Specification of the join table and its join columns (foreign keys).
511
     * Only valid for many-to-many mappings. Note that one-to-many associations can be mapped
512
     * through a join table by simply mapping the association as many-to-many with a unique
513
     * constraint on the join table.
514
     *
515
     * - <b>indexBy</b> (string, optional, to-many only)
516
     * Specification of a field on target-entity that is used to index the collection by.
517
     * This field HAS to be either the primary key or a unique column. Otherwise the collection
518
     * does not contain all the entities that are actually related.
519
     *
520
     * A join table definition has the following structure:
521
     * <pre>
522
     * array(
523
     *     'name' => <join table name>,
524
     *      'joinColumns' => array(<join column mapping from join table to source table>),
525
     *      'inverseJoinColumns' => array(<join column mapping from join table to target table>)
526
     * )
527
     * </pre>
528
     *
529
     * @var array
530
     */
531
    public $associationMappings = [];
532
533
    /**
534
     * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite.
535
     *
536
     * @var boolean
537
     */
538
    public $isIdentifierComposite = false;
539
540
    /**
541
     * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one foreign key association.
542
     *
543
     * This flag is necessary because some code blocks require special treatment of this cases.
544
     *
545
     * @var boolean
546
     */
547
    public $containsForeignIdentifier = false;
548
549
    /**
550
     * READ-ONLY: The ID generator used for generating IDs for this class.
551
     *
552
     * @var \Doctrine\ORM\Id\AbstractIdGenerator
553
     *
554
     * @todo Remove!
555
     */
556
    public $idGenerator;
557
558
    /**
559
     * READ-ONLY: The definition of the sequence generator of this class. Only used for the
560
     * SEQUENCE generation strategy.
561
     *
562
     * The definition has the following structure:
563
     * <code>
564
     * array(
565
     *     'sequenceName' => 'name',
566
     *     'allocationSize' => 20,
567
     *     'initialValue' => 1
568
     * )
569
     * </code>
570
     *
571
     * @var array
572
     *
573
     * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
574
     */
575
    public $sequenceGeneratorDefinition;
576
577
    /**
578
     * READ-ONLY: The definition of the table generator of this class. Only used for the
579
     * TABLE generation strategy.
580
     *
581
     * @var array
582
     *
583
     * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
584
     */
585
    public $tableGeneratorDefinition;
586
587
    /**
588
     * READ-ONLY: The policy used for change-tracking on entities of this class.
589
     *
590
     * @var integer
591
     */
592
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
593
594
    /**
595
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
596
     * with optimistic locking.
597
     *
598
     * @var boolean
599
     */
600
    public $isVersioned;
601
602
    /**
603
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
604
     *
605
     * @var mixed
606
     */
607
    public $versionField;
608
609
    /**
610
     * @var array
611
     */
612
    public $cache = null;
613
614
    /**
615
     * The ReflectionClass instance of the mapped class.
616
     *
617
     * @var ReflectionClass
618
     */
619
    public $reflClass;
620
621
    /**
622
     * Is this entity marked as "read-only"?
623
     *
624
     * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance
625
     * optimization for entities that are immutable, either in your domain or through the relation database
626
     * (coming from a view, or a history table for example).
627
     *
628
     * @var bool
629
     */
630
    public $isReadOnly = false;
631
632
    /**
633
     * NamingStrategy determining the default column and table names.
634
     *
635
     * @var \Doctrine\ORM\Mapping\NamingStrategy
636
     */
637
    protected $namingStrategy;
638
639
    /**
640
     * The ReflectionProperty instances of the mapped class.
641
     *
642
     * @var \ReflectionProperty[]
643
     */
644
    public $reflFields = [];
645
646
    /**
647
     * @var \Doctrine\Instantiator\InstantiatorInterface|null
648
     */
649
    private $instantiator;
650
651
    /**
652
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
653
     * metadata of the class with the given name.
654
     *
655
     * @param string              $entityName     The name of the entity class the new instance is used for.
656
     * @param NamingStrategy|null $namingStrategy
657
     */
658 753
    public function __construct($entityName, NamingStrategy $namingStrategy = null)
659
    {
660 753
        $this->name = $entityName;
661 753
        $this->rootEntityName = $entityName;
662 753
        $this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy();
663 753
        $this->instantiator   = new Instantiator();
664 753
    }
665
666
    /**
667
     * Gets the ReflectionProperties of the mapped class.
668
     *
669
     * @return array An array of ReflectionProperty instances.
670
     */
671 237
    public function getReflectionProperties()
672
    {
673 237
        return $this->reflFields;
674
    }
675
676
    /**
677
     * Gets a ReflectionProperty for a specific field of the mapped class.
678
     *
679
     * @param string $name
680
     *
681
     * @return \ReflectionProperty
682
     */
683 1
    public function getReflectionProperty($name)
684
    {
685 1
        return $this->reflFields[$name];
686
    }
687
688
    /**
689
     * Gets the ReflectionProperty for the single identifier field.
690
     *
691
     * @return \ReflectionProperty
692
     *
693
     * @throws BadMethodCallException If the class has a composite identifier.
694
     */
695
    public function getSingleIdReflectionProperty()
696
    {
697
        if ($this->isIdentifierComposite) {
698
            throw new BadMethodCallException("Class " . $this->name . " has a composite identifier.");
699
        }
700
701
        return $this->reflFields[$this->identifier[0]];
702
    }
703
704
    /**
705
     * Extracts the identifier values of an entity of this class.
706
     *
707
     * For composite identifiers, the identifier values are returned as an array
708
     * with the same order as the field order in {@link identifier}.
709
     *
710
     * @param object $entity
711
     *
712
     * @return array
713
     */
714 488
    public function getIdentifierValues($entity)
715
    {
716 488
        if ($this->isIdentifierComposite) {
717 95
            $id = [];
718
719 95
            foreach ($this->identifier as $idField) {
720 95
                $value = $this->reflFields[$idField]->getValue($entity);
721
722 95
                if (null !== $value) {
723 95
                    $id[$idField] = $value;
724
                }
725
            }
726
727 95
            return $id;
728
        }
729
730 469
        $id = $this->identifier[0];
731 469
        $value = $this->reflFields[$id]->getValue($entity);
732
733 469
        if (null === $value) {
734 31
            return [];
735
        }
736
737 443
        return [$id => $value];
738
    }
739
740
    /**
741
     * Populates the entity identifier of an entity.
742
     *
743
     * @param object $entity
744
     * @param array  $id
745
     *
746
     * @return void
747
     *
748
     * @todo Rename to assignIdentifier()
749
     */
750 8
    public function setIdentifierValues($entity, array $id)
751
    {
752 8
        foreach ($id as $idField => $idValue) {
753 8
            $this->reflFields[$idField]->setValue($entity, $idValue);
754
        }
755 8
    }
756
757
    /**
758
     * Sets the specified field to the specified value on the given entity.
759
     *
760
     * @param object $entity
761
     * @param string $field
762
     * @param mixed  $value
763
     *
764
     * @return void
765
     */
766 236
    public function setFieldValue($entity, $field, $value)
767
    {
768 236
        $this->reflFields[$field]->setValue($entity, $value);
769 236
    }
770
771
    /**
772
     * Gets the specified field's value off the given entity.
773
     *
774
     * @param object $entity
775
     * @param string $field
776
     *
777
     * @return mixed
778
     */
779 334
    public function getFieldValue($entity, $field)
780
    {
781 334
        return $this->reflFields[$field]->getValue($entity);
782
    }
783
784
    /**
785
     * Creates a string representation of this instance.
786
     *
787
     * @return string The string representation of this instance.
788
     *
789
     * @todo Construct meaningful string representation.
790
     */
791
    public function __toString()
792
    {
793
        return __CLASS__ . '@' . spl_object_hash($this);
794
    }
795
796
    /**
797
     * Determines which fields get serialized.
798
     *
799
     * It is only serialized what is necessary for best unserialization performance.
800
     * That means any metadata properties that are not set or empty or simply have
801
     * their default value are NOT serialized.
802
     *
803
     * Parts that are also NOT serialized because they can not be properly unserialized:
804
     *      - reflClass (ReflectionClass)
805
     *      - reflFields (ReflectionProperty array)
806
     *
807
     * @return array The names of all the fields that should be serialized.
808
     */
809 6
    public function __sleep()
810
    {
811
        // This metadata is always serialized/cached.
812
        $serialized = [
813 6
            'associationMappings',
814
            'columnNames', //TODO: 3.0 Remove this. Can use fieldMappings[$fieldName]['columnName']
815
            'fieldMappings',
816
            'fieldNames',
817
            'embeddedClasses',
818
            'identifier',
819
            'isIdentifierComposite', // TODO: REMOVE
820
            'name',
821
            'namespace', // TODO: REMOVE
822
            'table',
823
            'rootEntityName',
824
            'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
825
        ];
826
827
        // The rest of the metadata is only serialized if necessary.
828 6
        if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
829
            $serialized[] = 'changeTrackingPolicy';
830
        }
831
832 6
        if ($this->customRepositoryClassName) {
833 1
            $serialized[] = 'customRepositoryClassName';
834
        }
835
836 6
        if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
837 1
            $serialized[] = 'inheritanceType';
838 1
            $serialized[] = 'discriminatorColumn';
839 1
            $serialized[] = 'discriminatorValue';
840 1
            $serialized[] = 'discriminatorMap';
841 1
            $serialized[] = 'parentClasses';
842 1
            $serialized[] = 'subClasses';
843
        }
844
845 6
        if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
846 1
            $serialized[] = 'generatorType';
847 1
            if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
848
                $serialized[] = 'sequenceGeneratorDefinition';
849
            }
850
        }
851
852 6
        if ($this->isMappedSuperclass) {
853
            $serialized[] = 'isMappedSuperclass';
854
        }
855
856 6
        if ($this->isEmbeddedClass) {
857 1
            $serialized[] = 'isEmbeddedClass';
858
        }
859
860 6
        if ($this->containsForeignIdentifier) {
861
            $serialized[] = 'containsForeignIdentifier';
862
        }
863
864 6
        if ($this->isVersioned) {
865
            $serialized[] = 'isVersioned';
866
            $serialized[] = 'versionField';
867
        }
868
869 6
        if ($this->lifecycleCallbacks) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->lifecycleCallbacks of type array<mixed,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...
870
            $serialized[] = 'lifecycleCallbacks';
871
        }
872
873 6
        if ($this->entityListeners) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityListeners 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...
874 1
            $serialized[] = 'entityListeners';
875
        }
876
877 6
        if ($this->namedQueries) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->namedQueries 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...
878 1
            $serialized[] = 'namedQueries';
879
        }
880
881 6
        if ($this->namedNativeQueries) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->namedNativeQueries 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...
882
            $serialized[] = 'namedNativeQueries';
883
        }
884
885 6
        if ($this->sqlResultSetMappings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->sqlResultSetMappings 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...
886
            $serialized[] = 'sqlResultSetMappings';
887
        }
888
889 6
        if ($this->isReadOnly) {
890 1
            $serialized[] = 'isReadOnly';
891
        }
892
893 6
        if ($this->customGeneratorDefinition) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->customGeneratorDefinition 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...
894
            $serialized[] = "customGeneratorDefinition";
895
        }
896
897 6
        if ($this->cache) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cache 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...
898
            $serialized[] = 'cache';
899
        }
900
901 6
        return $serialized;
902
    }
903
904
    /**
905
     * Creates a new instance of the mapped class, without invoking the constructor.
906
     *
907
     * @return object
908
     */
909 717
    public function newInstance()
910
    {
911 717
        return $this->instantiator->instantiate($this->name);
0 ignored issues
show
Bug introduced by
The method instantiate() does not exist on null. ( Ignorable by Annotation )

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

911
        return $this->instantiator->/** @scrutinizer ignore-call */ instantiate($this->name);

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...
912
    }
913
914
    /**
915
     * Restores some state that can not be serialized/unserialized.
916
     *
917
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
918
     *
919
     * @return void
920
     */
921 2131
    public function wakeupReflection($reflService)
922
    {
923
        // Restore ReflectionClass and properties
924 2131
        $this->reflClass    = $reflService->getClass($this->name);
925 2131
        $this->instantiator = $this->instantiator ?: new Instantiator();
926
927 2131
        $parentReflFields = [];
928
929 2131
        foreach ($this->embeddedClasses as $property => $embeddedClass) {
930 23
            if (isset($embeddedClass['declaredField'])) {
931 15
                $parentReflFields[$property] = new ReflectionEmbeddedProperty(
932 15
                    $parentReflFields[$embeddedClass['declaredField']],
933 15
                    $reflService->getAccessibleProperty(
0 ignored issues
show
Bug introduced by
It seems like $reflService->getAccessi...Class['originalField']) can also be of type null; however, parameter $childProperty of Doctrine\ORM\Mapping\Ref...Property::__construct() does only seem to accept ReflectionProperty, 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

933
                    /** @scrutinizer ignore-type */ $reflService->getAccessibleProperty(
Loading history...
934 15
                        $this->embeddedClasses[$embeddedClass['declaredField']]['class'],
935 15
                        $embeddedClass['originalField']
936
                    ),
937 15
                    $this->embeddedClasses[$embeddedClass['declaredField']]['class']
938
                );
939
940 15
                continue;
941
            }
942
943 23
            $fieldRefl = $reflService->getAccessibleProperty(
944 23
                $embeddedClass['declared'] ?? $this->name,
945 23
                $property
946
            );
947
948 23
            $parentReflFields[$property] = $fieldRefl;
949 23
            $this->reflFields[$property] = $fieldRefl;
950
        }
951
952 2131
        foreach ($this->fieldMappings as $field => $mapping) {
953 2126
            if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
954 22
                $this->reflFields[$field] = new ReflectionEmbeddedProperty(
955 22
                    $parentReflFields[$mapping['declaredField']],
956 22
                    $reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField']),
957 22
                    $mapping['originalClass']
958
                );
959 22
                continue;
960
            }
961
962 2126
            $this->reflFields[$field] = isset($mapping['declared'])
963 546
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
964 2126
                : $reflService->getAccessibleProperty($this->name, $field);
965
        }
966
967 2131
        foreach ($this->associationMappings as $field => $mapping) {
968 1773
            $this->reflFields[$field] = isset($mapping['declared'])
969 437
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
970 1773
                : $reflService->getAccessibleProperty($this->name, $field);
971
        }
972 2131
    }
973
974
    /**
975
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
976
     * metadata of the class with the given name.
977
     *
978
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service.
979
     *
980
     * @return void
981
     */
982 696
    public function initializeReflection($reflService)
983
    {
984 696
        $this->reflClass = $reflService->getClass($this->name);
985 696
        $this->namespace = $reflService->getClassNamespace($this->name);
986
987 696
        if ($this->reflClass) {
988 689
            $this->name = $this->rootEntityName = $this->reflClass->getName();
989
        }
990
991 696
        $this->table['name'] = $this->namingStrategy->classToTableName($this->name);
992 696
    }
993
994
    /**
995
     * Validates Identifier.
996
     *
997
     * @return void
998
     *
999
     * @throws MappingException
1000
     */
1001 471
    public function validateIdentifier()
1002
    {
1003 471
        if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
1004 63
            return;
1005
        }
1006
1007
        // Verify & complete identifier mapping
1008 469
        if ( ! $this->identifier) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->identifier 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...
1009 6
            throw MappingException::identifierRequired($this->name);
1010
        }
1011
1012 463
        if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
1013
            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
1014
        }
1015 463
    }
1016
1017
    /**
1018
     * Validates association targets actually exist.
1019
     *
1020
     * @return void
1021
     *
1022
     * @throws MappingException
1023
     */
1024 472
    public function validateAssociations()
1025
    {
1026 472
        foreach ($this->associationMappings as $mapping) {
1027
            if (
1028 304
                ! class_exists($mapping['targetEntity'])
1029 304
                && ! interface_exists($mapping['targetEntity'])
1030 304
                && ! trait_exists($mapping['targetEntity'])
1031
            ) {
1032 304
                throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
1033
            }
1034
        }
1035 471
    }
1036
1037
    /**
1038
     * Validates lifecycle callbacks.
1039
     *
1040
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
1041
     *
1042
     * @return void
1043
     *
1044
     * @throws MappingException
1045
     */
1046 472
    public function validateLifecycleCallbacks($reflService)
1047
    {
1048 472
        foreach ($this->lifecycleCallbacks as $callbacks) {
1049 13
            foreach ($callbacks as $callbackFuncName) {
1050 13
                if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
1051 13
                    throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
1052
                }
1053
            }
1054
        }
1055 471
    }
1056
1057
    /**
1058
     * {@inheritDoc}
1059
     */
1060 611
    public function getReflectionClass()
1061
    {
1062 611
        return $this->reflClass;
1063
    }
1064
1065
    /**
1066
     * @param array $cache
1067
     *
1068
     * @return void
1069
     */
1070 25
    public function enableCache(array $cache)
1071
    {
1072 25
        if ( ! isset($cache['usage'])) {
1073
            $cache['usage'] = self::CACHE_USAGE_READ_ONLY;
1074
        }
1075
1076 25
        if ( ! isset($cache['region'])) {
1077 25
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName));
1078
        }
1079
1080 25
        $this->cache = $cache;
1081 25
    }
1082
1083
    /**
1084
     * @param string $fieldName
1085
     * @param array  $cache
1086
     *
1087
     * @return void
1088
     */
1089 2
    public function enableAssociationCache($fieldName, array $cache)
1090
    {
1091 2
        $this->associationMappings[$fieldName]['cache'] = $this->getAssociationCacheDefaults($fieldName, $cache);
1092 2
    }
1093
1094
    /**
1095
     * @param string $fieldName
1096
     * @param array  $cache
1097
     *
1098
     * @return array
1099
     */
1100 19
    public function getAssociationCacheDefaults($fieldName, array $cache)
1101
    {
1102 19
        if ( ! isset($cache['usage'])) {
1103 1
            $cache['usage'] = isset($this->cache['usage'])
1104 1
                ? $this->cache['usage']
1105
                : self::CACHE_USAGE_READ_ONLY;
1106
        }
1107
1108 19
        if ( ! isset($cache['region'])) {
1109 19
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName;
1110
        }
1111
1112 19
        return $cache;
1113
    }
1114
1115
    /**
1116
     * Sets the change tracking policy used by this class.
1117
     *
1118
     * @param integer $policy
1119
     *
1120
     * @return void
1121
     */
1122 164
    public function setChangeTrackingPolicy($policy)
1123
    {
1124 164
        $this->changeTrackingPolicy = $policy;
1125 164
    }
1126
1127
    /**
1128
     * Whether the change tracking policy of this class is "deferred explicit".
1129
     *
1130
     * @return boolean
1131
     */
1132 275
    public function isChangeTrackingDeferredExplicit()
1133
    {
1134 275
        return self::CHANGETRACKING_DEFERRED_EXPLICIT === $this->changeTrackingPolicy;
1135
    }
1136
1137
    /**
1138
     * Whether the change tracking policy of this class is "deferred implicit".
1139
     *
1140
     * @return boolean
1141
     */
1142 485
    public function isChangeTrackingDeferredImplicit()
1143
    {
1144 485
        return self::CHANGETRACKING_DEFERRED_IMPLICIT === $this->changeTrackingPolicy;
1145
    }
1146
1147
    /**
1148
     * Whether the change tracking policy of this class is "notify".
1149
     *
1150
     * @return boolean
1151
     */
1152 313
    public function isChangeTrackingNotify()
1153
    {
1154 313
        return self::CHANGETRACKING_NOTIFY === $this->changeTrackingPolicy;
1155
    }
1156
1157
    /**
1158
     * Checks whether a field is part of the identifier/primary key field(s).
1159
     *
1160
     * @param string $fieldName The field name.
1161
     *
1162
     * @return boolean TRUE if the field is part of the table identifier/primary key field(s),
1163
     *                 FALSE otherwise.
1164
     */
1165 1153
    public function isIdentifier($fieldName)
1166
    {
1167 1153
        if ( ! $this->identifier) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->identifier 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...
1168 1
            return false;
1169
        }
1170
1171 1152
        if ( ! $this->isIdentifierComposite) {
1172 1147
            return $fieldName === $this->identifier[0];
1173
        }
1174
1175 99
        return in_array($fieldName, $this->identifier, true);
1176
    }
1177
1178
    /**
1179
     * Checks if the field is unique.
1180
     *
1181
     * @param string $fieldName The field name.
1182
     *
1183
     * @return boolean TRUE if the field is unique, FALSE otherwise.
1184
     */
1185
    public function isUniqueField($fieldName)
1186
    {
1187
        $mapping = $this->getFieldMapping($fieldName);
1188
1189
        return false !== $mapping && isset($mapping['unique']) && $mapping['unique'];
1190
    }
1191
1192
    /**
1193
     * Checks if the field is not null.
1194
     *
1195
     * @param string $fieldName The field name.
1196
     *
1197
     * @return boolean TRUE if the field is not null, FALSE otherwise.
1198
     */
1199 1
    public function isNullable($fieldName)
1200
    {
1201 1
        $mapping = $this->getFieldMapping($fieldName);
1202
1203 1
        return false !== $mapping && isset($mapping['nullable']) && $mapping['nullable'];
1204
    }
1205
1206
    /**
1207
     * Gets a column name for a field name.
1208
     * If the column name for the field cannot be found, the given field name
1209
     * is returned.
1210
     *
1211
     * @param string $fieldName The field name.
1212
     *
1213
     * @return string The column name.
1214
     */
1215 16
    public function getColumnName($fieldName)
1216
    {
1217 16
        return isset($this->columnNames[$fieldName])
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ORM\Mapping\Cla...adataInfo::$columnNames has been deprecated: 3.0 Remove this. ( Ignorable by Annotation )

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

1217
        return isset(/** @scrutinizer ignore-deprecated */ $this->columnNames[$fieldName])

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1218 16
            ? $this->columnNames[$fieldName]
1219 16
            : $fieldName;
1220
    }
1221
1222
    /**
1223
     * Gets the mapping of a (regular) field that holds some data but not a
1224
     * reference to another object.
1225
     *
1226
     * @param string $fieldName The field name.
1227
     *
1228
     * @return array The field mapping.
1229
     *
1230
     * @throws MappingException
1231
     */
1232 211
    public function getFieldMapping($fieldName)
1233
    {
1234 211
        if ( ! isset($this->fieldMappings[$fieldName])) {
1235 1
            throw MappingException::mappingNotFound($this->name, $fieldName);
1236
        }
1237
1238 210
        return $this->fieldMappings[$fieldName];
1239
    }
1240
1241
    /**
1242
     * Gets the mapping of an association.
1243
     *
1244
     * @see ClassMetadataInfo::$associationMappings
1245
     *
1246
     * @param string $fieldName The field name that represents the association in
1247
     *                          the object model.
1248
     *
1249
     * @return array The mapping.
1250
     *
1251
     * @throws MappingException
1252
     */
1253 501
    public function getAssociationMapping($fieldName)
1254
    {
1255 501
        if ( ! isset($this->associationMappings[$fieldName])) {
1256
            throw MappingException::mappingNotFound($this->name, $fieldName);
1257
        }
1258
1259 501
        return $this->associationMappings[$fieldName];
1260
    }
1261
1262
    /**
1263
     * Gets all association mappings of the class.
1264
     *
1265
     * @return array
1266
     */
1267
    public function getAssociationMappings()
1268
    {
1269
        return $this->associationMappings;
1270
    }
1271
1272
    /**
1273
     * Gets the field name for a column name.
1274
     * If no field name can be found the column name is returned.
1275
     *
1276
     * @param string $columnName The column name.
1277
     *
1278
     * @return string The column alias.
1279
     */
1280 256
    public function getFieldName($columnName)
1281
    {
1282 256
        return isset($this->fieldNames[$columnName])
1283 256
            ? $this->fieldNames[$columnName]
1284 256
            : $columnName;
1285
    }
1286
1287
    /**
1288
     * Gets the named query.
1289
     *
1290
     * @see ClassMetadataInfo::$namedQueries
1291
     *
1292
     * @param string $queryName The query name.
1293
     *
1294
     * @return string
1295
     *
1296
     * @throws MappingException
1297
     */
1298 4
    public function getNamedQuery($queryName)
1299
    {
1300 4
        if ( ! isset($this->namedQueries[$queryName])) {
1301 1
            throw MappingException::queryNotFound($this->name, $queryName);
1302
        }
1303
1304 3
        return $this->namedQueries[$queryName]['dql'];
1305
    }
1306
1307
    /**
1308
     * Gets all named queries of the class.
1309
     *
1310
     * @return array
1311
     */
1312 7
    public function getNamedQueries()
1313
    {
1314 7
        return $this->namedQueries;
1315
    }
1316
1317
    /**
1318
     * Gets the named native query.
1319
     *
1320
     * @see ClassMetadataInfo::$namedNativeQueries
1321
     *
1322
     * @param string $queryName The query name.
1323
     *
1324
     * @return array
1325
     *
1326
     * @throws MappingException
1327
     */
1328 17
    public function getNamedNativeQuery($queryName)
1329
    {
1330 17
        if ( ! isset($this->namedNativeQueries[$queryName])) {
1331
            throw MappingException::queryNotFound($this->name, $queryName);
1332
        }
1333
1334 17
        return $this->namedNativeQueries[$queryName];
1335
    }
1336
1337
    /**
1338
     * Gets all named native queries of the class.
1339
     *
1340
     * @return array
1341
     */
1342 2
    public function getNamedNativeQueries()
1343
    {
1344 2
        return $this->namedNativeQueries;
1345
    }
1346
1347
    /**
1348
     * Gets the result set mapping.
1349
     *
1350
     * @see ClassMetadataInfo::$sqlResultSetMappings
1351
     *
1352
     * @param string $name The result set mapping name.
1353
     *
1354
     * @return array
1355
     *
1356
     * @throws MappingException
1357
     */
1358 21
    public function getSqlResultSetMapping($name)
1359
    {
1360 21
        if ( ! isset($this->sqlResultSetMappings[$name])) {
1361
            throw MappingException::resultMappingNotFound($this->name, $name);
1362
        }
1363
1364 21
        return $this->sqlResultSetMappings[$name];
1365
    }
1366
1367
    /**
1368
     * Gets all sql result set mappings of the class.
1369
     *
1370
     * @return array
1371
     */
1372 8
    public function getSqlResultSetMappings()
1373
    {
1374 8
        return $this->sqlResultSetMappings;
1375
    }
1376
1377
    /**
1378
     * Validates & completes the given field mapping.
1379
     *
1380
     * @param array $mapping The field mapping to validate & complete.
1381
     *
1382
     * @return void
1383
     *
1384
     * @throws MappingException
1385
     */
1386 619
    protected function _validateAndCompleteFieldMapping(array &$mapping)
1387
    {
1388
        // Check mandatory fields
1389 619
        if ( ! isset($mapping['fieldName']) || !$mapping['fieldName']) {
1390 1
            throw MappingException::missingFieldName($this->name);
1391
        }
1392
1393 618
        if ( ! isset($mapping['type'])) {
1394
            // Default to string
1395 65
            $mapping['type'] = 'string';
1396
        }
1397
1398
        // Complete fieldName and columnName mapping
1399 618
        if ( ! isset($mapping['columnName'])) {
1400 519
            $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name);
1401
        }
1402
1403 618
        if ('`' === $mapping['columnName'][0]) {
1404 12
            $mapping['columnName']  = trim($mapping['columnName'], '`');
0 ignored issues
show
Bug introduced by
$mapping['columnName'] of type array is incompatible with the type string expected by parameter $str of trim(). ( Ignorable by Annotation )

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

1404
            $mapping['columnName']  = trim(/** @scrutinizer ignore-type */ $mapping['columnName'], '`');
Loading history...
1405 12
            $mapping['quoted']      = true;
1406
        }
1407
1408 618
        $this->columnNames[$mapping['fieldName']] = $mapping['columnName'];
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ORM\Mapping\Cla...adataInfo::$columnNames has been deprecated: 3.0 Remove this. ( Ignorable by Annotation )

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

1408
        /** @scrutinizer ignore-deprecated */ $this->columnNames[$mapping['fieldName']] = $mapping['columnName'];

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1409
1410 618
        if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn && $this->discriminatorColumn['name'] === $mapping['columnName'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->discriminatorColumn 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...
1411 2
            throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);
1412
        }
1413
1414 617
        $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
1415
1416
        // Complete id mapping
1417 617
        if (isset($mapping['id']) && true === $mapping['id']) {
1418 578
            if ($this->versionField == $mapping['fieldName']) {
1419
                throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
1420
            }
1421
1422 578
            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1423 578
                $this->identifier[] = $mapping['fieldName'];
1424
            }
1425
1426
            // Check for composite key
1427 578
            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1428 24
                $this->isIdentifierComposite = true;
1429
            }
1430
        }
1431
1432 617
        if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
1433 5
            if (isset($mapping['id']) && true === $mapping['id']) {
1434
                 throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
1435
            }
1436
1437 5
            $mapping['requireSQLConversion'] = true;
1438
        }
1439 617
    }
1440
1441
    /**
1442
     * Validates & completes the basic mapping information that is common to all
1443
     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
1444
     *
1445
     * @param array $mapping The mapping.
1446
     *
1447
     * @return array The updated mapping.
1448
     *
1449
     * @throws MappingException If something is wrong with the mapping.
1450
     */
1451 395
    protected function _validateAndCompleteAssociationMapping(array $mapping)
1452
    {
1453 395
        if ( ! isset($mapping['mappedBy'])) {
1454 379
            $mapping['mappedBy'] = null;
1455
        }
1456
1457 395
        if ( ! isset($mapping['inversedBy'])) {
1458 365
            $mapping['inversedBy'] = null;
1459
        }
1460
1461 395
        $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
1462
1463 395
        if (empty($mapping['indexBy'])) {
1464 392
            unset($mapping['indexBy']);
1465
        }
1466
1467
        // If targetEntity is unqualified, assume it is in the same namespace as
1468
        // the sourceEntity.
1469 395
        $mapping['sourceEntity'] = $this->name;
1470
1471 395
        if (isset($mapping['targetEntity'])) {
1472 395
            $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']);
1473 395
            $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
1474
        }
1475
1476 395
        if (($mapping['type'] & self::MANY_TO_ONE) > 0 && isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) {
1477 1
            throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
1478
        }
1479
1480
        // Complete id mapping
1481 394
        if (isset($mapping['id']) && true === $mapping['id']) {
1482 59
            if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) {
1483 1
                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
1484
            }
1485
1486 58
            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1487 58
                if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) >= 2) {
1488
                    throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
1489
                        $mapping['targetEntity'], $this->name, $mapping['fieldName']
1490
                    );
1491
                }
1492
1493 58
                $this->identifier[] = $mapping['fieldName'];
1494 58
                $this->containsForeignIdentifier = true;
1495
            }
1496
1497
            // Check for composite key
1498 58
            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1499 28
                $this->isIdentifierComposite = true;
1500
            }
1501
1502 58
            if ($this->cache && !isset($mapping['cache'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cache 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...
1503 3
                throw CacheException::nonCacheableEntityAssociation($this->name, $mapping['fieldName']);
1504
            }
1505
        }
1506
1507
        // Mandatory attributes for both sides
1508
        // Mandatory: fieldName, targetEntity
1509 390
        if ( ! isset($mapping['fieldName']) || !$mapping['fieldName']) {
1510
            throw MappingException::missingFieldName($this->name);
1511
        }
1512
1513 390
        if ( ! isset($mapping['targetEntity'])) {
1514
            throw MappingException::missingTargetEntity($mapping['fieldName']);
1515
        }
1516
1517
        // Mandatory and optional attributes for either side
1518 390
        if ( ! $mapping['mappedBy']) {
1519 374
            if (isset($mapping['joinTable']) && $mapping['joinTable']) {
1520 140
                if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') {
1521 4
                    $mapping['joinTable']['name']   = trim($mapping['joinTable']['name'], '`');
1522 374
                    $mapping['joinTable']['quoted'] = true;
1523
                }
1524
            }
1525
        } else {
1526 209
            $mapping['isOwningSide'] = false;
1527
        }
1528
1529 390
        if (isset($mapping['id']) && true === $mapping['id'] && $mapping['type'] & self::TO_MANY) {
1530 3
            throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']);
1531
        }
1532
1533
        // Fetch mode. Default fetch mode to LAZY, if not set.
1534 387
        if ( ! isset($mapping['fetch'])) {
1535 107
            $mapping['fetch'] = self::FETCH_LAZY;
1536
        }
1537
1538
        // Cascades
1539 387
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : [];
1540
1541 387
        $allCascades = ['remove', 'persist', 'refresh', 'merge', 'detach'];
1542 387
        if (in_array('all', $cascades)) {
1543 44
            $cascades = $allCascades;
1544 380
        } elseif (count($cascades) !== count(array_intersect($cascades, $allCascades))) {
1545 1
            throw MappingException::invalidCascadeOption(
1546 1
                array_diff($cascades, $allCascades),
1547 1
                $this->name,
1548 1
                $mapping['fieldName']
1549
            );
1550
        }
1551
1552 386
        $mapping['cascade'] = $cascades;
1553 386
        $mapping['isCascadeRemove']  = in_array('remove', $cascades);
1554 386
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1555 386
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1556 386
        $mapping['isCascadeMerge']   = in_array('merge', $cascades);
1557 386
        $mapping['isCascadeDetach']  = in_array('detach', $cascades);
1558
1559 386
        return $mapping;
1560
    }
1561
1562
    /**
1563
     * Validates & completes a one-to-one association mapping.
1564
     *
1565
     * @param array $mapping The mapping to validate & complete.
1566
     *
1567
     * @return array The validated & completed mapping.
1568
     *
1569
     * @throws RuntimeException
1570
     * @throws MappingException
1571
     */
1572 335
    protected function _validateAndCompleteOneToOneMapping(array $mapping)
1573
    {
1574 335
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1575
1576 329
        if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
1577 235
            $mapping['isOwningSide'] = true;
1578
        }
1579
1580 329
        if ($mapping['isOwningSide']) {
1581 316
            if (empty($mapping['joinColumns'])) {
1582
                // Apply default join column
1583 102
                $mapping['joinColumns'] = [
1584
                    [
1585 102
                        'name' => $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name),
0 ignored issues
show
Unused Code introduced by
The call to Doctrine\ORM\Mapping\Nam...ategy::joinColumnName() has too many arguments starting with $this->name. ( Ignorable by Annotation )

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

1585
                        'name' => $this->namingStrategy->/** @scrutinizer ignore-call */ joinColumnName($mapping['fieldName'], $this->name),

This check compares calls to functions or methods with their respective definitions. If the call has more 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...
1586 102
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName()
1587
                    ]
1588
                ];
1589
            }
1590
1591 316
            $uniqueConstraintColumns = [];
1592
1593 316
            foreach ($mapping['joinColumns'] as &$joinColumn) {
1594 316
                if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
1595 169
                    if (count($mapping['joinColumns']) === 1) {
1596 167
                        if (empty($mapping['id'])) {
1597 167
                            $joinColumn['unique'] = true;
1598
                        }
1599
                    } else {
1600 2
                        $uniqueConstraintColumns[] = $joinColumn['name'];
1601
                    }
1602
                }
1603
1604 316
                if (empty($joinColumn['name'])) {
1605 36
                    $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name);
1606
                }
1607
1608 316
                if (empty($joinColumn['referencedColumnName'])) {
1609 6
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1610
                }
1611
1612 316
                if ($joinColumn['name'][0] === '`') {
1613 8
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1614 8
                    $joinColumn['quoted'] = true;
1615
                }
1616
1617 316
                if ($joinColumn['referencedColumnName'][0] === '`') {
1618 5
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1619 5
                    $joinColumn['quoted']               = true;
1620
                }
1621
1622 316
                $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1623 316
                $mapping['joinColumnFieldNames'][$joinColumn['name']] = $joinColumn['fieldName'] ?? $joinColumn['name'];
1624
            }
1625
1626 316
            if ($uniqueConstraintColumns) {
1627 2
                if ( ! $this->table) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->table 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...
1628
                    throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
1629
                }
1630
1631 2
                $this->table['uniqueConstraints'][$mapping['fieldName'] . "_uniq"] = [
1632 2
                    'columns' => $uniqueConstraintColumns
1633
                ];
1634
            }
1635
1636 316
            $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
1637
        }
1638
1639 329
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'];
1640 329
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove'];
1641
1642 329
        if ($mapping['orphanRemoval']) {
1643 22
            unset($mapping['unique']);
1644
        }
1645
1646 329
        if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
1647 2
            throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']);
1648
        }
1649
1650 327
        return $mapping;
1651
    }
1652
1653
    /**
1654
     * Validates & completes a one-to-many association mapping.
1655
     *
1656
     * @param array $mapping The mapping to validate and complete.
1657
     *
1658
     * @return array The validated and completed mapping.
1659
     *
1660
     * @throws MappingException
1661
     * @throws InvalidArgumentException
1662
     */
1663 150
    protected function _validateAndCompleteOneToManyMapping(array $mapping)
1664
    {
1665 150
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1666
1667
        // OneToMany-side MUST be inverse (must have mappedBy)
1668 149
        if ( ! isset($mapping['mappedBy'])) {
1669
            throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
1670
        }
1671
1672 149
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'];
1673 149
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove'];
1674
1675 149
        $this->assertMappingOrderBy($mapping);
1676
1677 149
        return $mapping;
1678
    }
1679
1680
    /**
1681
     * Validates & completes a many-to-many association mapping.
1682
     *
1683
     * @param array $mapping The mapping to validate & complete.
1684
     *
1685
     * @return array The validated & completed mapping.
1686
     *
1687
     * @throws \InvalidArgumentException
1688
     */
1689 171
    protected function _validateAndCompleteManyToManyMapping(array $mapping)
1690
    {
1691 171
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1692
1693 169
        if ($mapping['isOwningSide']) {
1694
            // owning side MUST have a join table
1695 151
            if ( ! isset($mapping['joinTable']['name'])) {
1696 29
                $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']);
1697
            }
1698
1699 151
            $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity']
1700 151
                && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns'])));
1701
1702 151
            if ( ! isset($mapping['joinTable']['joinColumns'])) {
1703 28
                $mapping['joinTable']['joinColumns'] = [
1704
                    [
1705 28
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null),
1706 28
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1707 28
                        'onDelete' => 'CASCADE'
1708
                    ]
1709
                ];
1710
            }
1711
1712 151
            if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
1713 29
                $mapping['joinTable']['inverseJoinColumns'] = [
1714
                    [
1715 29
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null),
1716 29
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1717 29
                        'onDelete' => 'CASCADE'
1718
                    ]
1719
                ];
1720
            }
1721
1722 151
            $mapping['joinTableColumns'] = [];
1723
1724 151
            foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
1725 151
                if (empty($joinColumn['name'])) {
1726 2
                    $joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']);
1727
                }
1728
1729 151
                if (empty($joinColumn['referencedColumnName'])) {
1730 6
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1731
                }
1732
1733 151
                if ($joinColumn['name'][0] === '`') {
1734 3
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1735 3
                    $joinColumn['quoted'] = true;
1736
                }
1737
1738 151
                if ($joinColumn['referencedColumnName'][0] === '`') {
1739 3
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1740 3
                    $joinColumn['quoted']               = true;
1741
                }
1742
1743 151
                if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
1744 39
                    $mapping['isOnDeleteCascade'] = true;
1745
                }
1746
1747 151
                $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1748 151
                $mapping['joinTableColumns'][] = $joinColumn['name'];
1749
            }
1750
1751 151
            foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
1752 151
                if (empty($inverseJoinColumn['name'])) {
1753 2
                    $inverseJoinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']);
1754
                }
1755
1756 151
                if (empty($inverseJoinColumn['referencedColumnName'])) {
1757 6
                    $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1758
                }
1759
1760 151
                if ($inverseJoinColumn['name'][0] === '`') {
1761 3
                    $inverseJoinColumn['name']   = trim($inverseJoinColumn['name'], '`');
1762 3
                    $inverseJoinColumn['quoted'] = true;
1763
                }
1764
1765 151
                if ($inverseJoinColumn['referencedColumnName'][0] === '`') {
1766 3
                    $inverseJoinColumn['referencedColumnName']  = trim($inverseJoinColumn['referencedColumnName'], '`');
1767 3
                    $inverseJoinColumn['quoted']                = true;
1768
                }
1769
1770 151
                if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
1771 35
                    $mapping['isOnDeleteCascade'] = true;
1772
                }
1773
1774 151
                $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
1775 151
                $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
1776
            }
1777
        }
1778
1779 169
        $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'];
1780
1781 169
        $this->assertMappingOrderBy($mapping);
1782
1783 169
        return $mapping;
1784
    }
1785
1786
    /**
1787
     * {@inheritDoc}
1788
     */
1789 645
    public function getIdentifierFieldNames()
1790
    {
1791 645
        return $this->identifier;
1792
    }
1793
1794
    /**
1795
     * Gets the name of the single id field. Note that this only works on
1796
     * entity classes that have a single-field pk.
1797
     *
1798
     * @return string
1799
     *
1800
     * @throws MappingException If the class doesn't have an identifier or it has a composite primary key.
1801
     */
1802 1215
    public function getSingleIdentifierFieldName()
1803
    {
1804 1215
        if ($this->isIdentifierComposite) {
1805 1
            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
1806
        }
1807
1808 1214
        if ( ! isset($this->identifier[0])) {
1809 1
            throw MappingException::noIdDefined($this->name);
1810
        }
1811
1812 1213
        return $this->identifier[0];
1813
    }
1814
1815
    /**
1816
     * Gets the column name of the single id column. Note that this only works on
1817
     * entity classes that have a single-field pk.
1818
     *
1819
     * @return string
1820
     *
1821
     * @throws MappingException If the class doesn't have an identifier or it has a composite primary key.
1822
     */
1823 3
    public function getSingleIdentifierColumnName()
1824
    {
1825 3
        return $this->getColumnName($this->getSingleIdentifierFieldName());
1826
    }
1827
1828
    /**
1829
     * INTERNAL:
1830
     * Sets the mapped identifier/primary key fields of this class.
1831
     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1832
     *
1833
     * @param array $identifier
1834
     *
1835
     * @return void
1836
     */
1837 147
    public function setIdentifier(array $identifier)
1838
    {
1839 147
        $this->identifier = $identifier;
1840 147
        $this->isIdentifierComposite = (count($this->identifier) > 1);
1841 147
    }
1842
1843
    /**
1844
     * {@inheritDoc}
1845
     */
1846 65
    public function getIdentifier()
1847
    {
1848 65
        return $this->identifier;
1849
    }
1850
1851
    /**
1852
     * {@inheritDoc}
1853
     */
1854 313
    public function hasField($fieldName)
1855
    {
1856 313
        return isset($this->fieldMappings[$fieldName]) || isset($this->embeddedClasses[$fieldName]);
1857
    }
1858
1859
    /**
1860
     * Gets an array containing all the column names.
1861
     *
1862
     * @param array|null $fieldNames
1863
     *
1864
     * @return array
1865
     */
1866 49
    public function getColumnNames(array $fieldNames = null)
1867
    {
1868 49
        if (null === $fieldNames) {
1869 48
            return array_keys($this->fieldNames);
1870
        }
1871
1872 1
        return array_values(array_map([$this, 'getColumnName'], $fieldNames));
1873
    }
1874
1875
    /**
1876
     * Returns an array with all the identifier column names.
1877
     *
1878
     * @return array
1879
     */
1880 348
    public function getIdentifierColumnNames()
1881
    {
1882 348
        $columnNames = [];
1883
1884 348
        foreach ($this->identifier as $idProperty) {
1885 348
            if (isset($this->fieldMappings[$idProperty])) {
1886 343
                $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
1887
1888 343
                continue;
1889
            }
1890
1891
            // Association defined as Id field
1892 23
            $joinColumns      = $this->associationMappings[$idProperty]['joinColumns'];
1893
            $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
1894
1895 23
            $columnNames = array_merge($columnNames, $assocColumnNames);
1896
        }
1897
1898 348
        return $columnNames;
1899
    }
1900
1901
    /**
1902
     * Sets the type of Id generator to use for the mapped class.
1903
     *
1904
     * @param int $generatorType
1905
     *
1906
     * @return void
1907
     */
1908 529
    public function setIdGeneratorType($generatorType)
1909
    {
1910 529
        $this->generatorType = $generatorType;
1911 529
    }
1912
1913
    /**
1914
     * Checks whether the mapped class uses an Id generator.
1915
     *
1916
     * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
1917
     */
1918 463
    public function usesIdGenerator()
1919
    {
1920 463
        return $this->generatorType != self::GENERATOR_TYPE_NONE;
1921
    }
1922
1923
    /**
1924
     * @return boolean
1925
     */
1926 1435
    public function isInheritanceTypeNone()
1927
    {
1928 1435
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1929
    }
1930
1931
    /**
1932
     * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
1933
     *
1934
     * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
1935
     *                 FALSE otherwise.
1936
     */
1937 1117
    public function isInheritanceTypeJoined()
1938
    {
1939 1117
        return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
1940
    }
1941
1942
    /**
1943
     * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
1944
     *
1945
     * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
1946
     *                 FALSE otherwise.
1947
     */
1948 1320
    public function isInheritanceTypeSingleTable()
1949
    {
1950 1320
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
1951
    }
1952
1953
    /**
1954
     * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
1955
     *
1956
     * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
1957
     *                 FALSE otherwise.
1958
     */
1959 281
    public function isInheritanceTypeTablePerClass()
1960
    {
1961 281
        return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
1962
    }
1963
1964
    /**
1965
     * Checks whether the class uses an identity column for the Id generation.
1966
     *
1967
     * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
1968
     */
1969 1143
    public function isIdGeneratorIdentity()
1970
    {
1971 1143
        return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
1972
    }
1973
1974
    /**
1975
     * Checks whether the class uses a sequence for id generation.
1976
     *
1977
     * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
1978
     */
1979 353
    public function isIdGeneratorSequence()
1980
    {
1981 353
        return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
1982
    }
1983
1984
    /**
1985
     * Checks whether the class uses a table for id generation.
1986
     *
1987
     * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
1988
     */
1989 101
    public function isIdGeneratorTable()
1990
    {
1991 101
        return $this->generatorType == self::GENERATOR_TYPE_TABLE;
1992
    }
1993
1994
    /**
1995
     * Checks whether the class has a natural identifier/pk (which means it does
1996
     * not use any Id generator.
1997
     *
1998
     * @return boolean
1999
     */
2000 75
    public function isIdentifierNatural()
2001
    {
2002 75
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
2003
    }
2004
2005
    /**
2006
     * Checks whether the class use a UUID for id generation.
2007
     *
2008
     * @return boolean
2009
     */
2010
    public function isIdentifierUuid()
2011
    {
2012
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
2013
    }
2014
2015
    /**
2016
     * Gets the type of a field.
2017
     *
2018
     * @param string $fieldName
2019
     *
2020
     * @return \Doctrine\DBAL\Types\Type|string|null
2021
     *
2022
     * @todo 3.0 Remove this. PersisterHelper should fix it somehow
2023
     */
2024 979
    public function getTypeOfField($fieldName)
2025
    {
2026 979
        return isset($this->fieldMappings[$fieldName])
2027 979
            ? $this->fieldMappings[$fieldName]['type']
2028 979
            : null;
2029
    }
2030
2031
    /**
2032
     * Gets the type of a column.
2033
     *
2034
     * @param string $columnName
2035
     *
2036
     * @return \Doctrine\DBAL\Types\Type|string|null
2037
     *
2038
     * @deprecated 3.0 remove this. this method is bogus and unreliable, since it cannot resolve the type of a column
2039
     *             that is derived by a referenced field on a different entity.
2040
     */
2041
    public function getTypeOfColumn($columnName)
2042
    {
2043
        return $this->getTypeOfField($this->getFieldName($columnName));
2044
    }
2045
2046
    /**
2047
     * Gets the name of the primary table.
2048
     *
2049
     * @return string
2050
     */
2051 1429
    public function getTableName()
2052
    {
2053 1429
        return $this->table['name'];
2054
    }
2055
2056
    /**
2057
     * Gets primary table's schema name.
2058
     *
2059
     * @return string|null
2060
     */
2061 13
    public function getSchemaName()
2062
    {
2063 13
        return isset($this->table['schema']) ? $this->table['schema'] : null;
2064
    }
2065
2066
    /**
2067
     * Gets the table name to use for temporary identifier tables of this class.
2068
     *
2069
     * @return string
2070
     */
2071 7
    public function getTemporaryIdTableName()
2072
    {
2073
        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
2074 7
        return str_replace('.', '_', $this->getTableName() . '_id_tmp');
2075
    }
2076
2077
    /**
2078
     * Sets the mapped subclasses of this class.
2079
     *
2080
     * @param array $subclasses The names of all mapped subclasses.
2081
     *
2082
     * @return void
2083
     */
2084 2
    public function setSubclasses(array $subclasses)
2085
    {
2086 2
        foreach ($subclasses as $subclass) {
2087 2
            $this->subClasses[] = $this->fullyQualifiedClassName($subclass);
2088
        }
2089 2
    }
2090
2091
    /**
2092
     * Sets the parent class names.
2093
     * Assumes that the class names in the passed array are in the order:
2094
     * directParent -> directParentParent -> directParentParentParent ... -> root.
2095
     *
2096
     * @param array $classNames
2097
     *
2098
     * @return void
2099
     */
2100 480
    public function setParentClasses(array $classNames)
2101
    {
2102 480
        $this->parentClasses = $classNames;
2103
2104 480
        if (count($classNames) > 0) {
2105 100
            $this->rootEntityName = array_pop($classNames);
2106
        }
2107 480
    }
2108
2109
    /**
2110
     * Sets the inheritance type used by the class and its subclasses.
2111
     *
2112
     * @param integer $type
2113
     *
2114
     * @return void
2115
     *
2116
     * @throws MappingException
2117
     */
2118 197
    public function setInheritanceType($type)
2119
    {
2120 197
        if ( ! $this->_isInheritanceType($type)) {
2121
            throw MappingException::invalidInheritanceType($this->name, $type);
2122
        }
2123
2124 197
        $this->inheritanceType = $type;
2125 197
    }
2126
2127
    /**
2128
     * Sets the association to override association mapping of property for an entity relationship.
2129
     *
2130
     * @param string $fieldName
2131
     * @param array  $overrideMapping
2132
     *
2133
     * @return void
2134
     *
2135
     * @throws MappingException
2136
     */
2137 26
    public function setAssociationOverride($fieldName, array $overrideMapping)
2138
    {
2139 26
        if ( ! isset($this->associationMappings[$fieldName])) {
2140 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2141
        }
2142
2143 25
        $mapping = $this->associationMappings[$fieldName];
2144
2145 25
        if (isset($overrideMapping['joinColumns'])) {
2146 13
            $mapping['joinColumns'] = $overrideMapping['joinColumns'];
2147
        }
2148
2149 25
        if (isset($overrideMapping['inversedBy'])) {
2150 6
            $mapping['inversedBy'] = $overrideMapping['inversedBy'];
2151
        }
2152
2153 25
        if (isset($overrideMapping['joinTable'])) {
2154 12
            $mapping['joinTable'] = $overrideMapping['joinTable'];
2155
        }
2156
2157 25
        if (isset($overrideMapping['fetch'])) {
2158 6
            $mapping['fetch'] = $overrideMapping['fetch'];
2159
        }
2160
2161 25
        $mapping['joinColumnFieldNames']        = null;
2162 25
        $mapping['joinTableColumns']            = null;
2163 25
        $mapping['sourceToTargetKeyColumns']    = null;
2164 25
        $mapping['relationToSourceKeyColumns']  = null;
2165 25
        $mapping['relationToTargetKeyColumns']  = null;
2166
2167 25
        switch ($mapping['type']) {
2168 25
            case self::ONE_TO_ONE:
2169 1
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2170 1
                break;
2171 24
            case self::ONE_TO_MANY:
2172
                $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2173
                break;
2174 24
            case self::MANY_TO_ONE:
2175 12
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2176 12
                break;
2177 24
            case self::MANY_TO_MANY:
2178 24
                $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2179 24
                break;
2180
        }
2181
2182 25
        $this->associationMappings[$fieldName] = $mapping;
2183 25
    }
2184
2185
    /**
2186
     * Sets the override for a mapped field.
2187
     *
2188
     * @param string $fieldName
2189
     * @param array  $overrideMapping
2190
     *
2191
     * @return void
2192
     *
2193
     * @throws MappingException
2194
     */
2195 15
    public function setAttributeOverride($fieldName, array $overrideMapping)
2196
    {
2197 15
        if ( ! isset($this->fieldMappings[$fieldName])) {
2198 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2199
        }
2200
2201 14
        $mapping = $this->fieldMappings[$fieldName];
2202
2203 14
        if (isset($mapping['id'])) {
2204 12
            $overrideMapping['id'] = $mapping['id'];
2205
        }
2206
2207 14
        if ( ! isset($overrideMapping['type'])) {
2208 6
            $overrideMapping['type'] = $mapping['type'];
2209
        }
2210
2211 14
        if ( ! isset($overrideMapping['fieldName'])) {
2212 5
            $overrideMapping['fieldName'] = $mapping['fieldName'];
2213
        }
2214
2215 14
        if ($overrideMapping['type'] !== $mapping['type']) {
2216 1
            throw MappingException::invalidOverrideFieldType($this->name, $fieldName);
2217
        }
2218
2219 13
        unset($this->fieldMappings[$fieldName]);
2220 13
        unset($this->fieldNames[$mapping['columnName']]);
2221 13
        unset($this->columnNames[$mapping['fieldName']]);
2222
2223 13
        $this->_validateAndCompleteFieldMapping($overrideMapping);
2224
2225 13
        $this->fieldMappings[$fieldName] = $overrideMapping;
2226 13
    }
2227
2228
    /**
2229
     * Checks whether a mapped field is inherited from an entity superclass.
2230
     *
2231
     * @param string $fieldName
2232
     *
2233
     * @return bool TRUE if the field is inherited, FALSE otherwise.
2234
     */
2235 449
    public function isInheritedField($fieldName)
2236
    {
2237 449
        return isset($this->fieldMappings[$fieldName]['inherited']);
2238
    }
2239
2240
    /**
2241
     * Checks if this entity is the root in any entity-inheritance-hierarchy.
2242
     *
2243
     * @return bool
2244
     */
2245 479
    public function isRootEntity()
2246
    {
2247 479
        return $this->name == $this->rootEntityName;
2248
    }
2249
2250
    /**
2251
     * Checks whether a mapped association field is inherited from a superclass.
2252
     *
2253
     * @param string $fieldName
2254
     *
2255
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
2256
     */
2257 414
    public function isInheritedAssociation($fieldName)
2258
    {
2259 414
        return isset($this->associationMappings[$fieldName]['inherited']);
2260
    }
2261
2262 414
    public function isInheritedEmbeddedClass($fieldName)
2263
    {
2264 414
        return isset($this->embeddedClasses[$fieldName]['inherited']);
2265
    }
2266
2267
    /**
2268
     * Sets the name of the primary table the class is mapped to.
2269
     *
2270
     * @param string $tableName The table name.
2271
     *
2272
     * @return void
2273
     *
2274
     * @deprecated Use {@link setPrimaryTable}.
2275
     */
2276 5
    public function setTableName($tableName)
2277
    {
2278 5
        $this->table['name'] = $tableName;
2279 5
    }
2280
2281
    /**
2282
     * Sets the primary table definition. The provided array supports the
2283
     * following structure:
2284
     *
2285
     * name => <tableName> (optional, defaults to class name)
2286
     * indexes => array of indexes (optional)
2287
     * uniqueConstraints => array of constraints (optional)
2288
     *
2289
     * If a key is omitted, the current value is kept.
2290
     *
2291
     * @param array $table The table description.
2292
     *
2293
     * @return void
2294
     */
2295 362
    public function setPrimaryTable(array $table)
2296
    {
2297 362
        if (isset($table['name'])) {
2298
            // Split schema and table name from a table name like "myschema.mytable"
2299 293
            if (strpos($table['name'], '.') !== false) {
2300 9
                list($this->table['schema'], $table['name']) = explode('.', $table['name'], 2);
2301
            }
2302
2303 293
            if ($table['name'][0] === '`') {
2304 18
                $table['name']          = trim($table['name'], '`');
2305 18
                $this->table['quoted']  = true;
2306
            }
2307
2308 293
            $this->table['name'] = $table['name'];
2309
        }
2310
2311 362
        if (isset($table['quoted'])) {
2312 2
            $this->table['quoted'] = $table['quoted'];
2313
        }
2314
2315 362
        if (isset($table['schema'])) {
2316 6
            $this->table['schema'] = $table['schema'];
2317
        }
2318
2319 362
        if (isset($table['indexes'])) {
2320 18
            $this->table['indexes'] = $table['indexes'];
2321
        }
2322
2323 362
        if (isset($table['uniqueConstraints'])) {
2324 9
            $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
2325
        }
2326
2327 362
        if (isset($table['options'])) {
2328 11
            $this->table['options'] = $table['options'];
2329
        }
2330 362
    }
2331
2332
    /**
2333
     * Checks whether the given type identifies an inheritance type.
2334
     *
2335
     * @param integer $type
2336
     *
2337
     * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
2338
     */
2339 197
    private function _isInheritanceType($type)
2340
    {
2341 197
        return $type == self::INHERITANCE_TYPE_NONE ||
2342 124
                $type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
2343 64
                $type == self::INHERITANCE_TYPE_JOINED ||
2344 197
                $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
2345
    }
2346
2347
    /**
2348
     * Adds a mapped field to the class.
2349
     *
2350
     * @param array $mapping The field mapping.
2351
     *
2352
     * @return void
2353
     *
2354
     * @throws MappingException
2355
     */
2356 619
    public function mapField(array $mapping)
2357
    {
2358 619
        $this->_validateAndCompleteFieldMapping($mapping);
2359 617
        $this->assertFieldNotMapped($mapping['fieldName']);
2360
2361 616
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
2362 616
    }
2363
2364
    /**
2365
     * INTERNAL:
2366
     * Adds an association mapping without completing/validating it.
2367
     * This is mainly used to add inherited association mappings to derived classes.
2368
     *
2369
     * @param array $mapping
2370
     *
2371
     * @return void
2372
     *
2373
     * @throws MappingException
2374
     */
2375 59
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
2376
    {
2377 59
        if (isset($this->associationMappings[$mapping['fieldName']])) {
2378 1
            throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
2379
        }
2380 59
        $this->associationMappings[$mapping['fieldName']] = $mapping;
2381 59
    }
2382
2383
    /**
2384
     * INTERNAL:
2385
     * Adds a field mapping without completing/validating it.
2386
     * This is mainly used to add inherited field mappings to derived classes.
2387
     *
2388
     * @param array $fieldMapping
2389
     *
2390
     * @return void
2391
     */
2392 131
    public function addInheritedFieldMapping(array $fieldMapping)
2393
    {
2394 131
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
2395 131
        $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ORM\Mapping\Cla...adataInfo::$columnNames has been deprecated: 3.0 Remove this. ( Ignorable by Annotation )

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

2395
        /** @scrutinizer ignore-deprecated */ $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
2396 131
        $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
2397 131
    }
2398
2399
    /**
2400
     * INTERNAL:
2401
     * Adds a named query to this class.
2402
     *
2403
     * @param array $queryMapping
2404
     *
2405
     * @return void
2406
     *
2407
     * @throws MappingException
2408
     */
2409 36
    public function addNamedQuery(array $queryMapping)
2410
    {
2411 36
        if (!isset($queryMapping['name'])) {
2412 2
            throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2413
        }
2414
2415 34
        if (isset($this->namedQueries[$queryMapping['name']])) {
2416 1
            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2417
        }
2418
2419 34
        if (!isset($queryMapping['query'])) {
2420
            throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2421
        }
2422
2423 34
        $name   = $queryMapping['name'];
2424 34
        $query  = $queryMapping['query'];
2425 34
        $dql    = str_replace('__CLASS__', $this->name, $query);
2426
2427 34
        $this->namedQueries[$name] = [
2428 34
            'name'  => $name,
2429 34
            'query' => $query,
2430 34
            'dql'   => $dql,
2431
        ];
2432 34
    }
2433
2434
    /**
2435
     * INTERNAL:
2436
     * Adds a named native query to this class.
2437
     *
2438
     * @param array $queryMapping
2439
     *
2440
     * @return void
2441
     *
2442
     * @throws MappingException
2443
     */
2444 41
    public function addNamedNativeQuery(array $queryMapping)
2445
    {
2446 41
        if (!isset($queryMapping['name'])) {
2447
            throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2448
        }
2449
2450 41
        if (isset($this->namedNativeQueries[$queryMapping['name']])) {
2451 1
            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2452
        }
2453
2454 41
        if (!isset($queryMapping['query'])) {
2455
            throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2456
        }
2457
2458 41
        if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) {
2459
            throw MappingException::missingQueryMapping($this->name, $queryMapping['name']);
2460
        }
2461
2462 41
        $queryMapping['isSelfClass'] = false;
2463
2464 41
        if (isset($queryMapping['resultClass'])) {
2465 39
            if ($queryMapping['resultClass'] === '__CLASS__') {
2466
2467 13
                $queryMapping['isSelfClass'] = true;
2468 13
                $queryMapping['resultClass'] = $this->name;
2469
            }
2470
2471 39
            $queryMapping['resultClass'] = $this->fullyQualifiedClassName($queryMapping['resultClass']);
2472 39
            $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\');
2473
        }
2474
2475 41
        $this->namedNativeQueries[$queryMapping['name']] = $queryMapping;
2476 41
    }
2477
2478
    /**
2479
     * INTERNAL:
2480
     * Adds a sql result set mapping to this class.
2481
     *
2482
     * @param array $resultMapping
2483
     *
2484
     * @return void
2485
     *
2486
     * @throws MappingException
2487
     */
2488 41
    public function addSqlResultSetMapping(array $resultMapping)
2489
    {
2490 41
        if (!isset($resultMapping['name'])) {
2491
            throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name);
2492
        }
2493
2494 41
        if (isset($this->sqlResultSetMappings[$resultMapping['name']])) {
2495 1
            throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']);
2496
        }
2497
2498 41
        if (isset($resultMapping['entities'])) {
2499 41
            foreach ($resultMapping['entities'] as $key => $entityResult) {
2500 41
                if (!isset($entityResult['entityClass'])) {
2501 1
                    throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']);
2502
                }
2503
2504 40
                $entityResult['isSelfClass'] = false;
2505 40
                if ($entityResult['entityClass'] === '__CLASS__') {
2506
2507 23
                    $entityResult['isSelfClass'] = true;
2508 23
                    $entityResult['entityClass'] = $this->name;
2509
2510
                }
2511
2512 40
                $entityResult['entityClass'] = $this->fullyQualifiedClassName($entityResult['entityClass']);
2513
2514 40
                $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\');
2515 40
                $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass'];
2516
2517 40
                if (isset($entityResult['fields'])) {
2518 36
                    foreach ($entityResult['fields'] as $k => $field) {
2519 36
                        if (!isset($field['name'])) {
2520
                            throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']);
2521
                        }
2522
2523 36
                        if (!isset($field['column'])) {
2524 17
                            $fieldName = $field['name'];
2525 17
                            if (strpos($fieldName, '.')) {
2526 10
                                list(, $fieldName) = explode('.', $fieldName);
2527
                            }
2528
2529 40
                            $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName;
2530
                        }
2531
                    }
2532
                }
2533
            }
2534
        }
2535
2536 40
        $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping;
2537 40
    }
2538
2539
    /**
2540
     * Adds a one-to-one mapping.
2541
     *
2542
     * @param array $mapping The mapping.
2543
     *
2544
     * @return void
2545
     */
2546 188
    public function mapOneToOne(array $mapping)
2547
    {
2548 188
        $mapping['type'] = self::ONE_TO_ONE;
2549
2550 188
        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2551
2552 185
        $this->_storeAssociationMapping($mapping);
2553 184
    }
2554
2555
    /**
2556
     * Adds a one-to-many mapping.
2557
     *
2558
     * @param array $mapping The mapping.
2559
     *
2560
     * @return void
2561
     */
2562 150
    public function mapOneToMany(array $mapping)
2563
    {
2564 150
        $mapping['type'] = self::ONE_TO_MANY;
2565
2566 150
        $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2567
2568 149
        $this->_storeAssociationMapping($mapping);
2569 149
    }
2570
2571
    /**
2572
     * Adds a many-to-one mapping.
2573
     *
2574
     * @param array $mapping The mapping.
2575
     *
2576
     * @return void
2577
     */
2578 182
    public function mapManyToOne(array $mapping)
2579
    {
2580 182
        $mapping['type'] = self::MANY_TO_ONE;
2581
2582
        // A many-to-one mapping is essentially a one-one backreference
2583 182
        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2584
2585 177
        $this->_storeAssociationMapping($mapping);
2586 177
    }
2587
2588
    /**
2589
     * Adds a many-to-many mapping.
2590
     *
2591
     * @param array $mapping The mapping.
2592
     *
2593
     * @return void
2594
     */
2595 171
    public function mapManyToMany(array $mapping)
2596
    {
2597 171
        $mapping['type'] = self::MANY_TO_MANY;
2598
2599 171
        $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2600
2601 169
        $this->_storeAssociationMapping($mapping);
2602 169
    }
2603
2604
    /**
2605
     * Stores the association mapping.
2606
     *
2607
     * @param array $assocMapping
2608
     *
2609
     * @return void
2610
     *
2611
     * @throws MappingException
2612
     */
2613 384
    protected function _storeAssociationMapping(array $assocMapping)
2614
    {
2615 384
        $sourceFieldName = $assocMapping['fieldName'];
2616
2617 384
        $this->assertFieldNotMapped($sourceFieldName);
2618
2619 383
        $this->associationMappings[$sourceFieldName] = $assocMapping;
2620 383
    }
2621
2622
    /**
2623
     * Registers a custom repository class for the entity class.
2624
     *
2625
     * @param string $repositoryClassName The class name of the custom mapper.
2626
     *
2627
     * @return void
2628
     */
2629 65
    public function setCustomRepositoryClass($repositoryClassName)
2630
    {
2631 65
        $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName);
2632 65
    }
2633
2634
    /**
2635
     * Dispatches the lifecycle event of the given entity to the registered
2636
     * lifecycle callbacks and lifecycle listeners.
2637
     *
2638
     * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker
2639
     *
2640
     * @param string $lifecycleEvent The lifecycle event.
2641
     * @param object $entity         The Entity on which the event occurred.
2642
     *
2643
     * @return void
2644
     */
2645 1
    public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
2646
    {
2647 1
        foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
2648 1
            $entity->$callback();
2649
        }
2650 1
    }
2651
2652
    /**
2653
     * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
2654
     *
2655
     * @param string $lifecycleEvent
2656
     *
2657
     * @return boolean
2658
     */
2659
    public function hasLifecycleCallbacks($lifecycleEvent)
2660
    {
2661
        return isset($this->lifecycleCallbacks[$lifecycleEvent]);
2662
    }
2663
2664
    /**
2665
     * Gets the registered lifecycle callbacks for an event.
2666
     *
2667
     * @param string $event
2668
     *
2669
     * @return array
2670
     */
2671
    public function getLifecycleCallbacks($event)
2672
    {
2673
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : [];
2674
    }
2675
2676
    /**
2677
     * Adds a lifecycle callback for entities of this class.
2678
     *
2679
     * @param string $callback
2680
     * @param string $event
2681
     *
2682
     * @return void
2683
     */
2684 47
    public function addLifecycleCallback($callback, $event)
2685
    {
2686 47
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
2687 3
            return;
2688
        }
2689
2690 47
        $this->lifecycleCallbacks[$event][] = $callback;
2691 47
    }
2692
2693
    /**
2694
     * Sets the lifecycle callbacks for entities of this class.
2695
     * Any previously registered callbacks are overwritten.
2696
     *
2697
     * @param array $callbacks
2698
     *
2699
     * @return void
2700
     */
2701 146
    public function setLifecycleCallbacks(array $callbacks)
2702
    {
2703 146
        $this->lifecycleCallbacks = $callbacks;
2704 146
    }
2705
2706
    /**
2707
     * Adds a entity listener for entities of this class.
2708
     *
2709
     * @param string $eventName The entity lifecycle event.
2710
     * @param string $class     The listener class.
2711
     * @param string $method    The listener callback method.
2712
     *
2713
     * @throws \Doctrine\ORM\Mapping\MappingException
2714
     */
2715 43
    public function addEntityListener($eventName, $class, $method)
2716
    {
2717 43
        $class    = $this->fullyQualifiedClassName($class);
2718
2719
        $listener = [
2720 43
            'class'  => $class,
2721 43
            'method' => $method,
2722
        ];
2723
2724 43
        if ( ! class_exists($class)) {
2725 1
            throw MappingException::entityListenerClassNotFound($class, $this->name);
2726
        }
2727
2728 42
        if ( ! method_exists($class, $method)) {
2729 1
            throw MappingException::entityListenerMethodNotFound($class, $method, $this->name);
2730
        }
2731
2732 41
        if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName])) {
2733 1
            throw MappingException::duplicateEntityListener($class, $method, $this->name);
2734
        }
2735
2736 41
        $this->entityListeners[$eventName][] = $listener;
2737 41
    }
2738
2739
    /**
2740
     * Sets the discriminator column definition.
2741
     *
2742
     * @param array $columnDef
2743
     *
2744
     * @return void
2745
     *
2746
     * @throws MappingException
2747
     *
2748
     * @see getDiscriminatorColumn()
2749
     */
2750 189
    public function setDiscriminatorColumn($columnDef)
2751
    {
2752 189
        if ($columnDef !== null) {
0 ignored issues
show
introduced by
The condition $columnDef !== null is always true.
Loading history...
2753 128
            if ( ! isset($columnDef['name'])) {
2754 1
                throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name);
2755
            }
2756
2757 127
            if (isset($this->fieldNames[$columnDef['name']])) {
2758 1
                throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
2759
            }
2760
2761 126
            if ( ! isset($columnDef['fieldName'])) {
2762 122
                $columnDef['fieldName'] = $columnDef['name'];
2763
            }
2764
2765 126
            if ( ! isset($columnDef['type'])) {
2766 2
                $columnDef['type'] = "string";
2767
            }
2768
2769 126
            if (in_array($columnDef['type'], ["boolean", "array", "object", "datetime", "time", "date"])) {
2770
                throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
2771
            }
2772
2773 126
            $this->discriminatorColumn = $columnDef;
2774
        }
2775 187
    }
2776
2777
    /**
2778
     * Sets the discriminator values used by this class.
2779
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
2780
     *
2781
     * @param array $map
2782
     *
2783
     * @return void
2784
     */
2785 181
    public function setDiscriminatorMap(array $map)
2786
    {
2787 181
        foreach ($map as $value => $className) {
2788 121
            $this->addDiscriminatorMapClass($value, $className);
2789
        }
2790 181
    }
2791
2792
    /**
2793
     * Adds one entry of the discriminator map with a new class and corresponding name.
2794
     *
2795
     * @param string $name
2796
     * @param string $className
2797
     *
2798
     * @return void
2799
     *
2800
     * @throws MappingException
2801
     */
2802 122
    public function addDiscriminatorMapClass($name, $className)
2803
    {
2804 122
        $className = $this->fullyQualifiedClassName($className);
2805 122
        $className = ltrim($className, '\\');
2806
2807 122
        $this->discriminatorMap[$name] = $className;
2808
2809 122
        if ($this->name === $className) {
2810 93
            $this->discriminatorValue = $name;
2811
2812 93
            return;
2813
        }
2814
2815 121
        if ( ! (class_exists($className) || interface_exists($className))) {
2816
            throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
2817
        }
2818
2819 121
        if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
2820 111
            $this->subClasses[] = $className;
2821
        }
2822 121
    }
2823
2824
    /**
2825
     * Checks whether the class has a named query with the given query name.
2826
     *
2827
     * @param string $queryName
2828
     *
2829
     * @return boolean
2830
     */
2831 1
    public function hasNamedQuery($queryName)
2832
    {
2833 1
        return isset($this->namedQueries[$queryName]);
2834
    }
2835
2836
    /**
2837
     * Checks whether the class has a named native query with the given query name.
2838
     *
2839
     * @param string $queryName
2840
     *
2841
     * @return boolean
2842
     */
2843 1
    public function hasNamedNativeQuery($queryName)
2844
    {
2845 1
        return isset($this->namedNativeQueries[$queryName]);
2846
    }
2847
2848
    /**
2849
     * Checks whether the class has a named native query with the given query name.
2850
     *
2851
     * @param string $name
2852
     *
2853
     * @return boolean
2854
     */
2855 1
    public function hasSqlResultSetMapping($name)
2856
    {
2857 1
        return isset($this->sqlResultSetMappings[$name]);
2858
    }
2859
2860
    /**
2861
     * {@inheritDoc}
2862
     */
2863 352
    public function hasAssociation($fieldName)
2864
    {
2865 352
        return isset($this->associationMappings[$fieldName]);
2866
    }
2867
2868
    /**
2869
     * {@inheritDoc}
2870
     */
2871 1
    public function isSingleValuedAssociation($fieldName)
2872
    {
2873 1
        return isset($this->associationMappings[$fieldName])
2874 1
            && ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2875
    }
2876
2877
    /**
2878
     * {@inheritDoc}
2879
     */
2880 1099
    public function isCollectionValuedAssociation($fieldName)
2881
    {
2882 1099
        return isset($this->associationMappings[$fieldName])
2883 1099
            && ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2884
    }
2885
2886
    /**
2887
     * Is this an association that only has a single join column?
2888
     *
2889
     * @param string $fieldName
2890
     *
2891
     * @return bool
2892
     */
2893 99
    public function isAssociationWithSingleJoinColumn($fieldName)
2894
    {
2895 99
        return isset($this->associationMappings[$fieldName])
2896 99
            && isset($this->associationMappings[$fieldName]['joinColumns'][0])
2897 99
            && ! isset($this->associationMappings[$fieldName]['joinColumns'][1]);
2898
    }
2899
2900
    /**
2901
     * Returns the single association join column (if any).
2902
     *
2903
     * @param string $fieldName
2904
     *
2905
     * @return string
2906
     *
2907
     * @throws MappingException
2908
     */
2909 10
    public function getSingleAssociationJoinColumnName($fieldName)
2910
    {
2911 10
        if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2912
            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2913
        }
2914
2915 10
        return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
2916
    }
2917
2918
    /**
2919
     * Returns the single association referenced join column name (if any).
2920
     *
2921
     * @param string $fieldName
2922
     *
2923
     * @return string
2924
     *
2925
     * @throws MappingException
2926
     */
2927 10
    public function getSingleAssociationReferencedJoinColumnName($fieldName)
2928
    {
2929 10
        if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2930
            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2931
        }
2932
2933 10
        return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
2934
    }
2935
2936
    /**
2937
     * Used to retrieve a fieldname for either field or association from a given column.
2938
     *
2939
     * This method is used in foreign-key as primary-key contexts.
2940
     *
2941
     * @param string $columnName
2942
     *
2943
     * @return string
2944
     *
2945
     * @throws MappingException
2946
     */
2947 679
    public function getFieldForColumn($columnName)
2948
    {
2949 679
        if (isset($this->fieldNames[$columnName])) {
2950 657
            return $this->fieldNames[$columnName];
2951
        }
2952
2953 98
        foreach ($this->associationMappings as $assocName => $mapping) {
2954 98
            if ($this->isAssociationWithSingleJoinColumn($assocName) &&
2955 98
                $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
2956
2957 98
                return $assocName;
2958
            }
2959
        }
2960
2961
        throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
2962
    }
2963
2964
    /**
2965
     * Sets the ID generator used to generate IDs for instances of this class.
2966
     *
2967
     * @param \Doctrine\ORM\Id\AbstractIdGenerator $generator
2968
     *
2969
     * @return void
2970
     */
2971 482
    public function setIdGenerator($generator)
2972
    {
2973 482
        $this->idGenerator = $generator;
2974 482
    }
2975
2976
    /**
2977
     * Sets definition.
2978
     *
2979
     * @param array $definition
2980
     *
2981
     * @return void
2982
     */
2983 13
    public function setCustomGeneratorDefinition(array $definition)
2984
    {
2985 13
        $this->customGeneratorDefinition = $definition;
2986 13
    }
2987
2988
    /**
2989
     * Sets the definition of the sequence ID generator for this class.
2990
     *
2991
     * The definition must have the following structure:
2992
     * <code>
2993
     * array(
2994
     *     'sequenceName'   => 'name',
2995
     *     'allocationSize' => 20,
2996
     *     'initialValue'   => 1
2997
     *     'quoted'         => 1
2998
     * )
2999
     * </code>
3000
     *
3001
     * @param array $definition
3002
     *
3003
     * @return void
3004
     *
3005
     * @throws MappingException
3006
     */
3007 30
    public function setSequenceGeneratorDefinition(array $definition)
3008
    {
3009 30
        if ( ! isset($definition['sequenceName']) || trim($definition['sequenceName']) === '') {
3010 1
            throw MappingException::missingSequenceName($this->name);
3011
        }
3012
3013 29
        if ($definition['sequenceName'][0] == '`') {
3014 1
            $definition['sequenceName']   = trim($definition['sequenceName'], '`');
0 ignored issues
show
Bug introduced by
$definition['sequenceName'] of type array is incompatible with the type string expected by parameter $str of trim(). ( Ignorable by Annotation )

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

3014
            $definition['sequenceName']   = trim(/** @scrutinizer ignore-type */ $definition['sequenceName'], '`');
Loading history...
3015 1
            $definition['quoted'] = true;
3016
        }
3017
3018 29
        if ( ! isset($definition['allocationSize']) || trim($definition['allocationSize']) === '') {
3019 2
            $definition['allocationSize'] = '1';
3020
        }
3021
3022 29
        if ( ! isset($definition['initialValue']) || trim($definition['initialValue']) === '') {
3023 2
            $definition['initialValue'] = '1';
3024
        }
3025
3026 29
        $this->sequenceGeneratorDefinition = $definition;
3027 29
    }
3028
3029
    /**
3030
     * Sets the version field mapping used for versioning. Sets the default
3031
     * value to use depending on the column type.
3032
     *
3033
     * @param array $mapping The version field mapping array.
3034
     *
3035
     * @return void
3036
     *
3037
     * @throws MappingException
3038
     */
3039 35
    public function setVersionMapping(array &$mapping)
3040
    {
3041 35
        $this->isVersioned = true;
3042 35
        $this->versionField = $mapping['fieldName'];
3043
3044 35
        if ( ! isset($mapping['default'])) {
3045 35
            if (in_array($mapping['type'], ['integer', 'bigint', 'smallint'])) {
3046 33
                $mapping['default'] = 1;
3047 3
            } else if ($mapping['type'] == 'datetime') {
3048 2
                $mapping['default'] = 'CURRENT_TIMESTAMP';
3049
            } else {
3050 1
                throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
3051
            }
3052
        }
3053 34
    }
3054
3055
    /**
3056
     * Sets whether this class is to be versioned for optimistic locking.
3057
     *
3058
     * @param boolean $bool
3059
     *
3060
     * @return void
3061
     */
3062 146
    public function setVersioned($bool)
3063
    {
3064 146
        $this->isVersioned = $bool;
3065 146
    }
3066
3067
    /**
3068
     * Sets the name of the field that is to be used for versioning if this class is
3069
     * versioned for optimistic locking.
3070
     *
3071
     * @param string $versionField
3072
     *
3073
     * @return void
3074
     */
3075 146
    public function setVersionField($versionField)
3076
    {
3077 146
        $this->versionField = $versionField;
3078 146
    }
3079
3080
    /**
3081
     * Marks this class as read only, no change tracking is applied to it.
3082
     *
3083
     * @return void
3084
     */
3085 3
    public function markReadOnly()
3086
    {
3087 3
        $this->isReadOnly = true;
3088 3
    }
3089
3090
    /**
3091
     * {@inheritDoc}
3092
     */
3093
    public function getFieldNames()
3094
    {
3095
        return array_keys($this->fieldMappings);
3096
    }
3097
3098
    /**
3099
     * {@inheritDoc}
3100
     */
3101
    public function getAssociationNames()
3102
    {
3103
        return array_keys($this->associationMappings);
3104
    }
3105
3106
    /**
3107
     * {@inheritDoc}
3108
     *
3109
     * @throws InvalidArgumentException
3110
     */
3111 1
    public function getAssociationTargetClass($assocName)
3112
    {
3113 1
        if ( ! isset($this->associationMappings[$assocName])) {
3114
            throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
3115
        }
3116
3117 1
        return $this->associationMappings[$assocName]['targetEntity'];
3118
    }
3119
3120
    /**
3121
     * {@inheritDoc}
3122
     */
3123 805
    public function getName()
3124
    {
3125 805
        return $this->name;
3126
    }
3127
3128
    /**
3129
     * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
3130
     *
3131
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3132
     *
3133
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3134
     *
3135
     * @return array
3136
     */
3137
    public function getQuotedIdentifierColumnNames($platform)
3138
    {
3139
        $quotedColumnNames = [];
3140
3141
        foreach ($this->identifier as $idProperty) {
3142
            if (isset($this->fieldMappings[$idProperty])) {
3143
                $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
3144
                    ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
3145
                    : $this->fieldMappings[$idProperty]['columnName'];
3146
3147
                continue;
3148
            }
3149
3150
            // Association defined as Id field
3151
            $joinColumns            = $this->associationMappings[$idProperty]['joinColumns'];
3152
            $assocQuotedColumnNames = array_map(
3153
                function ($joinColumn) use ($platform) {
3154
                    return isset($joinColumn['quoted'])
3155
                        ? $platform->quoteIdentifier($joinColumn['name'])
3156
                        : $joinColumn['name'];
3157
                },
3158
                $joinColumns
3159
            );
3160
3161
            $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
3162
        }
3163
3164
        return $quotedColumnNames;
3165
    }
3166
3167
    /**
3168
     * Gets the (possibly quoted) column name of a mapped field for safe use  in an SQL statement.
3169
     *
3170
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3171
     *
3172
     * @param string                                    $field
3173
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3174
     *
3175
     * @return string
3176
     */
3177
    public function getQuotedColumnName($field, $platform)
3178
    {
3179
        return isset($this->fieldMappings[$field]['quoted'])
3180
            ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
3181
            : $this->fieldMappings[$field]['columnName'];
3182
    }
3183
3184
    /**
3185
     * Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement.
3186
     *
3187
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3188
     *
3189
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3190
     *
3191
     * @return string
3192
     */
3193
    public function getQuotedTableName($platform)
3194
    {
3195
        return isset($this->table['quoted'])
3196
            ? $platform->quoteIdentifier($this->table['name'])
3197
            : $this->table['name'];
3198
    }
3199
3200
    /**
3201
     * Gets the (possibly quoted) name of the join table.
3202
     *
3203
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3204
     *
3205
     * @param array                                     $assoc
3206
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3207
     *
3208
     * @return string
3209
     */
3210
    public function getQuotedJoinTableName(array $assoc, $platform)
3211
    {
3212
        return isset($assoc['joinTable']['quoted'])
3213
            ? $platform->quoteIdentifier($assoc['joinTable']['name'])
3214
            : $assoc['joinTable']['name'];
3215
    }
3216
3217
    /**
3218
     * {@inheritDoc}
3219
     */
3220 12
    public function isAssociationInverseSide($fieldName)
3221
    {
3222 12
        return isset($this->associationMappings[$fieldName])
3223 12
            && ! $this->associationMappings[$fieldName]['isOwningSide'];
3224
    }
3225
3226
    /**
3227
     * {@inheritDoc}
3228
     */
3229
    public function getAssociationMappedByTargetField($fieldName)
3230
    {
3231
        return $this->associationMappings[$fieldName]['mappedBy'];
3232
    }
3233
3234
    /**
3235
     * @param string $targetClass
3236
     *
3237
     * @return array
3238
     */
3239 2
    public function getAssociationsByTargetClass($targetClass)
3240
    {
3241 2
        $relations = [];
3242
3243 2
        foreach ($this->associationMappings as $mapping) {
3244 2
            if ($mapping['targetEntity'] == $targetClass) {
3245 2
                $relations[$mapping['fieldName']] = $mapping;
3246
            }
3247
        }
3248
3249 2
        return $relations;
3250
    }
3251
3252
    /**
3253
     * @param  string|null $className
3254
     *
3255
     * @return string|null null if the input value is null
3256
     */
3257 536
    public function fullyQualifiedClassName($className)
3258
    {
3259 536
        if (empty($className)) {
3260 49
            return $className;
3261
        }
3262
3263 520
        if ($className !== null && strpos($className, '\\') === false && $this->namespace) {
3264 398
            return $this->namespace . '\\' . $className;
3265
        }
3266
3267 260
        return $className;
3268
    }
3269
3270
    /**
3271
     * @param string $name
3272
     *
3273
     * @return mixed
3274
     */
3275 2
    public function getMetadataValue($name)
3276
    {
3277
3278 2
        if (isset($this->$name)) {
3279 2
            return $this->$name;
3280
        }
3281
3282
        return null;
3283
    }
3284
3285
    /**
3286
     * Map Embedded Class
3287
     *
3288
     * @param array $mapping
3289
     *
3290
     * @throws MappingException
3291
     * @return void
3292
     */
3293 31
    public function mapEmbedded(array $mapping)
3294
    {
3295 31
        $this->assertFieldNotMapped($mapping['fieldName']);
3296
3297 31
        $this->embeddedClasses[$mapping['fieldName']] = [
3298 31
            'class' => $this->fullyQualifiedClassName($mapping['class']),
3299 31
            'columnPrefix' => $mapping['columnPrefix'],
3300 31
            'declaredField' => $mapping['declaredField'] ?? null,
3301 31
            'originalField' => $mapping['originalField'] ?? null,
3302
        ];
3303 31
    }
3304
3305
    /**
3306
     * Inline the embeddable class
3307
     *
3308
     * @param string            $property
3309
     * @param ClassMetadataInfo $embeddable
3310
     */
3311 12
    public function inlineEmbeddable($property, ClassMetadataInfo $embeddable)
3312
    {
3313 12
        foreach ($embeddable->fieldMappings as $fieldMapping) {
3314 12
            $fieldMapping['originalClass'] = $fieldMapping['originalClass'] ?? $embeddable->name;
3315 12
            $fieldMapping['declaredField'] = isset($fieldMapping['declaredField'])
3316 4
                ? $property . '.' . $fieldMapping['declaredField']
3317 12
                : $property;
3318 12
            $fieldMapping['originalField'] = $fieldMapping['originalField'] ?? $fieldMapping['fieldName'];
3319 12
            $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName'];
3320
3321 12
            if (! empty($this->embeddedClasses[$property]['columnPrefix'])) {
3322 2
                $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName'];
3323 11
            } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) {
3324 8
                $fieldMapping['columnName'] = $this->namingStrategy
3325 8
                    ->embeddedFieldToColumnName(
3326 8
                        $property,
3327 8
                        $fieldMapping['columnName'],
3328 8
                        $this->reflClass->name,
3329 8
                        $embeddable->reflClass->name
3330
                    );
3331
            }
3332
3333 12
            $this->mapField($fieldMapping);
3334
        }
3335 12
    }
3336
3337
    /**
3338
     * @param string $fieldName
3339
     * @throws MappingException
3340
     */
3341 657
    private function assertFieldNotMapped($fieldName)
3342
    {
3343 657
        if (isset($this->fieldMappings[$fieldName]) ||
3344 657
            isset($this->associationMappings[$fieldName]) ||
3345 657
            isset($this->embeddedClasses[$fieldName])) {
3346
3347 2
            throw MappingException::duplicateFieldMapping($this->name, $fieldName);
3348
        }
3349 657
    }
3350
3351
    /**
3352
     * Gets the sequence name based on class metadata.
3353
     *
3354
     * @param AbstractPlatform $platform
3355
     * @return string
3356
     *
3357
     * @todo Sequence names should be computed in DBAL depending on the platform
3358
     */
3359 3
    public function getSequenceName(AbstractPlatform $platform)
3360
    {
3361 3
        $sequencePrefix = $this->getSequencePrefix($platform);
3362 3
        $columnName     = $this->getSingleIdentifierColumnName();
3363 3
        $sequenceName   = $sequencePrefix . '_' . $columnName . '_seq';
3364
3365 3
        return $sequenceName;
3366
    }
3367
3368
    /**
3369
     * Gets the sequence name prefix based on class metadata.
3370
     *
3371
     * @param AbstractPlatform $platform
3372
     * @return string
3373
     *
3374
     * @todo Sequence names should be computed in DBAL depending on the platform
3375
     */
3376 3
    public function getSequencePrefix(AbstractPlatform $platform)
3377
    {
3378 3
        $tableName      = $this->getTableName();
3379 3
        $sequencePrefix = $tableName;
3380
3381
        // Prepend the schema name to the table name if there is one
3382 3
        if ($schemaName = $this->getSchemaName()) {
3383 3
            $sequencePrefix = $schemaName . '.' . $tableName;
3384
3385 3
            if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) {
3386 3
                $sequencePrefix = $schemaName . '__' . $tableName;
3387
            }
3388
        }
3389
3390 3
        return $sequencePrefix;
3391
    }
3392
3393
    /**
3394
     * @param array $mapping
3395
     */
3396 242
    private function assertMappingOrderBy(array $mapping)
3397
    {
3398 242
        if (isset($mapping['orderBy']) && !is_array($mapping['orderBy'])) {
3399
            throw new InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy']));
3400
        }
3401 242
    }
3402
}
3403