Completed
Push — master ( dfc31b...3ca65e )
by Luís
17s
created

setSequenceGeneratorDefinition()   B

Complexity

Conditions 8
Paths 9

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 8

Importance

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

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

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

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

Loading history...
816
            'fieldMappings',
817
            'fieldNames',
818
            'embeddedClasses',
819
            'identifier',
820
            'isIdentifierComposite', // TODO: REMOVE
821
            'name',
822
            'namespace', // TODO: REMOVE
823
            'table',
824
            'rootEntityName',
825
            'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
826
        ];
827
828
        // The rest of the metadata is only serialized if necessary.
829 6
        if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
830
            $serialized[] = 'changeTrackingPolicy';
831
        }
832
833 6
        if ($this->customRepositoryClassName) {
834 1
            $serialized[] = 'customRepositoryClassName';
835
        }
836
837 6
        if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
838 1
            $serialized[] = 'inheritanceType';
839 1
            $serialized[] = 'discriminatorColumn';
840 1
            $serialized[] = 'discriminatorValue';
841 1
            $serialized[] = 'discriminatorMap';
842 1
            $serialized[] = 'parentClasses';
843 1
            $serialized[] = 'subClasses';
844
        }
845
846 6
        if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
847 1
            $serialized[] = 'generatorType';
848 1
            if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
849
                $serialized[] = 'sequenceGeneratorDefinition';
850
            }
851
        }
852
853 6
        if ($this->isMappedSuperclass) {
854
            $serialized[] = 'isMappedSuperclass';
855
        }
856
857 6
        if ($this->isEmbeddedClass) {
858 1
            $serialized[] = 'isEmbeddedClass';
859
        }
860
861 6
        if ($this->containsForeignIdentifier) {
862
            $serialized[] = 'containsForeignIdentifier';
863
        }
864
865 6
        if ($this->isVersioned) {
866
            $serialized[] = 'isVersioned';
867
            $serialized[] = 'versionField';
868
        }
869
870 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...
871
            $serialized[] = 'lifecycleCallbacks';
872
        }
873
874 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...
875 1
            $serialized[] = 'entityListeners';
876
        }
877
878 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...
879 1
            $serialized[] = 'namedQueries';
880
        }
881
882 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...
883
            $serialized[] = 'namedNativeQueries';
884
        }
885
886 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...
887
            $serialized[] = 'sqlResultSetMappings';
888
        }
889
890 6
        if ($this->isReadOnly) {
891 1
            $serialized[] = 'isReadOnly';
892
        }
893
894 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...
895
            $serialized[] = "customGeneratorDefinition";
896
        }
897
898 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...
899
            $serialized[] = 'cache';
900
        }
901
902 6
        return $serialized;
903
    }
904
905
    /**
906
     * Creates a new instance of the mapped class, without invoking the constructor.
907
     *
908
     * @return object
909
     */
910 710
    public function newInstance()
911
    {
912 710
        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

912
        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...
913
    }
914
915
    /**
916
     * Restores some state that can not be serialized/unserialized.
917
     *
918
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
919
     *
920
     * @return void
921
     */
922 2100
    public function wakeupReflection($reflService)
923
    {
924
        // Restore ReflectionClass and properties
925 2100
        $this->reflClass    = $reflService->getClass($this->name);
926 2100
        $this->instantiator = $this->instantiator ?: new Instantiator();
927
928 2100
        $parentReflFields = [];
929
930 2100
        foreach ($this->embeddedClasses as $property => $embeddedClass) {
931 23
            if (isset($embeddedClass['declaredField'])) {
932 15
                $parentReflFields[$property] = new ReflectionEmbeddedProperty(
933 15
                    $parentReflFields[$embeddedClass['declaredField']],
934 15
                    $reflService->getAccessibleProperty(
935 15
                        $this->embeddedClasses[$embeddedClass['declaredField']]['class'],
936 15
                        $embeddedClass['originalField']
937
                    ),
938 15
                    $this->embeddedClasses[$embeddedClass['declaredField']]['class']
939
                );
940
941 15
                continue;
942
            }
943
944 23
            $fieldRefl = $reflService->getAccessibleProperty(
945 23
                $embeddedClass['declared'] ?? $this->name,
946 23
                $property
947
            );
948
949 23
            $parentReflFields[$property] = $fieldRefl;
950 23
            $this->reflFields[$property] = $fieldRefl;
951
        }
952
953 2100
        foreach ($this->fieldMappings as $field => $mapping) {
954 2095
            if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
955 22
                $this->reflFields[$field] = new ReflectionEmbeddedProperty(
956 22
                    $parentReflFields[$mapping['declaredField']],
957 22
                    $reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField']),
958 22
                    $mapping['originalClass']
959
                );
960 22
                continue;
961
            }
962
963 2095
            $this->reflFields[$field] = isset($mapping['declared'])
964 539
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
965 2095
                : $reflService->getAccessibleProperty($this->name, $field);
966
        }
967
968 2100
        foreach ($this->associationMappings as $field => $mapping) {
969 1752
            $this->reflFields[$field] = isset($mapping['declared'])
970 433
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
971 1752
                : $reflService->getAccessibleProperty($this->name, $field);
972
        }
973 2100
    }
974
975
    /**
976
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
977
     * metadata of the class with the given name.
978
     *
979
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service.
980
     *
981
     * @return void
982
     */
983 676
    public function initializeReflection($reflService)
984
    {
985 676
        $this->reflClass = $reflService->getClass($this->name);
986 676
        $this->namespace = $reflService->getClassNamespace($this->name);
987
988 676
        if ($this->reflClass) {
989 669
            $this->name = $this->rootEntityName = $this->reflClass->getName();
990
        }
991
992 676
        $this->table['name'] = $this->namingStrategy->classToTableName($this->name);
993 676
    }
994
995
    /**
996
     * Validates Identifier.
997
     *
998
     * @return void
999
     *
1000
     * @throws MappingException
1001
     */
1002 458
    public function validateIdentifier()
1003
    {
1004 458
        if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
1005 63
            return;
1006
        }
1007
1008
        // Verify & complete identifier mapping
1009 456
        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...
1010 6
            throw MappingException::identifierRequired($this->name);
1011
        }
1012
1013 450
        if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
1014
            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
1015
        }
1016 450
    }
1017
1018
    /**
1019
     * Validates association targets actually exist.
1020
     *
1021
     * @return void
1022
     *
1023
     * @throws MappingException
1024
     */
1025 459
    public function validateAssociations()
1026
    {
1027 459
        foreach ($this->associationMappings as $mapping) {
1028 296
            if ( ! ClassLoader::classExists($mapping['targetEntity']) ) {
1029 296
                throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
1030
            }
1031
        }
1032 458
    }
1033
1034
    /**
1035
     * Validates lifecycle callbacks.
1036
     *
1037
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
1038
     *
1039
     * @return void
1040
     *
1041
     * @throws MappingException
1042
     */
1043 459
    public function validateLifecycleCallbacks($reflService)
1044
    {
1045 459
        foreach ($this->lifecycleCallbacks as $callbacks) {
1046 13
            foreach ($callbacks as $callbackFuncName) {
1047 13
                if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
1048 13
                    throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
1049
                }
1050
            }
1051
        }
1052 458
    }
1053
1054
    /**
1055
     * {@inheritDoc}
1056
     */
1057 593
    public function getReflectionClass()
1058
    {
1059 593
        return $this->reflClass;
1060
    }
1061
1062
    /**
1063
     * @param array $cache
1064
     *
1065
     * @return void
1066
     */
1067 24
    public function enableCache(array $cache)
1068
    {
1069 24
        if ( ! isset($cache['usage'])) {
1070
            $cache['usage'] = self::CACHE_USAGE_READ_ONLY;
1071
        }
1072
1073 24 View Code Duplication
        if ( ! isset($cache['region'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1074 24
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName));
1075
        }
1076
1077 24
        $this->cache = $cache;
1078 24
    }
1079
1080
    /**
1081
     * @param string $fieldName
1082
     * @param array  $cache
1083
     *
1084
     * @return void
1085
     */
1086 2
    public function enableAssociationCache($fieldName, array $cache)
1087
    {
1088 2
        $this->associationMappings[$fieldName]['cache'] = $this->getAssociationCacheDefaults($fieldName, $cache);
1089 2
    }
1090
1091
    /**
1092
     * @param string $fieldName
1093
     * @param array  $cache
1094
     *
1095
     * @return array
1096
     */
1097 19
    public function getAssociationCacheDefaults($fieldName, array $cache)
1098
    {
1099 19
        if ( ! isset($cache['usage'])) {
1100 1
            $cache['usage'] = isset($this->cache['usage'])
1101 1
                ? $this->cache['usage']
1102
                : self::CACHE_USAGE_READ_ONLY;
1103
        }
1104
1105 19 View Code Duplication
        if ( ! isset($cache['region'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1106 19
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName;
1107
        }
1108
1109 19
        return $cache;
1110
    }
1111
1112
    /**
1113
     * Sets the change tracking policy used by this class.
1114
     *
1115
     * @param integer $policy
1116
     *
1117
     * @return void
1118
     */
1119 162
    public function setChangeTrackingPolicy($policy)
1120
    {
1121 162
        $this->changeTrackingPolicy = $policy;
1122 162
    }
1123
1124
    /**
1125
     * Whether the change tracking policy of this class is "deferred explicit".
1126
     *
1127
     * @return boolean
1128
     */
1129 274
    public function isChangeTrackingDeferredExplicit()
1130
    {
1131 274
        return self::CHANGETRACKING_DEFERRED_EXPLICIT === $this->changeTrackingPolicy;
1132
    }
1133
1134
    /**
1135
     * Whether the change tracking policy of this class is "deferred implicit".
1136
     *
1137
     * @return boolean
1138
     */
1139 478
    public function isChangeTrackingDeferredImplicit()
1140
    {
1141 478
        return self::CHANGETRACKING_DEFERRED_IMPLICIT === $this->changeTrackingPolicy;
1142
    }
1143
1144
    /**
1145
     * Whether the change tracking policy of this class is "notify".
1146
     *
1147
     * @return boolean
1148
     */
1149 306
    public function isChangeTrackingNotify()
1150
    {
1151 306
        return self::CHANGETRACKING_NOTIFY === $this->changeTrackingPolicy;
1152
    }
1153
1154
    /**
1155
     * Checks whether a field is part of the identifier/primary key field(s).
1156
     *
1157
     * @param string $fieldName The field name.
1158
     *
1159
     * @return boolean TRUE if the field is part of the table identifier/primary key field(s),
1160
     *                 FALSE otherwise.
1161
     */
1162 1132
    public function isIdentifier($fieldName)
1163
    {
1164 1132
        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...
1165 1
            return false;
1166
        }
1167
1168 1131
        if ( ! $this->isIdentifierComposite) {
1169 1126
            return $fieldName === $this->identifier[0];
1170
        }
1171
1172 95
        return in_array($fieldName, $this->identifier, true);
1173
    }
1174
1175
    /**
1176
     * Checks if the field is unique.
1177
     *
1178
     * @param string $fieldName The field name.
1179
     *
1180
     * @return boolean TRUE if the field is unique, FALSE otherwise.
1181
     */
1182
    public function isUniqueField($fieldName)
1183
    {
1184
        $mapping = $this->getFieldMapping($fieldName);
1185
1186
        return false !== $mapping && isset($mapping['unique']) && $mapping['unique'];
1187
    }
1188
1189
    /**
1190
     * Checks if the field is not null.
1191
     *
1192
     * @param string $fieldName The field name.
1193
     *
1194
     * @return boolean TRUE if the field is not null, FALSE otherwise.
1195
     */
1196 1
    public function isNullable($fieldName)
1197
    {
1198 1
        $mapping = $this->getFieldMapping($fieldName);
1199
1200 1
        return false !== $mapping && isset($mapping['nullable']) && $mapping['nullable'];
1201
    }
1202
1203
    /**
1204
     * Gets a column name for a field name.
1205
     * If the column name for the field cannot be found, the given field name
1206
     * is returned.
1207
     *
1208
     * @param string $fieldName The field name.
1209
     *
1210
     * @return string The column name.
1211
     */
1212 16
    public function getColumnName($fieldName)
1213
    {
1214 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

1214
        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...
1215 16
            ? $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

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

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1387 1
            throw MappingException::missingFieldName($this->name);
1388
        }
1389
1390 593
        if ( ! isset($mapping['type'])) {
1391
            // Default to string
1392 65
            $mapping['type'] = 'string';
1393
        }
1394
1395
        // Complete fieldName and columnName mapping
1396 593
        if ( ! isset($mapping['columnName'])) {
1397 495
            $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name);
1398
        }
1399
1400 593
        if ('`' === $mapping['columnName'][0]) {
1401 11
            $mapping['columnName']  = trim($mapping['columnName'], '`');
1402 11
            $mapping['quoted']      = true;
1403
        }
1404
1405 593
        $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

1405
        /** @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...
1406
1407 593
        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...
1408 2
            throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);
1409
        }
1410
1411 592
        $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
1412
1413
        // Complete id mapping
1414 592
        if (isset($mapping['id']) && true === $mapping['id']) {
1415 552
            if ($this->versionField == $mapping['fieldName']) {
1416
                throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
1417
            }
1418
1419 552
            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1420 552
                $this->identifier[] = $mapping['fieldName'];
1421
            }
1422
1423
            // Check for composite key
1424 552 View Code Duplication
            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1425 22
                $this->isIdentifierComposite = true;
1426
            }
1427
        }
1428
1429 592
        if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
1430 5 View Code Duplication
            if (isset($mapping['id']) && true === $mapping['id']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1431
                 throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
1432
            }
1433
1434 5
            $mapping['requireSQLConversion'] = true;
1435
        }
1436 592
    }
1437
1438
    /**
1439
     * Validates & completes the basic mapping information that is common to all
1440
     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
1441
     *
1442
     * @param array $mapping The mapping.
1443
     *
1444
     * @return array The updated mapping.
1445
     *
1446
     * @throws MappingException If something is wrong with the mapping.
1447
     */
1448 385
    protected function _validateAndCompleteAssociationMapping(array $mapping)
1449
    {
1450 385
        if ( ! isset($mapping['mappedBy'])) {
1451 371
            $mapping['mappedBy'] = null;
1452
        }
1453
1454 385
        if ( ! isset($mapping['inversedBy'])) {
1455 355
            $mapping['inversedBy'] = null;
1456
        }
1457
1458 385
        $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
1459
1460 385
        if (empty($mapping['indexBy'])) {
1461 382
            unset($mapping['indexBy']);
1462
        }
1463
1464
        // If targetEntity is unqualified, assume it is in the same namespace as
1465
        // the sourceEntity.
1466 385
        $mapping['sourceEntity'] = $this->name;
1467
1468 385
        if (isset($mapping['targetEntity'])) {
1469 385
            $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']);
0 ignored issues
show
Bug introduced by
It seems like $mapping['targetEntity'] can also be of type true; however, parameter $className of Doctrine\ORM\Mapping\Cla...llyQualifiedClassName() does only seem to accept null|string, 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

1469
            $mapping['targetEntity'] = $this->fullyQualifiedClassName(/** @scrutinizer ignore-type */ $mapping['targetEntity']);
Loading history...
1470 385
            $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
1471
        }
1472
1473 385 View Code Duplication
        if (($mapping['type'] & self::MANY_TO_ONE) > 0 && isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1474 1
            throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
1475
        }
1476
1477
        // Complete id mapping
1478 384
        if (isset($mapping['id']) && true === $mapping['id']) {
1479 55
            if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) {
1480 1
                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
1481
            }
1482
1483 54
            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1484 54
                if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) >= 2) {
1485
                    throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
1486
                        $mapping['targetEntity'], $this->name, $mapping['fieldName']
1487
                    );
1488
                }
1489
1490 54
                $this->identifier[] = $mapping['fieldName'];
1491 54
                $this->containsForeignIdentifier = true;
1492
            }
1493
1494
            // Check for composite key
1495 54 View Code Duplication
            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1496 26
                $this->isIdentifierComposite = true;
1497
            }
1498
1499 54
            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...
1500 3
                throw CacheException::nonCacheableEntityAssociation($this->name, $mapping['fieldName']);
1501
            }
1502
        }
1503
1504
        // Mandatory attributes for both sides
1505
        // Mandatory: fieldName, targetEntity
1506 380 View Code Duplication
        if ( ! isset($mapping['fieldName']) || !$mapping['fieldName']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1507
            throw MappingException::missingFieldName($this->name);
1508
        }
1509
1510 380
        if ( ! isset($mapping['targetEntity'])) {
1511
            throw MappingException::missingTargetEntity($mapping['fieldName']);
1512
        }
1513
1514
        // Mandatory and optional attributes for either side
1515 380
        if ( ! $mapping['mappedBy']) {
1516 366
            if (isset($mapping['joinTable']) && $mapping['joinTable']) {
1517 139
                if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') {
1518 4
                    $mapping['joinTable']['name']   = trim($mapping['joinTable']['name'], '`');
1519 366
                    $mapping['joinTable']['quoted'] = true;
1520
                }
1521
            }
1522
        } else {
1523 201
            $mapping['isOwningSide'] = false;
1524
        }
1525
1526 380 View Code Duplication
        if (isset($mapping['id']) && true === $mapping['id'] && $mapping['type'] & self::TO_MANY) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1527 3
            throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']);
1528
        }
1529
1530
        // Fetch mode. Default fetch mode to LAZY, if not set.
1531 377
        if ( ! isset($mapping['fetch'])) {
1532 105
            $mapping['fetch'] = self::FETCH_LAZY;
1533
        }
1534
1535
        // Cascades
1536 377
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : [];
1537
1538 377
        $allCascades = ['remove', 'persist', 'refresh', 'merge', 'detach'];
1539 377
        if (in_array('all', $cascades)) {
1540 41
            $cascades = $allCascades;
1541 370
        } elseif (count($cascades) !== count(array_intersect($cascades, $allCascades))) {
1542 1
            throw MappingException::invalidCascadeOption(
1543 1
                array_diff($cascades, $allCascades),
1544 1
                $this->name,
1545 1
                $mapping['fieldName']
1546
            );
1547
        }
1548
1549 376
        $mapping['cascade'] = $cascades;
1550 376
        $mapping['isCascadeRemove']  = in_array('remove', $cascades);
1551 376
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1552 376
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1553 376
        $mapping['isCascadeMerge']   = in_array('merge', $cascades);
1554 376
        $mapping['isCascadeDetach']  = in_array('detach', $cascades);
1555
1556 376
        return $mapping;
1557
    }
1558
1559
    /**
1560
     * Validates & completes a one-to-one association mapping.
1561
     *
1562
     * @param array $mapping The mapping to validate & complete.
1563
     *
1564
     * @return array The validated & completed mapping.
1565
     *
1566
     * @throws RuntimeException
1567
     * @throws MappingException
1568
     */
1569 326
    protected function _validateAndCompleteOneToOneMapping(array $mapping)
1570
    {
1571 326
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1572
1573 320
        if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
1574 229
            $mapping['isOwningSide'] = true;
1575
        }
1576
1577 320
        if ($mapping['isOwningSide']) {
1578 307
            if (empty($mapping['joinColumns'])) {
1579
                // Apply default join column
1580 99
                $mapping['joinColumns'] = [
1581
                    [
1582 99
                        '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

1582
                        '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...
1583 99
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName()
1584
                    ]
1585
                ];
1586
            }
1587
1588 307
            $uniqueConstraintColumns = [];
1589
1590 307
            foreach ($mapping['joinColumns'] as &$joinColumn) {
1591 307
                if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
1592 166
                    if (count($mapping['joinColumns']) === 1) {
1593 164
                        if (empty($mapping['id'])) {
1594 164
                            $joinColumn['unique'] = true;
1595
                        }
1596
                    } else {
1597 2
                        $uniqueConstraintColumns[] = $joinColumn['name'];
1598
                    }
1599
                }
1600
1601 307
                if (empty($joinColumn['name'])) {
1602 35
                    $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name);
1603
                }
1604
1605 307
                if (empty($joinColumn['referencedColumnName'])) {
1606 6
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1607
                }
1608
1609 307
                if ($joinColumn['name'][0] === '`') {
1610 7
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1611 7
                    $joinColumn['quoted'] = true;
1612
                }
1613
1614 307
                if ($joinColumn['referencedColumnName'][0] === '`') {
1615 4
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1616 4
                    $joinColumn['quoted']               = true;
1617
                }
1618
1619 307
                $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1620 307
                $mapping['joinColumnFieldNames'][$joinColumn['name']] = $joinColumn['fieldName'] ?? $joinColumn['name'];
1621
            }
1622
1623 307
            if ($uniqueConstraintColumns) {
1624 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...
1625
                    throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
1626
                }
1627
1628 2
                $this->table['uniqueConstraints'][$mapping['fieldName'] . "_uniq"] = [
1629 2
                    'columns' => $uniqueConstraintColumns
1630
                ];
1631
            }
1632
1633 307
            $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
1634
        }
1635
1636 320
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'];
1637 320
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove'];
1638
1639 320
        if ($mapping['orphanRemoval']) {
1640 22
            unset($mapping['unique']);
1641
        }
1642
1643 320 View Code Duplication
        if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1644 2
            throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']);
1645
        }
1646
1647 318
        return $mapping;
1648
    }
1649
1650
    /**
1651
     * Validates & completes a one-to-many association mapping.
1652
     *
1653
     * @param array $mapping The mapping to validate and complete.
1654
     *
1655
     * @return array The validated and completed mapping.
1656
     *
1657
     * @throws MappingException
1658
     * @throws InvalidArgumentException
1659
     */
1660 144
    protected function _validateAndCompleteOneToManyMapping(array $mapping)
1661
    {
1662 144
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1663
1664
        // OneToMany-side MUST be inverse (must have mappedBy)
1665 143
        if ( ! isset($mapping['mappedBy'])) {
1666
            throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
1667
        }
1668
1669 143
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'];
1670 143
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove'];
1671
1672 143
        $this->assertMappingOrderBy($mapping);
1673
1674 143
        return $mapping;
1675
    }
1676
1677
    /**
1678
     * Validates & completes a many-to-many association mapping.
1679
     *
1680
     * @param array $mapping The mapping to validate & complete.
1681
     *
1682
     * @return array The validated & completed mapping.
1683
     *
1684
     * @throws \InvalidArgumentException
1685
     */
1686 169
    protected function _validateAndCompleteManyToManyMapping(array $mapping)
1687
    {
1688 169
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1689
1690 167
        if ($mapping['isOwningSide']) {
1691
            // owning side MUST have a join table
1692 149
            if ( ! isset($mapping['joinTable']['name'])) {
1693 28
                $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']);
1694
            }
1695
1696 149
            $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity']
1697 149
                && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns'])));
1698
1699 149 View Code Duplication
            if ( ! isset($mapping['joinTable']['joinColumns'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1700 27
                $mapping['joinTable']['joinColumns'] = [
1701
                    [
1702 27
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null),
1703 27
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1704 27
                        'onDelete' => 'CASCADE'
1705
                    ]
1706
                ];
1707
            }
1708
1709 149 View Code Duplication
            if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1710 28
                $mapping['joinTable']['inverseJoinColumns'] = [
1711
                    [
1712 28
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null),
1713 28
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1714 28
                        'onDelete' => 'CASCADE'
1715
                    ]
1716
                ];
1717
            }
1718
1719 149
            $mapping['joinTableColumns'] = [];
1720
1721 149 View Code Duplication
            foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1722 149
                if (empty($joinColumn['name'])) {
1723 2
                    $joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']);
1724
                }
1725
1726 149
                if (empty($joinColumn['referencedColumnName'])) {
1727 6
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1728
                }
1729
1730 149
                if ($joinColumn['name'][0] === '`') {
1731 3
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1732 3
                    $joinColumn['quoted'] = true;
1733
                }
1734
1735 149
                if ($joinColumn['referencedColumnName'][0] === '`') {
1736 3
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1737 3
                    $joinColumn['quoted']               = true;
1738
                }
1739
1740 149
                if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
1741 37
                    $mapping['isOnDeleteCascade'] = true;
1742
                }
1743
1744 149
                $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1745 149
                $mapping['joinTableColumns'][] = $joinColumn['name'];
1746
            }
1747
1748 149 View Code Duplication
            foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1749 149
                if (empty($inverseJoinColumn['name'])) {
1750 2
                    $inverseJoinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']);
1751
                }
1752
1753 149
                if (empty($inverseJoinColumn['referencedColumnName'])) {
1754 6
                    $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1755
                }
1756
1757 149
                if ($inverseJoinColumn['name'][0] === '`') {
1758 3
                    $inverseJoinColumn['name']   = trim($inverseJoinColumn['name'], '`');
1759 3
                    $inverseJoinColumn['quoted'] = true;
1760
                }
1761
1762 149
                if ($inverseJoinColumn['referencedColumnName'][0] === '`') {
1763 3
                    $inverseJoinColumn['referencedColumnName']  = trim($inverseJoinColumn['referencedColumnName'], '`');
1764 3
                    $inverseJoinColumn['quoted']                = true;
1765
                }
1766
1767 149
                if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
1768 33
                    $mapping['isOnDeleteCascade'] = true;
1769
                }
1770
1771 149
                $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
1772 149
                $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
1773
            }
1774
        }
1775
1776 167
        $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'];
1777
1778 167
        $this->assertMappingOrderBy($mapping);
1779
1780 167
        return $mapping;
1781
    }
1782
1783
    /**
1784
     * {@inheritDoc}
1785
     */
1786 633
    public function getIdentifierFieldNames()
1787
    {
1788 633
        return $this->identifier;
1789
    }
1790
1791
    /**
1792
     * Gets the name of the single id field. Note that this only works on
1793
     * entity classes that have a single-field pk.
1794
     *
1795
     * @return string
1796
     *
1797
     * @throws MappingException If the class doesn't have an identifier or it has a composite primary key.
1798
     */
1799 1189
    public function getSingleIdentifierFieldName()
1800
    {
1801 1189
        if ($this->isIdentifierComposite) {
1802 1
            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
1803
        }
1804
1805 1188
        if ( ! isset($this->identifier[0])) {
1806 1
            throw MappingException::noIdDefined($this->name);
1807
        }
1808
1809 1187
        return $this->identifier[0];
1810
    }
1811
1812
    /**
1813
     * Gets the column name of the single id column. Note that this only works on
1814
     * entity classes that have a single-field pk.
1815
     *
1816
     * @return string
1817
     *
1818
     * @throws MappingException If the class doesn't have an identifier or it has a composite primary key.
1819
     */
1820 3
    public function getSingleIdentifierColumnName()
1821
    {
1822 3
        return $this->getColumnName($this->getSingleIdentifierFieldName());
1823
    }
1824
1825
    /**
1826
     * INTERNAL:
1827
     * Sets the mapped identifier/primary key fields of this class.
1828
     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1829
     *
1830
     * @param array $identifier
1831
     *
1832
     * @return void
1833
     */
1834 145
    public function setIdentifier(array $identifier)
1835
    {
1836 145
        $this->identifier = $identifier;
1837 145
        $this->isIdentifierComposite = (count($this->identifier) > 1);
1838 145
    }
1839
1840
    /**
1841
     * {@inheritDoc}
1842
     */
1843 65
    public function getIdentifier()
1844
    {
1845 65
        return $this->identifier;
1846
    }
1847
1848
    /**
1849
     * {@inheritDoc}
1850
     */
1851 306
    public function hasField($fieldName)
1852
    {
1853 306
        return isset($this->fieldMappings[$fieldName]) || isset($this->embeddedClasses[$fieldName]);
1854
    }
1855
1856
    /**
1857
     * Gets an array containing all the column names.
1858
     *
1859
     * @param array|null $fieldNames
1860
     *
1861
     * @return array
1862
     */
1863 49
    public function getColumnNames(array $fieldNames = null)
1864
    {
1865 49
        if (null === $fieldNames) {
1866 48
            return array_keys($this->fieldNames);
1867
        }
1868
1869 1
        return array_values(array_map([$this, 'getColumnName'], $fieldNames));
1870
    }
1871
1872
    /**
1873
     * Returns an array with all the identifier column names.
1874
     *
1875
     * @return array
1876
     */
1877 342
    public function getIdentifierColumnNames()
1878
    {
1879 342
        $columnNames = [];
1880
1881 342
        foreach ($this->identifier as $idProperty) {
1882 342
            if (isset($this->fieldMappings[$idProperty])) {
1883 338
                $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
1884
1885 338
                continue;
1886
            }
1887
1888
            // Association defined as Id field
1889 22
            $joinColumns      = $this->associationMappings[$idProperty]['joinColumns'];
1890
            $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
1891
1892 22
            $columnNames = array_merge($columnNames, $assocColumnNames);
1893
        }
1894
1895 342
        return $columnNames;
1896
    }
1897
1898
    /**
1899
     * Sets the type of Id generator to use for the mapped class.
1900
     *
1901
     * @param int $generatorType
1902
     *
1903
     * @return void
1904
     */
1905 507
    public function setIdGeneratorType($generatorType)
1906
    {
1907 507
        $this->generatorType = $generatorType;
1908 507
    }
1909
1910
    /**
1911
     * Checks whether the mapped class uses an Id generator.
1912
     *
1913
     * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
1914
     */
1915 450
    public function usesIdGenerator()
1916
    {
1917 450
        return $this->generatorType != self::GENERATOR_TYPE_NONE;
1918
    }
1919
1920
    /**
1921
     * @return boolean
1922
     */
1923 1415
    public function isInheritanceTypeNone()
1924
    {
1925 1415
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1926
    }
1927
1928
    /**
1929
     * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
1930
     *
1931
     * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
1932
     *                 FALSE otherwise.
1933
     */
1934 1093
    public function isInheritanceTypeJoined()
1935
    {
1936 1093
        return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
1937
    }
1938
1939
    /**
1940
     * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
1941
     *
1942
     * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
1943
     *                 FALSE otherwise.
1944
     */
1945 1295
    public function isInheritanceTypeSingleTable()
1946
    {
1947 1295
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
1948
    }
1949
1950
    /**
1951
     * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
1952
     *
1953
     * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
1954
     *                 FALSE otherwise.
1955
     */
1956 270
    public function isInheritanceTypeTablePerClass()
1957
    {
1958 270
        return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
1959
    }
1960
1961
    /**
1962
     * Checks whether the class uses an identity column for the Id generation.
1963
     *
1964
     * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
1965
     */
1966 1121
    public function isIdGeneratorIdentity()
1967
    {
1968 1121
        return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
1969
    }
1970
1971
    /**
1972
     * Checks whether the class uses a sequence for id generation.
1973
     *
1974
     * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
1975
     */
1976 341
    public function isIdGeneratorSequence()
1977
    {
1978 341
        return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
1979
    }
1980
1981
    /**
1982
     * Checks whether the class uses a table for id generation.
1983
     *
1984
     * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
1985
     */
1986 99
    public function isIdGeneratorTable()
1987
    {
1988 99
        return $this->generatorType == self::GENERATOR_TYPE_TABLE;
1989
    }
1990
1991
    /**
1992
     * Checks whether the class has a natural identifier/pk (which means it does
1993
     * not use any Id generator.
1994
     *
1995
     * @return boolean
1996
     */
1997 75
    public function isIdentifierNatural()
1998
    {
1999 75
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
2000
    }
2001
2002
    /**
2003
     * Checks whether the class use a UUID for id generation.
2004
     *
2005
     * @return boolean
2006
     */
2007
    public function isIdentifierUuid()
2008
    {
2009
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
2010
    }
2011
2012
    /**
2013
     * Gets the type of a field.
2014
     *
2015
     * @param string $fieldName
2016
     *
2017
     * @return \Doctrine\DBAL\Types\Type|string|null
2018
     *
2019
     * @todo 3.0 Remove this. PersisterHelper should fix it somehow
2020
     */
2021 962
    public function getTypeOfField($fieldName)
2022
    {
2023 962
        return isset($this->fieldMappings[$fieldName])
2024 962
            ? $this->fieldMappings[$fieldName]['type']
2025 962
            : null;
2026
    }
2027
2028
    /**
2029
     * Gets the type of a column.
2030
     *
2031
     * @param string $columnName
2032
     *
2033
     * @return \Doctrine\DBAL\Types\Type|string|null
2034
     *
2035
     * @deprecated 3.0 remove this. this method is bogus and unreliable, since it cannot resolve the type of a column
2036
     *             that is derived by a referenced field on a different entity.
2037
     */
2038
    public function getTypeOfColumn($columnName)
2039
    {
2040
        return $this->getTypeOfField($this->getFieldName($columnName));
2041
    }
2042
2043
    /**
2044
     * Gets the name of the primary table.
2045
     *
2046
     * @return string
2047
     */
2048 1405
    public function getTableName()
2049
    {
2050 1405
        return $this->table['name'];
2051
    }
2052
2053
    /**
2054
     * Gets primary table's schema name.
2055
     *
2056
     * @return string|null
2057
     */
2058 13
    public function getSchemaName()
2059
    {
2060 13
        return isset($this->table['schema']) ? $this->table['schema'] : null;
2061
    }
2062
2063
    /**
2064
     * Gets the table name to use for temporary identifier tables of this class.
2065
     *
2066
     * @return string
2067
     */
2068 7
    public function getTemporaryIdTableName()
2069
    {
2070
        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
2071 7
        return str_replace('.', '_', $this->getTableName() . '_id_tmp');
2072
    }
2073
2074
    /**
2075
     * Sets the mapped subclasses of this class.
2076
     *
2077
     * @param array $subclasses The names of all mapped subclasses.
2078
     *
2079
     * @return void
2080
     */
2081 2
    public function setSubclasses(array $subclasses)
2082
    {
2083 2
        foreach ($subclasses as $subclass) {
2084 2
            $this->subClasses[] = $this->fullyQualifiedClassName($subclass);
2085
        }
2086 2
    }
2087
2088
    /**
2089
     * Sets the parent class names.
2090
     * Assumes that the class names in the passed array are in the order:
2091
     * directParent -> directParentParent -> directParentParentParent ... -> root.
2092
     *
2093
     * @param array $classNames
2094
     *
2095
     * @return void
2096
     */
2097 467
    public function setParentClasses(array $classNames)
2098
    {
2099 467
        $this->parentClasses = $classNames;
2100
2101 467
        if (count($classNames) > 0) {
2102 98
            $this->rootEntityName = array_pop($classNames);
2103
        }
2104 467
    }
2105
2106
    /**
2107
     * Sets the inheritance type used by the class and its subclasses.
2108
     *
2109
     * @param integer $type
2110
     *
2111
     * @return void
2112
     *
2113
     * @throws MappingException
2114
     */
2115 195
    public function setInheritanceType($type)
2116
    {
2117 195
        if ( ! $this->_isInheritanceType($type)) {
2118
            throw MappingException::invalidInheritanceType($this->name, $type);
2119
        }
2120
2121 195
        $this->inheritanceType = $type;
2122 195
    }
2123
2124
    /**
2125
     * Sets the association to override association mapping of property for an entity relationship.
2126
     *
2127
     * @param string $fieldName
2128
     * @param array  $overrideMapping
2129
     *
2130
     * @return void
2131
     *
2132
     * @throws MappingException
2133
     */
2134 26
    public function setAssociationOverride($fieldName, array $overrideMapping)
2135
    {
2136 26
        if ( ! isset($this->associationMappings[$fieldName])) {
2137 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2138
        }
2139
2140 25
        $mapping = $this->associationMappings[$fieldName];
2141
2142 25
        if (isset($overrideMapping['joinColumns'])) {
2143 13
            $mapping['joinColumns'] = $overrideMapping['joinColumns'];
2144
        }
2145
2146 25
        if (isset($overrideMapping['inversedBy'])) {
2147 6
            $mapping['inversedBy'] = $overrideMapping['inversedBy'];
2148
        }
2149
2150 25
        if (isset($overrideMapping['joinTable'])) {
2151 12
            $mapping['joinTable'] = $overrideMapping['joinTable'];
2152
        }
2153
2154 25
        if (isset($overrideMapping['fetch'])) {
2155 6
            $mapping['fetch'] = $overrideMapping['fetch'];
2156
        }
2157
2158 25
        $mapping['joinColumnFieldNames']        = null;
2159 25
        $mapping['joinTableColumns']            = null;
2160 25
        $mapping['sourceToTargetKeyColumns']    = null;
2161 25
        $mapping['relationToSourceKeyColumns']  = null;
2162 25
        $mapping['relationToTargetKeyColumns']  = null;
2163
2164 25
        switch ($mapping['type']) {
2165 25
            case self::ONE_TO_ONE:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

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

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

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

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

Loading history...
2166 1
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2167 1
                break;
2168 24
            case self::ONE_TO_MANY:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

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

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

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

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

Loading history...
2169
                $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2170
                break;
2171 24
            case self::MANY_TO_ONE:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

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

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

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

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

Loading history...
2172 12
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2173 12
                break;
2174 24
            case self::MANY_TO_MANY:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

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

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

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

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

Loading history...
2175 24
                $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2176 24
                break;
2177
        }
2178
2179 25
        $this->associationMappings[$fieldName] = $mapping;
2180 25
    }
2181
2182
    /**
2183
     * Sets the override for a mapped field.
2184
     *
2185
     * @param string $fieldName
2186
     * @param array  $overrideMapping
2187
     *
2188
     * @return void
2189
     *
2190
     * @throws MappingException
2191
     */
2192 15
    public function setAttributeOverride($fieldName, array $overrideMapping)
2193
    {
2194 15
        if ( ! isset($this->fieldMappings[$fieldName])) {
2195 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2196
        }
2197
2198 14
        $mapping = $this->fieldMappings[$fieldName];
2199
2200 14
        if (isset($mapping['id'])) {
2201 12
            $overrideMapping['id'] = $mapping['id'];
2202
        }
2203
2204 14
        if ( ! isset($overrideMapping['type'])) {
2205 6
            $overrideMapping['type'] = $mapping['type'];
2206
        }
2207
2208 14
        if ( ! isset($overrideMapping['fieldName'])) {
2209 5
            $overrideMapping['fieldName'] = $mapping['fieldName'];
2210
        }
2211
2212 14
        if ($overrideMapping['type'] !== $mapping['type']) {
2213 1
            throw MappingException::invalidOverrideFieldType($this->name, $fieldName);
2214
        }
2215
2216 13
        unset($this->fieldMappings[$fieldName]);
2217 13
        unset($this->fieldNames[$mapping['columnName']]);
2218 13
        unset($this->columnNames[$mapping['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

2218
        unset(/** @scrutinizer ignore-deprecated */ $this->columnNames[$mapping['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...
2219
2220 13
        $this->_validateAndCompleteFieldMapping($overrideMapping);
2221
2222 13
        $this->fieldMappings[$fieldName] = $overrideMapping;
2223 13
    }
2224
2225
    /**
2226
     * Checks whether a mapped field is inherited from an entity superclass.
2227
     *
2228
     * @param string $fieldName
2229
     *
2230
     * @return bool TRUE if the field is inherited, FALSE otherwise.
2231
     */
2232 424
    public function isInheritedField($fieldName)
2233
    {
2234 424
        return isset($this->fieldMappings[$fieldName]['inherited']);
2235
    }
2236
2237
    /**
2238
     * Checks if this entity is the root in any entity-inheritance-hierarchy.
2239
     *
2240
     * @return bool
2241
     */
2242 466
    public function isRootEntity()
2243
    {
2244 466
        return $this->name == $this->rootEntityName;
2245
    }
2246
2247
    /**
2248
     * Checks whether a mapped association field is inherited from a superclass.
2249
     *
2250
     * @param string $fieldName
2251
     *
2252
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
2253
     */
2254 401
    public function isInheritedAssociation($fieldName)
2255
    {
2256 401
        return isset($this->associationMappings[$fieldName]['inherited']);
2257
    }
2258
2259 401
    public function isInheritedEmbeddedClass($fieldName)
2260
    {
2261 401
        return isset($this->embeddedClasses[$fieldName]['inherited']);
2262
    }
2263
2264
    /**
2265
     * Sets the name of the primary table the class is mapped to.
2266
     *
2267
     * @param string $tableName The table name.
2268
     *
2269
     * @return void
2270
     *
2271
     * @deprecated Use {@link setPrimaryTable}.
2272
     */
2273 5
    public function setTableName($tableName)
2274
    {
2275 5
        $this->table['name'] = $tableName;
2276 5
    }
2277
2278
    /**
2279
     * Sets the primary table definition. The provided array supports the
2280
     * following structure:
2281
     *
2282
     * name => <tableName> (optional, defaults to class name)
2283
     * indexes => array of indexes (optional)
2284
     * uniqueConstraints => array of constraints (optional)
2285
     *
2286
     * If a key is omitted, the current value is kept.
2287
     *
2288
     * @param array $table The table description.
2289
     *
2290
     * @return void
2291
     */
2292 357
    public function setPrimaryTable(array $table)
2293
    {
2294 357
        if (isset($table['name'])) {
2295
            // Split schema and table name from a table name like "myschema.mytable"
2296 290
            if (strpos($table['name'], '.') !== false) {
2297 9
                list($this->table['schema'], $table['name']) = explode('.', $table['name'], 2);
2298
            }
2299
2300 290
            if ($table['name'][0] === '`') {
2301 17
                $table['name']          = trim($table['name'], '`');
2302 17
                $this->table['quoted']  = true;
2303
            }
2304
2305 290
            $this->table['name'] = $table['name'];
2306
        }
2307
2308 357
        if (isset($table['quoted'])) {
2309 2
            $this->table['quoted'] = $table['quoted'];
2310
        }
2311
2312 357
        if (isset($table['schema'])) {
2313 6
            $this->table['schema'] = $table['schema'];
2314
        }
2315
2316 357
        if (isset($table['indexes'])) {
2317 18
            $this->table['indexes'] = $table['indexes'];
2318
        }
2319
2320 357
        if (isset($table['uniqueConstraints'])) {
2321 9
            $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
2322
        }
2323
2324 357
        if (isset($table['options'])) {
2325 11
            $this->table['options'] = $table['options'];
2326
        }
2327 357
    }
2328
2329
    /**
2330
     * Checks whether the given type identifies an inheritance type.
2331
     *
2332
     * @param integer $type
2333
     *
2334
     * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
2335
     */
2336 195
    private function _isInheritanceType($type)
2337
    {
2338 195
        return $type == self::INHERITANCE_TYPE_NONE ||
2339 122
                $type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
2340 61
                $type == self::INHERITANCE_TYPE_JOINED ||
2341 195
                $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
2342
    }
2343
2344
    /**
2345
     * Adds a mapped field to the class.
2346
     *
2347
     * @param array $mapping The field mapping.
2348
     *
2349
     * @return void
2350
     *
2351
     * @throws MappingException
2352
     */
2353 594
    public function mapField(array $mapping)
2354
    {
2355 594
        $this->_validateAndCompleteFieldMapping($mapping);
2356 592
        $this->assertFieldNotMapped($mapping['fieldName']);
2357
2358 591
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
2359 591
    }
2360
2361
    /**
2362
     * INTERNAL:
2363
     * Adds an association mapping without completing/validating it.
2364
     * This is mainly used to add inherited association mappings to derived classes.
2365
     *
2366
     * @param array $mapping
2367
     *
2368
     * @return void
2369
     *
2370
     * @throws MappingException
2371
     */
2372 58
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
2373
    {
2374 58
        if (isset($this->associationMappings[$mapping['fieldName']])) {
2375 1
            throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
2376
        }
2377 58
        $this->associationMappings[$mapping['fieldName']] = $mapping;
2378 58
    }
2379
2380
    /**
2381
     * INTERNAL:
2382
     * Adds a field mapping without completing/validating it.
2383
     * This is mainly used to add inherited field mappings to derived classes.
2384
     *
2385
     * @param array $fieldMapping
2386
     *
2387
     * @return void
2388
     */
2389 129
    public function addInheritedFieldMapping(array $fieldMapping)
2390
    {
2391 129
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
2392 129
        $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

2392
        /** @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...
2393 129
        $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
2394 129
    }
2395
2396
    /**
2397
     * INTERNAL:
2398
     * Adds a named query to this class.
2399
     *
2400
     * @param array $queryMapping
2401
     *
2402
     * @return void
2403
     *
2404
     * @throws MappingException
2405
     */
2406 36
    public function addNamedQuery(array $queryMapping)
2407
    {
2408 36
        if (!isset($queryMapping['name'])) {
2409 2
            throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2410
        }
2411
2412 34 View Code Duplication
        if (isset($this->namedQueries[$queryMapping['name']])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2413 1
            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2414
        }
2415
2416 34
        if (!isset($queryMapping['query'])) {
2417
            throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2418
        }
2419
2420 34
        $name   = $queryMapping['name'];
2421 34
        $query  = $queryMapping['query'];
2422 34
        $dql    = str_replace('__CLASS__', $this->name, $query);
2423
2424 34
        $this->namedQueries[$name] = [
2425 34
            'name'  => $name,
2426 34
            'query' => $query,
2427 34
            'dql'   => $dql,
2428
        ];
2429 34
    }
2430
2431
    /**
2432
     * INTERNAL:
2433
     * Adds a named native query to this class.
2434
     *
2435
     * @param array $queryMapping
2436
     *
2437
     * @return void
2438
     *
2439
     * @throws MappingException
2440
     */
2441 41
    public function addNamedNativeQuery(array $queryMapping)
2442
    {
2443 41
        if (!isset($queryMapping['name'])) {
2444
            throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2445
        }
2446
2447 41 View Code Duplication
        if (isset($this->namedNativeQueries[$queryMapping['name']])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2448 1
            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2449
        }
2450
2451 41
        if (!isset($queryMapping['query'])) {
2452
            throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2453
        }
2454
2455 41
        if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) {
2456
            throw MappingException::missingQueryMapping($this->name, $queryMapping['name']);
2457
        }
2458
2459 41
        $queryMapping['isSelfClass'] = false;
2460
2461 41
        if (isset($queryMapping['resultClass'])) {
2462 39
            if ($queryMapping['resultClass'] === '__CLASS__') {
2463
2464 12
                $queryMapping['isSelfClass'] = true;
2465 12
                $queryMapping['resultClass'] = $this->name;
2466
            }
2467
2468 39
            $queryMapping['resultClass'] = $this->fullyQualifiedClassName($queryMapping['resultClass']);
2469 39
            $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\');
2470
        }
2471
2472 41
        $this->namedNativeQueries[$queryMapping['name']] = $queryMapping;
2473 41
    }
2474
2475
    /**
2476
     * INTERNAL:
2477
     * Adds a sql result set mapping to this class.
2478
     *
2479
     * @param array $resultMapping
2480
     *
2481
     * @return void
2482
     *
2483
     * @throws MappingException
2484
     */
2485 41
    public function addSqlResultSetMapping(array $resultMapping)
2486
    {
2487 41
        if (!isset($resultMapping['name'])) {
2488
            throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name);
2489
        }
2490
2491 41
        if (isset($this->sqlResultSetMappings[$resultMapping['name']])) {
2492 1
            throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']);
2493
        }
2494
2495 41
        if (isset($resultMapping['entities'])) {
2496 41
            foreach ($resultMapping['entities'] as $key => $entityResult) {
2497 41
                if (!isset($entityResult['entityClass'])) {
2498 1
                    throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']);
2499
                }
2500
2501 40
                $entityResult['isSelfClass'] = false;
2502 40
                if ($entityResult['entityClass'] === '__CLASS__') {
2503
2504 22
                    $entityResult['isSelfClass'] = true;
2505 22
                    $entityResult['entityClass'] = $this->name;
2506
2507
                }
2508
2509 40
                $entityResult['entityClass'] = $this->fullyQualifiedClassName($entityResult['entityClass']);
2510
2511 40
                $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\');
2512 40
                $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass'];
2513
2514 40
                if (isset($entityResult['fields'])) {
2515 36
                    foreach ($entityResult['fields'] as $k => $field) {
2516 36
                        if (!isset($field['name'])) {
2517
                            throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']);
2518
                        }
2519
2520 36
                        if (!isset($field['column'])) {
2521 16
                            $fieldName = $field['name'];
2522 16
                            if (strpos($fieldName, '.')) {
2523 10
                                list(, $fieldName) = explode('.', $fieldName);
2524
                            }
2525
2526 40
                            $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName;
2527
                        }
2528
                    }
2529
                }
2530
            }
2531
        }
2532
2533 40
        $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping;
2534 40
    }
2535
2536
    /**
2537
     * Adds a one-to-one mapping.
2538
     *
2539
     * @param array $mapping The mapping.
2540
     *
2541
     * @return void
2542
     */
2543 185
    public function mapOneToOne(array $mapping)
2544
    {
2545 185
        $mapping['type'] = self::ONE_TO_ONE;
2546
2547 185
        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2548
2549 182
        $this->_storeAssociationMapping($mapping);
2550 181
    }
2551
2552
    /**
2553
     * Adds a one-to-many mapping.
2554
     *
2555
     * @param array $mapping The mapping.
2556
     *
2557
     * @return void
2558
     */
2559 144
    public function mapOneToMany(array $mapping)
2560
    {
2561 144
        $mapping['type'] = self::ONE_TO_MANY;
2562
2563 144
        $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2564
2565 143
        $this->_storeAssociationMapping($mapping);
2566 143
    }
2567
2568
    /**
2569
     * Adds a many-to-one mapping.
2570
     *
2571
     * @param array $mapping The mapping.
2572
     *
2573
     * @return void
2574
     */
2575 175
    public function mapManyToOne(array $mapping)
2576
    {
2577 175
        $mapping['type'] = self::MANY_TO_ONE;
2578
2579
        // A many-to-one mapping is essentially a one-one backreference
2580 175
        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2581
2582 170
        $this->_storeAssociationMapping($mapping);
2583 170
    }
2584
2585
    /**
2586
     * Adds a many-to-many mapping.
2587
     *
2588
     * @param array $mapping The mapping.
2589
     *
2590
     * @return void
2591
     */
2592 169
    public function mapManyToMany(array $mapping)
2593
    {
2594 169
        $mapping['type'] = self::MANY_TO_MANY;
2595
2596 169
        $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2597
2598 167
        $this->_storeAssociationMapping($mapping);
2599 167
    }
2600
2601
    /**
2602
     * Stores the association mapping.
2603
     *
2604
     * @param array $assocMapping
2605
     *
2606
     * @return void
2607
     *
2608
     * @throws MappingException
2609
     */
2610 374
    protected function _storeAssociationMapping(array $assocMapping)
2611
    {
2612 374
        $sourceFieldName = $assocMapping['fieldName'];
2613
2614 374
        $this->assertFieldNotMapped($sourceFieldName);
2615
2616 373
        $this->associationMappings[$sourceFieldName] = $assocMapping;
2617 373
    }
2618
2619
    /**
2620
     * Registers a custom repository class for the entity class.
2621
     *
2622
     * @param string $repositoryClassName The class name of the custom mapper.
2623
     *
2624
     * @return void
2625
     */
2626 65
    public function setCustomRepositoryClass($repositoryClassName)
2627
    {
2628 65
        $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName);
2629 65
    }
2630
2631
    /**
2632
     * Dispatches the lifecycle event of the given entity to the registered
2633
     * lifecycle callbacks and lifecycle listeners.
2634
     *
2635
     * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker
2636
     *
2637
     * @param string $lifecycleEvent The lifecycle event.
2638
     * @param object $entity         The Entity on which the event occurred.
2639
     *
2640
     * @return void
2641
     */
2642 1
    public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
2643
    {
2644 1
        foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
2645 1
            $entity->$callback();
2646
        }
2647 1
    }
2648
2649
    /**
2650
     * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
2651
     *
2652
     * @param string $lifecycleEvent
2653
     *
2654
     * @return boolean
2655
     */
2656
    public function hasLifecycleCallbacks($lifecycleEvent)
2657
    {
2658
        return isset($this->lifecycleCallbacks[$lifecycleEvent]);
2659
    }
2660
2661
    /**
2662
     * Gets the registered lifecycle callbacks for an event.
2663
     *
2664
     * @param string $event
2665
     *
2666
     * @return array
2667
     */
2668
    public function getLifecycleCallbacks($event)
2669
    {
2670
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : [];
2671
    }
2672
2673
    /**
2674
     * Adds a lifecycle callback for entities of this class.
2675
     *
2676
     * @param string $callback
2677
     * @param string $event
2678
     *
2679
     * @return void
2680
     */
2681 47
    public function addLifecycleCallback($callback, $event)
2682
    {
2683 47
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
2684 3
            return;
2685
        }
2686
2687 47
        $this->lifecycleCallbacks[$event][] = $callback;
2688 47
    }
2689
2690
    /**
2691
     * Sets the lifecycle callbacks for entities of this class.
2692
     * Any previously registered callbacks are overwritten.
2693
     *
2694
     * @param array $callbacks
2695
     *
2696
     * @return void
2697
     */
2698 144
    public function setLifecycleCallbacks(array $callbacks)
2699
    {
2700 144
        $this->lifecycleCallbacks = $callbacks;
2701 144
    }
2702
2703
    /**
2704
     * Adds a entity listener for entities of this class.
2705
     *
2706
     * @param string $eventName The entity lifecycle event.
2707
     * @param string $class     The listener class.
2708
     * @param string $method    The listener callback method.
2709
     *
2710
     * @throws \Doctrine\ORM\Mapping\MappingException
2711
     */
2712 43
    public function addEntityListener($eventName, $class, $method)
2713
    {
2714 43
        $class    = $this->fullyQualifiedClassName($class);
2715
2716
        $listener = [
2717 43
            'class'  => $class,
2718 43
            'method' => $method,
2719
        ];
2720
2721 43
        if ( ! class_exists($class)) {
2722 1
            throw MappingException::entityListenerClassNotFound($class, $this->name);
2723
        }
2724
2725 42
        if ( ! method_exists($class, $method)) {
2726 1
            throw MappingException::entityListenerMethodNotFound($class, $method, $this->name);
2727
        }
2728
2729 41
        if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName])) {
2730 1
            throw MappingException::duplicateEntityListener($class, $method, $this->name);
2731
        }
2732
2733 41
        $this->entityListeners[$eventName][] = $listener;
2734 41
    }
2735
2736
    /**
2737
     * Sets the discriminator column definition.
2738
     *
2739
     * @param array $columnDef
2740
     *
2741
     * @return void
2742
     *
2743
     * @throws MappingException
2744
     *
2745
     * @see getDiscriminatorColumn()
2746
     */
2747 187
    public function setDiscriminatorColumn($columnDef)
2748
    {
2749 187
        if ($columnDef !== null) {
2750 126
            if ( ! isset($columnDef['name'])) {
2751 1
                throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name);
2752
            }
2753
2754 125
            if (isset($this->fieldNames[$columnDef['name']])) {
2755 1
                throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
2756
            }
2757
2758 124
            if ( ! isset($columnDef['fieldName'])) {
2759 119
                $columnDef['fieldName'] = $columnDef['name'];
2760
            }
2761
2762 124
            if ( ! isset($columnDef['type'])) {
2763 2
                $columnDef['type'] = "string";
2764
            }
2765
2766 124
            if (in_array($columnDef['type'], ["boolean", "array", "object", "datetime", "time", "date"])) {
2767
                throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
2768
            }
2769
2770 124
            $this->discriminatorColumn = $columnDef;
2771
        }
2772 185
    }
2773
2774
    /**
2775
     * Sets the discriminator values used by this class.
2776
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
2777
     *
2778
     * @param array $map
2779
     *
2780
     * @return void
2781
     */
2782 179
    public function setDiscriminatorMap(array $map)
2783
    {
2784 179
        foreach ($map as $value => $className) {
2785 119
            $this->addDiscriminatorMapClass($value, $className);
2786
        }
2787 179
    }
2788
2789
    /**
2790
     * Adds one entry of the discriminator map with a new class and corresponding name.
2791
     *
2792
     * @param string $name
2793
     * @param string $className
2794
     *
2795
     * @return void
2796
     *
2797
     * @throws MappingException
2798
     */
2799 120
    public function addDiscriminatorMapClass($name, $className)
2800
    {
2801 120
        $className = $this->fullyQualifiedClassName($className);
2802 120
        $className = ltrim($className, '\\');
2803
2804 120
        $this->discriminatorMap[$name] = $className;
2805
2806 120
        if ($this->name === $className) {
2807 91
            $this->discriminatorValue = $name;
2808
2809 91
            return;
2810
        }
2811
2812 119
        if ( ! (class_exists($className) || interface_exists($className))) {
2813
            throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
2814
        }
2815
2816 119
        if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
2817 110
            $this->subClasses[] = $className;
2818
        }
2819 119
    }
2820
2821
    /**
2822
     * Checks whether the class has a named query with the given query name.
2823
     *
2824
     * @param string $queryName
2825
     *
2826
     * @return boolean
2827
     */
2828 1
    public function hasNamedQuery($queryName)
2829
    {
2830 1
        return isset($this->namedQueries[$queryName]);
2831
    }
2832
2833
    /**
2834
     * Checks whether the class has a named native query with the given query name.
2835
     *
2836
     * @param string $queryName
2837
     *
2838
     * @return boolean
2839
     */
2840 1
    public function hasNamedNativeQuery($queryName)
2841
    {
2842 1
        return isset($this->namedNativeQueries[$queryName]);
2843
    }
2844
2845
    /**
2846
     * Checks whether the class has a named native query with the given query name.
2847
     *
2848
     * @param string $name
2849
     *
2850
     * @return boolean
2851
     */
2852 1
    public function hasSqlResultSetMapping($name)
2853
    {
2854 1
        return isset($this->sqlResultSetMappings[$name]);
2855
    }
2856
2857
    /**
2858
     * {@inheritDoc}
2859
     */
2860 349
    public function hasAssociation($fieldName)
2861
    {
2862 349
        return isset($this->associationMappings[$fieldName]);
2863
    }
2864
2865
    /**
2866
     * {@inheritDoc}
2867
     */
2868 1
    public function isSingleValuedAssociation($fieldName)
2869
    {
2870 1
        return isset($this->associationMappings[$fieldName])
2871 1
            && ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2872
    }
2873
2874
    /**
2875
     * {@inheritDoc}
2876
     */
2877 1079
    public function isCollectionValuedAssociation($fieldName)
2878
    {
2879 1079
        return isset($this->associationMappings[$fieldName])
2880 1079
            && ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2881
    }
2882
2883
    /**
2884
     * Is this an association that only has a single join column?
2885
     *
2886
     * @param string $fieldName
2887
     *
2888
     * @return bool
2889
     */
2890 99
    public function isAssociationWithSingleJoinColumn($fieldName)
2891
    {
2892 99
        return isset($this->associationMappings[$fieldName])
2893 99
            && isset($this->associationMappings[$fieldName]['joinColumns'][0])
2894 99
            && ! isset($this->associationMappings[$fieldName]['joinColumns'][1]);
2895
    }
2896
2897
    /**
2898
     * Returns the single association join column (if any).
2899
     *
2900
     * @param string $fieldName
2901
     *
2902
     * @return string
2903
     *
2904
     * @throws MappingException
2905
     */
2906 9 View Code Duplication
    public function getSingleAssociationJoinColumnName($fieldName)
2907
    {
2908 9
        if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2909
            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2910
        }
2911
2912 9
        return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
2913
    }
2914
2915
    /**
2916
     * Returns the single association referenced join column name (if any).
2917
     *
2918
     * @param string $fieldName
2919
     *
2920
     * @return string
2921
     *
2922
     * @throws MappingException
2923
     */
2924 9 View Code Duplication
    public function getSingleAssociationReferencedJoinColumnName($fieldName)
2925
    {
2926 9
        if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2927
            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2928
        }
2929
2930 9
        return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
2931
    }
2932
2933
    /**
2934
     * Used to retrieve a fieldname for either field or association from a given column.
2935
     *
2936
     * This method is used in foreign-key as primary-key contexts.
2937
     *
2938
     * @param string $columnName
2939
     *
2940
     * @return string
2941
     *
2942
     * @throws MappingException
2943
     */
2944 673
    public function getFieldForColumn($columnName)
2945
    {
2946 673
        if (isset($this->fieldNames[$columnName])) {
2947 650
            return $this->fieldNames[$columnName];
2948
        }
2949
2950 98
        foreach ($this->associationMappings as $assocName => $mapping) {
2951 98
            if ($this->isAssociationWithSingleJoinColumn($assocName) &&
2952 98
                $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
2953
2954 98
                return $assocName;
2955
            }
2956
        }
2957
2958
        throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
2959
    }
2960
2961
    /**
2962
     * Sets the ID generator used to generate IDs for instances of this class.
2963
     *
2964
     * @param \Doctrine\ORM\Id\AbstractIdGenerator $generator
2965
     *
2966
     * @return void
2967
     */
2968 469
    public function setIdGenerator($generator)
2969
    {
2970 469
        $this->idGenerator = $generator;
2971 469
    }
2972
2973
    /**
2974
     * Sets definition.
2975
     *
2976
     * @param array $definition
2977
     *
2978
     * @return void
2979
     */
2980 13
    public function setCustomGeneratorDefinition(array $definition)
2981
    {
2982 13
        $this->customGeneratorDefinition = $definition;
2983 13
    }
2984
2985
    /**
2986
     * Sets the definition of the sequence ID generator for this class.
2987
     *
2988
     * The definition must have the following structure:
2989
     * <code>
2990
     * array(
2991
     *     'sequenceName'   => 'name',
2992
     *     'allocationSize' => 20,
2993
     *     'initialValue'   => 1
2994
     *     'quoted'         => 1
2995
     * )
2996
     * </code>
2997
     *
2998
     * @param array $definition
2999
     *
3000
     * @return void
3001
     *
3002
     * @throws MappingException
3003
     */
3004 30
    public function setSequenceGeneratorDefinition(array $definition)
3005
    {
3006 30
        if ( ! isset($definition['sequenceName']) || trim($definition['sequenceName']) === '') {
3007 1
            throw MappingException::missingSequenceName($this->name);
3008
        }
3009
3010 29
        if ($definition['sequenceName'][0] == '`') {
3011 1
            $definition['sequenceName']   = trim($definition['sequenceName'], '`');
3012 1
            $definition['quoted'] = true;
3013
        }
3014
3015 29
        if ( ! isset($definition['allocationSize']) || trim($definition['allocationSize']) === '') {
3016 2
            $definition['allocationSize'] = '1';
3017
        }
3018
3019 29
        if ( ! isset($definition['initialValue']) || trim($definition['initialValue']) === '') {
3020 2
            $definition['initialValue'] = '1';
3021
        }
3022
3023 29
        $this->sequenceGeneratorDefinition = $definition;
3024 29
    }
3025
3026
    /**
3027
     * Sets the version field mapping used for versioning. Sets the default
3028
     * value to use depending on the column type.
3029
     *
3030
     * @param array $mapping The version field mapping array.
3031
     *
3032
     * @return void
3033
     *
3034
     * @throws MappingException
3035
     */
3036 33
    public function setVersionMapping(array &$mapping)
3037
    {
3038 33
        $this->isVersioned = true;
3039 33
        $this->versionField = $mapping['fieldName'];
3040
3041 33
        if ( ! isset($mapping['default'])) {
3042 33
            if (in_array($mapping['type'], ['integer', 'bigint', 'smallint'])) {
3043 32
                $mapping['default'] = 1;
3044 2
            } else if ($mapping['type'] == 'datetime') {
3045 1
                $mapping['default'] = 'CURRENT_TIMESTAMP';
3046
            } else {
3047 1
                throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
3048
            }
3049
        }
3050 32
    }
3051
3052
    /**
3053
     * Sets whether this class is to be versioned for optimistic locking.
3054
     *
3055
     * @param boolean $bool
3056
     *
3057
     * @return void
3058
     */
3059 144
    public function setVersioned($bool)
3060
    {
3061 144
        $this->isVersioned = $bool;
3062 144
    }
3063
3064
    /**
3065
     * Sets the name of the field that is to be used for versioning if this class is
3066
     * versioned for optimistic locking.
3067
     *
3068
     * @param string $versionField
3069
     *
3070
     * @return void
3071
     */
3072 144
    public function setVersionField($versionField)
3073
    {
3074 144
        $this->versionField = $versionField;
3075 144
    }
3076
3077
    /**
3078
     * Marks this class as read only, no change tracking is applied to it.
3079
     *
3080
     * @return void
3081
     */
3082 3
    public function markReadOnly()
3083
    {
3084 3
        $this->isReadOnly = true;
3085 3
    }
3086
3087
    /**
3088
     * {@inheritDoc}
3089
     */
3090
    public function getFieldNames()
3091
    {
3092
        return array_keys($this->fieldMappings);
3093
    }
3094
3095
    /**
3096
     * {@inheritDoc}
3097
     */
3098
    public function getAssociationNames()
3099
    {
3100
        return array_keys($this->associationMappings);
3101
    }
3102
3103
    /**
3104
     * {@inheritDoc}
3105
     *
3106
     * @throws InvalidArgumentException
3107
     */
3108 1
    public function getAssociationTargetClass($assocName)
3109
    {
3110 1
        if ( ! isset($this->associationMappings[$assocName])) {
3111
            throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
3112
        }
3113
3114 1
        return $this->associationMappings[$assocName]['targetEntity'];
3115
    }
3116
3117
    /**
3118
     * {@inheritDoc}
3119
     */
3120 784
    public function getName()
3121
    {
3122 784
        return $this->name;
3123
    }
3124
3125
    /**
3126
     * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
3127
     *
3128
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3129
     *
3130
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3131
     *
3132
     * @return array
3133
     */
3134
    public function getQuotedIdentifierColumnNames($platform)
3135
    {
3136
        $quotedColumnNames = [];
3137
3138
        foreach ($this->identifier as $idProperty) {
3139
            if (isset($this->fieldMappings[$idProperty])) {
3140
                $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
3141
                    ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
3142
                    : $this->fieldMappings[$idProperty]['columnName'];
3143
3144
                continue;
3145
            }
3146
3147
            // Association defined as Id field
3148
            $joinColumns            = $this->associationMappings[$idProperty]['joinColumns'];
3149
            $assocQuotedColumnNames = array_map(
3150 View Code Duplication
                function ($joinColumn) use ($platform) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3151
                    return isset($joinColumn['quoted'])
3152
                        ? $platform->quoteIdentifier($joinColumn['name'])
3153
                        : $joinColumn['name'];
3154
                },
3155
                $joinColumns
3156
            );
3157
3158
            $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
3159
        }
3160
3161
        return $quotedColumnNames;
3162
    }
3163
3164
    /**
3165
     * Gets the (possibly quoted) column name of a mapped field for safe use  in an SQL statement.
3166
     *
3167
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3168
     *
3169
     * @param string                                    $field
3170
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3171
     *
3172
     * @return string
3173
     */
3174
    public function getQuotedColumnName($field, $platform)
3175
    {
3176
        return isset($this->fieldMappings[$field]['quoted'])
3177
            ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
3178
            : $this->fieldMappings[$field]['columnName'];
3179
    }
3180
3181
    /**
3182
     * Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement.
3183
     *
3184
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3185
     *
3186
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3187
     *
3188
     * @return string
3189
     */
3190
    public function getQuotedTableName($platform)
3191
    {
3192
        return isset($this->table['quoted'])
3193
            ? $platform->quoteIdentifier($this->table['name'])
3194
            : $this->table['name'];
3195
    }
3196
3197
    /**
3198
     * Gets the (possibly quoted) name of the join table.
3199
     *
3200
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3201
     *
3202
     * @param array                                     $assoc
3203
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3204
     *
3205
     * @return string
3206
     */
3207
    public function getQuotedJoinTableName(array $assoc, $platform)
3208
    {
3209
        return isset($assoc['joinTable']['quoted'])
3210
            ? $platform->quoteIdentifier($assoc['joinTable']['name'])
3211
            : $assoc['joinTable']['name'];
3212
    }
3213
3214
    /**
3215
     * {@inheritDoc}
3216
     */
3217 12
    public function isAssociationInverseSide($fieldName)
3218
    {
3219 12
        return isset($this->associationMappings[$fieldName])
3220 12
            && ! $this->associationMappings[$fieldName]['isOwningSide'];
3221
    }
3222
3223
    /**
3224
     * {@inheritDoc}
3225
     */
3226
    public function getAssociationMappedByTargetField($fieldName)
3227
    {
3228
        return $this->associationMappings[$fieldName]['mappedBy'];
3229
    }
3230
3231
    /**
3232
     * @param string $targetClass
3233
     *
3234
     * @return array
3235
     */
3236 2
    public function getAssociationsByTargetClass($targetClass)
3237
    {
3238 2
        $relations = [];
3239
3240 2
        foreach ($this->associationMappings as $mapping) {
3241 2
            if ($mapping['targetEntity'] == $targetClass) {
3242 2
                $relations[$mapping['fieldName']] = $mapping;
3243
            }
3244
        }
3245
3246 2
        return $relations;
3247
    }
3248
3249
    /**
3250
     * @param  string|null $className
3251
     *
3252
     * @return string|null null if the input value is null
3253
     */
3254 525
    public function fullyQualifiedClassName($className)
3255
    {
3256 525
        if (empty($className)) {
3257 49
            return $className;
3258
        }
3259
3260 509
        if ($className !== null && strpos($className, '\\') === false && $this->namespace) {
3261 388
            return $this->namespace . '\\' . $className;
3262
        }
3263
3264 257
        return $className;
3265
    }
3266
3267
    /**
3268
     * @param string $name
3269
     *
3270
     * @return mixed
3271
     */
3272
    public function getMetadataValue($name)
3273
    {
3274
3275
        if (isset($this->$name)) {
3276
            return $this->$name;
3277
        }
3278
3279
        return null;
3280
    }
3281
3282
    /**
3283
     * Map Embedded Class
3284
     *
3285
     * @param array $mapping
3286
     *
3287
     * @throws MappingException
3288
     * @return void
3289
     */
3290 31
    public function mapEmbedded(array $mapping)
3291
    {
3292 31
        $this->assertFieldNotMapped($mapping['fieldName']);
3293
3294 31
        $this->embeddedClasses[$mapping['fieldName']] = [
3295 31
            'class' => $this->fullyQualifiedClassName($mapping['class']),
3296 31
            'columnPrefix' => $mapping['columnPrefix'],
3297 31
            'declaredField' => $mapping['declaredField'] ?? null,
3298 31
            'originalField' => $mapping['originalField'] ?? null,
3299
        ];
3300 31
    }
3301
3302
    /**
3303
     * Inline the embeddable class
3304
     *
3305
     * @param string            $property
3306
     * @param ClassMetadataInfo $embeddable
3307
     */
3308 12
    public function inlineEmbeddable($property, ClassMetadataInfo $embeddable)
3309
    {
3310 12
        foreach ($embeddable->fieldMappings as $fieldMapping) {
3311 12
            $fieldMapping['originalClass'] = $fieldMapping['originalClass'] ?? $embeddable->name;
3312 12
            $fieldMapping['declaredField'] = isset($fieldMapping['declaredField'])
3313 4
                ? $property . '.' . $fieldMapping['declaredField']
3314 12
                : $property;
3315 12
            $fieldMapping['originalField'] = $fieldMapping['originalField'] ?? $fieldMapping['fieldName'];
3316 12
            $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName'];
3317
3318 12
            if (! empty($this->embeddedClasses[$property]['columnPrefix'])) {
3319 2
                $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName'];
3320 11
            } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) {
3321 8
                $fieldMapping['columnName'] = $this->namingStrategy
3322 8
                    ->embeddedFieldToColumnName(
3323 8
                        $property,
3324 8
                        $fieldMapping['columnName'],
3325 8
                        $this->reflClass->name,
3326 8
                        $embeddable->reflClass->name
3327
                    );
3328
            }
3329
3330 12
            $this->mapField($fieldMapping);
3331
        }
3332 12
    }
3333
3334
    /**
3335
     * @param string $fieldName
3336
     * @throws MappingException
3337
     */
3338 630
    private function assertFieldNotMapped($fieldName)
3339
    {
3340 630
        if (isset($this->fieldMappings[$fieldName]) ||
3341 630
            isset($this->associationMappings[$fieldName]) ||
3342 630
            isset($this->embeddedClasses[$fieldName])) {
3343
3344 2
            throw MappingException::duplicateFieldMapping($this->name, $fieldName);
3345
        }
3346 630
    }
3347
3348
    /**
3349
     * Gets the sequence name based on class metadata.
3350
     *
3351
     * @param AbstractPlatform $platform
3352
     * @return string
3353
     *
3354
     * @todo Sequence names should be computed in DBAL depending on the platform
3355
     */
3356 3
    public function getSequenceName(AbstractPlatform $platform)
3357
    {
3358 3
        $sequencePrefix = $this->getSequencePrefix($platform);
3359 3
        $columnName     = $this->getSingleIdentifierColumnName();
3360 3
        $sequenceName   = $sequencePrefix . '_' . $columnName . '_seq';
3361
3362 3
        return $sequenceName;
3363
    }
3364
3365
    /**
3366
     * Gets the sequence name prefix based on class metadata.
3367
     *
3368
     * @param AbstractPlatform $platform
3369
     * @return string
3370
     *
3371
     * @todo Sequence names should be computed in DBAL depending on the platform
3372
     */
3373 3
    public function getSequencePrefix(AbstractPlatform $platform)
3374
    {
3375 3
        $tableName      = $this->getTableName();
3376 3
        $sequencePrefix = $tableName;
3377
3378
        // Prepend the schema name to the table name if there is one
3379 3
        if ($schemaName = $this->getSchemaName()) {
3380 3
            $sequencePrefix = $schemaName . '.' . $tableName;
3381
3382 3
            if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) {
3383 3
                $sequencePrefix = $schemaName . '__' . $tableName;
3384
            }
3385
        }
3386
3387 3
        return $sequencePrefix;
3388
    }
3389
3390
    /**
3391
     * @param array $mapping
3392
     */
3393 234
    private function assertMappingOrderBy(array $mapping)
3394
    {
3395 234
        if (isset($mapping['orderBy']) && !is_array($mapping['orderBy'])) {
3396
            throw new InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy']));
3397
        }
3398 234
    }
3399
}
3400