Completed
Pull Request — master (#5800)
by Herberto
10:37
created

ClassMetadataInfo::setIdentifier()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1
Metric Value
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 1
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.

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
     * The name of the custom collection class used for the entity class.
261
     * (Optional).
262
     *
263
     * @var string
264
     */
265
    public $customCollectionClassName;
266
267
    /**
268
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
269
     *
270
     * @var boolean
271
     */
272
    public $isMappedSuperclass = false;
273
274
    /**
275
     * READ-ONLY: Whether this class describes the mapping of an embeddable class.
276
     *
277
     * @var boolean
278
     */
279
    public $isEmbeddedClass = false;
280
281
    /**
282
     * READ-ONLY: The names of the parent classes (ancestors).
283
     *
284
     * @var array
285
     */
286
    public $parentClasses = array();
287
288
    /**
289
     * READ-ONLY: The names of all subclasses (descendants).
290
     *
291
     * @var array
292
     */
293
    public $subClasses = array();
294
295
    /**
296
     * READ-ONLY: The names of all embedded classes based on properties.
297
     *
298
     * @var array
299
     */
300
    public $embeddedClasses = array();
301
302
    /**
303
     * READ-ONLY: The named queries allowed to be called directly from Repository.
304
     *
305
     * @var array
306
     */
307
    public $namedQueries = array();
308
309
    /**
310
     * READ-ONLY: The named native queries allowed to be called directly from Repository.
311
     *
312
     * A native SQL named query definition has the following structure:
313
     * <pre>
314
     * array(
315
     *     'name'               => <query name>,
316
     *     'query'              => <sql query>,
317
     *     'resultClass'        => <class of the result>,
318
     *     'resultSetMapping'   => <name of a SqlResultSetMapping>
319
     * )
320
     * </pre>
321
     *
322
     * @var array
323
     */
324
    public $namedNativeQueries = array();
325
326
    /**
327
     * READ-ONLY: The mappings of the results of native SQL queries.
328
     *
329
     * A native result mapping definition has the following structure:
330
     * <pre>
331
     * array(
332
     *     'name'               => <result name>,
333
     *     'entities'           => array(<entity result mapping>),
334
     *     'columns'            => array(<column result mapping>)
335
     * )
336
     * </pre>
337
     *
338
     * @var array
339
     */
340
    public $sqlResultSetMappings = array();
341
342
    /**
343
     * READ-ONLY: The field names of all fields that are part of the identifier/primary key
344
     * of the mapped entity class.
345
     *
346
     * @var array
347
     */
348
    public $identifier = array();
349
350
    /**
351
     * READ-ONLY: The inheritance mapping type used by the class.
352
     *
353
     * @var integer
354
     */
355
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
356
357
    /**
358
     * READ-ONLY: The Id generator type used by the class.
359
     *
360
     * @var int
361
     */
362
    public $generatorType = self::GENERATOR_TYPE_NONE;
363
364
    /**
365
     * READ-ONLY: The field mappings of the class.
366
     * Keys are field names and values are mapping definitions.
367
     *
368
     * The mapping definition array has the following values:
369
     *
370
     * - <b>fieldName</b> (string)
371
     * The name of the field in the Entity.
372
     *
373
     * - <b>type</b> (string)
374
     * The type name of the mapped field. Can be one of Doctrine's mapping types
375
     * or a custom mapping type.
376
     *
377
     * - <b>columnName</b> (string, optional)
378
     * The column name. Optional. Defaults to the field name.
379
     *
380
     * - <b>length</b> (integer, optional)
381
     * The database length of the column. Optional. Default value taken from
382
     * the type.
383
     *
384
     * - <b>id</b> (boolean, optional)
385
     * Marks the field as the primary key of the entity. Multiple fields of an
386
     * entity can have the id attribute, forming a composite key.
387
     *
388
     * - <b>nullable</b> (boolean, optional)
389
     * Whether the column is nullable. Defaults to FALSE.
390
     *
391
     * - <b>columnDefinition</b> (string, optional, schema-only)
392
     * The SQL fragment that is used when generating the DDL for the column.
393
     *
394
     * - <b>precision</b> (integer, optional, schema-only)
395
     * The precision of a decimal column. Only valid if the column type is decimal.
396
     *
397
     * - <b>scale</b> (integer, optional, schema-only)
398
     * The scale of a decimal column. Only valid if the column type is decimal.
399
     *
400
     * - <b>'unique'</b> (string, optional, schema-only)
401
     * Whether a unique constraint should be generated for the column.
402
     *
403
     * @var array
404
     */
405
    public $fieldMappings = array();
406
407
    /**
408
     * READ-ONLY: An array of field names. Used to look up field names from column names.
409
     * Keys are column names and values are field names.
410
     *
411
     * @var array
412
     */
413
    public $fieldNames = array();
414
415
    /**
416
     * READ-ONLY: A map of field names to column names. Keys are field names and values column names.
417
     * Used to look up column names from field names.
418
     * This is the reverse lookup map of $_fieldNames.
419
     *
420
     * @var array
421
     *
422
     * @deprecated 3.0 Remove this.
423
     */
424
    public $columnNames = array();
425
426
    /**
427
     * READ-ONLY: The discriminator value of this class.
428
     *
429
     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
430
     * where a discriminator column is used.</b>
431
     *
432
     * @var mixed
433
     *
434
     * @see discriminatorColumn
435
     */
436
    public $discriminatorValue;
437
438
    /**
439
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
440
     *
441
     * <b>This does only apply to the JOINED and SINGLE_TABLE inheritance mapping strategies
442
     * where a discriminator column is used.</b>
443
     *
444
     * @var mixed
445
     *
446
     * @see discriminatorColumn
447
     */
448
    public $discriminatorMap = array();
449
450
    /**
451
     * READ-ONLY: The definition of the discriminator column used in JOINED and SINGLE_TABLE
452
     * inheritance mappings.
453
     *
454
     * @var array
455
     */
456
    public $discriminatorColumn;
457
458
    /**
459
     * READ-ONLY: The primary table definition. The definition is an array with the
460
     * following entries:
461
     *
462
     * name => <tableName>
463
     * schema => <schemaName>
464
     * indexes => array
465
     * uniqueConstraints => array
466
     *
467
     * @var array
468
     */
469
    public $table;
470
471
    /**
472
     * READ-ONLY: The registered lifecycle callbacks for entities of this class.
473
     *
474
     * @var array
475
     */
476
    public $lifecycleCallbacks = array();
477
478
    /**
479
     * READ-ONLY: The registered entity listeners.
480
     *
481
     * @var array
482
     */
483
    public $entityListeners = array();
484
485
    /**
486
     * READ-ONLY: The association mappings of this class.
487
     *
488
     * The mapping definition array supports the following keys:
489
     *
490
     * - <b>fieldName</b> (string)
491
     * The name of the field in the entity the association is mapped to.
492
     *
493
     * - <b>targetEntity</b> (string)
494
     * The class name of the target entity. If it is fully-qualified it is used as is.
495
     * If it is a simple, unqualified class name the namespace is assumed to be the same
496
     * as the namespace of the source entity.
497
     *
498
     * - <b>mappedBy</b> (string, required for bidirectional associations)
499
     * The name of the field that completes the bidirectional association on the owning side.
500
     * This key must be specified on the inverse side of a bidirectional association.
501
     *
502
     * - <b>inversedBy</b> (string, required for bidirectional associations)
503
     * The name of the field that completes the bidirectional association on the inverse side.
504
     * This key must be specified on the owning side of a bidirectional association.
505
     *
506
     * - <b>cascade</b> (array, optional)
507
     * The names of persistence operations to cascade on the association. The set of possible
508
     * values are: "persist", "remove", "detach", "merge", "refresh", "all" (implies all others).
509
     *
510
     * - <b>orderBy</b> (array, one-to-many/many-to-many only)
511
     * A map of field names (of the target entity) to sorting directions (ASC/DESC).
512
     * Example: array('priority' => 'desc')
513
     *
514
     * - <b>fetch</b> (integer, optional)
515
     * The fetching strategy to use for the association, usually defaults to FETCH_LAZY.
516
     * Possible values are: ClassMetadata::FETCH_EAGER, ClassMetadata::FETCH_LAZY.
517
     *
518
     * - <b>joinTable</b> (array, optional, many-to-many only)
519
     * Specification of the join table and its join columns (foreign keys).
520
     * Only valid for many-to-many mappings. Note that one-to-many associations can be mapped
521
     * through a join table by simply mapping the association as many-to-many with a unique
522
     * constraint on the join table.
523
     *
524
     * - <b>indexBy</b> (string, optional, to-many only)
525
     * Specification of a field on target-entity that is used to index the collection by.
526
     * This field HAS to be either the primary key or a unique column. Otherwise the collection
527
     * does not contain all the entities that are actually related.
528
     *
529
     * A join table definition has the following structure:
530
     * <pre>
531
     * array(
532
     *     'name' => <join table name>,
533
     *      'joinColumns' => array(<join column mapping from join table to source table>),
534
     *      'inverseJoinColumns' => array(<join column mapping from join table to target table>)
535
     * )
536
     * </pre>
537
     *
538
     * @var array
539
     */
540
    public $associationMappings = array();
541
542
    /**
543
     * READ-ONLY: Flag indicating whether the identifier/primary key of the class is composite.
544
     *
545
     * @var boolean
546
     */
547
    public $isIdentifierComposite = false;
548
549
    /**
550
     * READ-ONLY: Flag indicating whether the identifier/primary key contains at least one foreign key association.
551
     *
552
     * This flag is necessary because some code blocks require special treatment of this cases.
553
     *
554
     * @var boolean
555
     */
556
    public $containsForeignIdentifier = false;
557
558
    /**
559
     * READ-ONLY: The ID generator used for generating IDs for this class.
560
     *
561
     * @var \Doctrine\ORM\Id\AbstractIdGenerator
562
     *
563
     * @todo Remove!
564
     */
565
    public $idGenerator;
566
567
    /**
568
     * READ-ONLY: The definition of the sequence generator of this class. Only used for the
569
     * SEQUENCE generation strategy.
570
     *
571
     * The definition has the following structure:
572
     * <code>
573
     * array(
574
     *     'sequenceName' => 'name',
575
     *     'allocationSize' => 20,
576
     *     'initialValue' => 1
577
     * )
578
     * </code>
579
     *
580
     * @var array
581
     *
582
     * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
583
     */
584
    public $sequenceGeneratorDefinition;
585
586
    /**
587
     * READ-ONLY: The definition of the table generator of this class. Only used for the
588
     * TABLE generation strategy.
589
     *
590
     * @var array
591
     *
592
     * @todo Merge with tableGeneratorDefinition into generic generatorDefinition
593
     */
594
    public $tableGeneratorDefinition;
595
596
    /**
597
     * READ-ONLY: The policy used for change-tracking on entities of this class.
598
     *
599
     * @var integer
600
     */
601
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
602
603
    /**
604
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
605
     * with optimistic locking.
606
     *
607
     * @var boolean
608
     */
609
    public $isVersioned;
610
611
    /**
612
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
613
     *
614
     * @var mixed
615
     */
616
    public $versionField;
617
618
    /**
619
     * @var array
620
     */
621
    public $cache = null;
622
623
    /**
624
     * The ReflectionClass instance of the mapped class.
625
     *
626
     * @var ReflectionClass
627
     */
628
    public $reflClass;
629
630
    /**
631
     * Is this entity marked as "read-only"?
632
     *
633
     * That means it is never considered for change-tracking in the UnitOfWork. It is a very helpful performance
634
     * optimization for entities that are immutable, either in your domain or through the relation database
635
     * (coming from a view, or a history table for example).
636
     *
637
     * @var bool
638
     */
639
    public $isReadOnly = false;
640
641
    /**
642
     * NamingStrategy determining the default column and table names.
643
     *
644
     * @var \Doctrine\ORM\Mapping\NamingStrategy
645
     */
646
    protected $namingStrategy;
647
648
    /**
649
     * The ReflectionProperty instances of the mapped class.
650
     *
651
     * @var \ReflectionProperty[]
652
     */
653
    public $reflFields = array();
654
655
    /**
656
     * @var \Doctrine\Instantiator\InstantiatorInterface|null
657
     */
658
    private $instantiator;
659
660
    /**
661
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
662
     * metadata of the class with the given name.
663
     *
664
     * @param string              $entityName     The name of the entity class the new instance is used for.
665
     * @param NamingStrategy|null $namingStrategy
666
     */
667 648
    public function __construct($entityName, NamingStrategy $namingStrategy = null)
668
    {
669 648
        $this->name = $entityName;
670 648
        $this->rootEntityName = $entityName;
671 648
        $this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy();
672 648
        $this->instantiator   = new Instantiator();
673 648
    }
674
675
    /**
676
     * Gets the ReflectionProperties of the mapped class.
677
     *
678
     * @return array An array of ReflectionProperty instances.
679
     */
680 223
    public function getReflectionProperties()
681
    {
682 223
        return $this->reflFields;
683
    }
684
685
    /**
686
     * Gets a ReflectionProperty for a specific field of the mapped class.
687
     *
688
     * @param string $name
689
     *
690
     * @return \ReflectionProperty
691
     */
692 1
    public function getReflectionProperty($name)
693
    {
694 1
        return $this->reflFields[$name];
695
    }
696
697
    /**
698
     * Gets the ReflectionProperty for the single identifier field.
699
     *
700
     * @return \ReflectionProperty
701
     *
702
     * @throws BadMethodCallException If the class has a composite identifier.
703
     */
704
    public function getSingleIdReflectionProperty()
705
    {
706
        if ($this->isIdentifierComposite) {
707
            throw new BadMethodCallException("Class " . $this->name . " has a composite identifier.");
708
        }
709
710
        return $this->reflFields[$this->identifier[0]];
711
    }
712
713
    /**
714
     * Extracts the identifier values of an entity of this class.
715
     *
716
     * For composite identifiers, the identifier values are returned as an array
717
     * with the same order as the field order in {@link identifier}.
718
     *
719
     * @param object $entity
720
     *
721
     * @return array
722
     */
723 463
    public function getIdentifierValues($entity)
724
    {
725 463
        if ($this->isIdentifierComposite) {
726 90
            $id = array();
727
728 90
            foreach ($this->identifier as $idField) {
729 90
                $value = $this->reflFields[$idField]->getValue($entity);
730
731 90
                if ($value !== null) {
732 90
                    $id[$idField] = $value;
733
                }
734
            }
735
736 90
            return $id;
737
        }
738
739 444
        $id = $this->identifier[0];
740 444
        $value = $this->reflFields[$id]->getValue($entity);
741
742 444
        if (null === $value) {
743 28
            return array();
744
        }
745
746 421
        return array($id => $value);
747
    }
748
749
    /**
750
     * Populates the entity identifier of an entity.
751
     *
752
     * @param object $entity
753
     * @param array  $id
754
     *
755
     * @return void
756
     *
757
     * @todo Rename to assignIdentifier()
758
     */
759 6
    public function setIdentifierValues($entity, array $id)
760
    {
761 6
        foreach ($id as $idField => $idValue) {
762 6
            $this->reflFields[$idField]->setValue($entity, $idValue);
763
        }
764 6
    }
765
766
    /**
767
     * Sets the specified field to the specified value on the given entity.
768
     *
769
     * @param object $entity
770
     * @param string $field
771
     * @param mixed  $value
772
     *
773
     * @return void
774
     */
775 231
    public function setFieldValue($entity, $field, $value)
776
    {
777 231
        $this->reflFields[$field]->setValue($entity, $value);
778 231
    }
779
780
    /**
781
     * Gets the specified field's value off the given entity.
782
     *
783
     * @param object $entity
784
     * @param string $field
785
     *
786
     * @return mixed
787
     */
788 302
    public function getFieldValue($entity, $field)
789
    {
790 302
        return $this->reflFields[$field]->getValue($entity);
791
    }
792
793
    /**
794
     * Creates a string representation of this instance.
795
     *
796
     * @return string The string representation of this instance.
797
     *
798
     * @todo Construct meaningful string representation.
799
     */
800
    public function __toString()
801
    {
802
        return __CLASS__ . '@' . spl_object_hash($this);
803
    }
804
805
    /**
806
     * Determines which fields get serialized.
807
     *
808
     * It is only serialized what is necessary for best unserialization performance.
809
     * That means any metadata properties that are not set or empty or simply have
810
     * their default value are NOT serialized.
811
     *
812
     * Parts that are also NOT serialized because they can not be properly unserialized:
813
     *      - reflClass (ReflectionClass)
814
     *      - reflFields (ReflectionProperty array)
815
     *
816
     * @return array The names of all the fields that should be serialized.
817
     */
818 6
    public function __sleep()
819
    {
820
        // This metadata is always serialized/cached.
821
        $serialized = array(
822 6
            'associationMappings',
823
            'columnNames', //TODO: 3.0 Remove this. Can use fieldMappings[$fieldName]['columnName']
824
            'fieldMappings',
825
            'fieldNames',
826
            'embeddedClasses',
827
            'identifier',
828
            'isIdentifierComposite', // TODO: REMOVE
829
            'name',
830
            'namespace', // TODO: REMOVE
831
            'table',
832
            'rootEntityName',
833
            'idGenerator', //TODO: Does not really need to be serialized. Could be moved to runtime.
834
        );
835
836
        // The rest of the metadata is only serialized if necessary.
837 6
        if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
838
            $serialized[] = 'changeTrackingPolicy';
839
        }
840
841 6
        if ($this->customRepositoryClassName) {
842 1
            $serialized[] = 'customRepositoryClassName';
843
        }
844
845 6
        if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
846 1
            $serialized[] = 'inheritanceType';
847 1
            $serialized[] = 'discriminatorColumn';
848 1
            $serialized[] = 'discriminatorValue';
849 1
            $serialized[] = 'discriminatorMap';
850 1
            $serialized[] = 'parentClasses';
851 1
            $serialized[] = 'subClasses';
852
        }
853
854 6
        if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
855 1
            $serialized[] = 'generatorType';
856 1
            if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
857
                $serialized[] = 'sequenceGeneratorDefinition';
858
            }
859
        }
860
861 6
        if ($this->isMappedSuperclass) {
862
            $serialized[] = 'isMappedSuperclass';
863
        }
864
865 6
        if ($this->isEmbeddedClass) {
866 1
            $serialized[] = 'isEmbeddedClass';
867
        }
868
869 6
        if ($this->containsForeignIdentifier) {
870
            $serialized[] = 'containsForeignIdentifier';
871
        }
872
873 6
        if ($this->isVersioned) {
874
            $serialized[] = 'isVersioned';
875
            $serialized[] = 'versionField';
876
        }
877
878 6
        if ($this->lifecycleCallbacks) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->lifecycleCallbacks 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
            $serialized[] = 'lifecycleCallbacks';
880
        }
881
882 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...
883 1
            $serialized[] = 'entityListeners';
884
        }
885
886 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...
887 1
            $serialized[] = 'namedQueries';
888
        }
889
890 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...
891
            $serialized[] = 'namedNativeQueries';
892
        }
893
894 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...
895
            $serialized[] = 'sqlResultSetMappings';
896
        }
897
898 6
        if ($this->isReadOnly) {
899 1
            $serialized[] = 'isReadOnly';
900
        }
901
902 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...
903
            $serialized[] = "customGeneratorDefinition";
904
        }
905
906 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...
907
            $serialized[] = 'cache';
908
        }
909
910 6
        return $serialized;
911
    }
912
913
    /**
914
     * Creates a new instance of the mapped class, without invoking the constructor.
915
     *
916
     * @return object
917
     */
918 672
    public function newInstance()
919
    {
920 672
        return $this->instantiator->instantiate($this->name);
921
    }
922
923
    /**
924
     * Restores some state that can not be serialized/unserialized.
925
     *
926
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
927
     *
928
     * @return void
929
     */
930 2012
    public function wakeupReflection($reflService)
931
    {
932
        // Restore ReflectionClass and properties
933 2012
        $this->reflClass    = $reflService->getClass($this->name);
934 2012
        $this->instantiator = $this->instantiator ?: new Instantiator();
935
936 2012
        $parentReflFields = array();
937
938 2012
        foreach ($this->embeddedClasses as $property => $embeddedClass) {
939 20
            if (isset($embeddedClass['declaredField'])) {
940 15
                $parentReflFields[$property] = new ReflectionEmbeddedProperty(
941 15
                    $parentReflFields[$embeddedClass['declaredField']],
942 15
                    $reflService->getAccessibleProperty(
0 ignored issues
show
Bug introduced by
It seems like $reflService->getAccessi...Class['originalField']) can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
943 15
                        $this->embeddedClasses[$embeddedClass['declaredField']]['class'],
944 15
                        $embeddedClass['originalField']
945
                    ),
946 15
                    $this->embeddedClasses[$embeddedClass['declaredField']]['class']
947
                );
948
949 15
                continue;
950
            }
951
952 20
            $parentReflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
953 20
            $this->reflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
954
        }
955
956 2012
        foreach ($this->fieldMappings as $field => $mapping) {
957 2007
            if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
958 19
                $this->reflFields[$field] = new ReflectionEmbeddedProperty(
959 19
                    $parentReflFields[$mapping['declaredField']],
960 19
                    $reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField']),
0 ignored issues
show
Bug introduced by
It seems like $reflService->getAccessi...pping['originalField']) can be null; however, __construct() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
961 19
                    $mapping['originalClass']
962
                );
963 19
                continue;
964
            }
965
966 2007
            $this->reflFields[$field] = isset($mapping['declared'])
967 493
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
968 2007
                : $reflService->getAccessibleProperty($this->name, $field);
969
        }
970
971 2012
        foreach ($this->associationMappings as $field => $mapping) {
972 1700
            $this->reflFields[$field] = isset($mapping['declared'])
973 401
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
974 1700
                : $reflService->getAccessibleProperty($this->name, $field);
975
        }
976 2012
    }
977
978
    /**
979
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
980
     * metadata of the class with the given name.
981
     *
982
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service.
983
     *
984
     * @return void
985
     */
986 612
    public function initializeReflection($reflService)
987
    {
988 612
        $this->reflClass = $reflService->getClass($this->name);
989 612
        $this->namespace = $reflService->getClassNamespace($this->name);
990
991 612
        if ($this->reflClass) {
992 605
            $this->name = $this->rootEntityName = $this->reflClass->getName();
993
        }
994
995 612
        $this->table['name'] = $this->namingStrategy->classToTableName($this->name);
996 612
    }
997
998
    /**
999
     * Validates Identifier.
1000
     *
1001
     * @return void
1002
     *
1003
     * @throws MappingException
1004
     */
1005 401
    public function validateIdentifier()
1006
    {
1007 401
        if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
1008 59
            return;
1009
        }
1010
1011
        // Verify & complete identifier mapping
1012 399
        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...
1013 6
            throw MappingException::identifierRequired($this->name);
1014
        }
1015
1016 393
        if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
1017
            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
1018
        }
1019 393
    }
1020
1021
    /**
1022
     * Validates association targets actually exist.
1023
     *
1024
     * @return void
1025
     *
1026
     * @throws MappingException
1027
     */
1028 402
    public function validateAssociations()
1029
    {
1030 402
        foreach ($this->associationMappings as $mapping) {
1031 269
            if ( ! ClassLoader::classExists($mapping['targetEntity']) ) {
1032 269
                throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
1033
            }
1034
        }
1035 401
    }
1036
1037
    /**
1038
     * Validates lifecycle callbacks.
1039
     *
1040
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
1041
     *
1042
     * @return void
1043
     *
1044
     * @throws MappingException
1045
     */
1046 402
    public function validateLifecycleCallbacks($reflService)
1047
    {
1048 402
        foreach ($this->lifecycleCallbacks as $callbacks) {
1049 13
            foreach ($callbacks as $callbackFuncName) {
1050 13
                if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
1051 13
                    throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
1052
                }
1053
            }
1054
        }
1055 401
    }
1056
1057
    /**
1058
     * {@inheritDoc}
1059
     */
1060 529
    public function getReflectionClass()
1061
    {
1062 529
        return $this->reflClass;
1063
    }
1064
1065
    /**
1066
     * @param array $cache
1067
     *
1068
     * @return void
1069
     */
1070 21
    public function enableCache(array $cache)
1071
    {
1072 21
        if ( ! isset($cache['usage'])) {
1073
            $cache['usage'] = self::CACHE_USAGE_READ_ONLY;
1074
        }
1075
1076 21
        if ( ! isset($cache['region'])) {
1077 21
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName));
1078
        }
1079
1080 21
        $this->cache = $cache;
1081 21
    }
1082
1083
    /**
1084
     * @param string $fieldName
1085
     * @param array  $cache
1086
     *
1087
     * @return void
1088
     */
1089 2
    public function enableAssociationCache($fieldName, array $cache)
1090
    {
1091 2
        $this->associationMappings[$fieldName]['cache'] = $this->getAssociationCacheDefaults ($fieldName, $cache);
1092 2
    }
1093
1094
    /**
1095
     * @param string $fieldName
1096
     * @param array  $cache
1097
     *
1098
     * @return array
1099
     */
1100 17
    public function getAssociationCacheDefaults($fieldName, array $cache)
1101
    {
1102 17
        if ( ! isset($cache['usage'])) {
1103 1
            $cache['usage'] = isset($this->cache['usage'])
1104 1
                ? $this->cache['usage']
1105
                : self::CACHE_USAGE_READ_ONLY;
1106
        }
1107
1108 17
        if ( ! isset($cache['region'])) {
1109 17
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName;
1110
        }
1111
1112 17
        return $cache;
1113
    }
1114
1115
    /**
1116
     * Sets the change tracking policy used by this class.
1117
     *
1118
     * @param integer $policy
1119
     *
1120
     * @return void
1121
     */
1122 138
    public function setChangeTrackingPolicy($policy)
1123
    {
1124 138
        $this->changeTrackingPolicy = $policy;
1125 138
    }
1126
1127
    /**
1128
     * Whether the change tracking policy of this class is "deferred explicit".
1129
     *
1130
     * @return boolean
1131
     */
1132 267
    public function isChangeTrackingDeferredExplicit()
1133
    {
1134 267
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
1135
    }
1136
1137
    /**
1138
     * Whether the change tracking policy of this class is "deferred implicit".
1139
     *
1140
     * @return boolean
1141
     */
1142 457
    public function isChangeTrackingDeferredImplicit()
1143
    {
1144 457
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1145
    }
1146
1147
    /**
1148
     * Whether the change tracking policy of this class is "notify".
1149
     *
1150
     * @return boolean
1151
     */
1152 289
    public function isChangeTrackingNotify()
1153
    {
1154 289
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1155
    }
1156
1157
    /**
1158
     * Checks whether a field is part of the identifier/primary key field(s).
1159
     *
1160
     * @param string $fieldName The field name.
1161
     *
1162
     * @return boolean TRUE if the field is part of the table identifier/primary key field(s),
1163
     *                 FALSE otherwise.
1164
     */
1165 1068
    public function isIdentifier($fieldName)
1166
    {
1167 1068
        if ( ! $this->identifier) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->identifier of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1168 1
            return false;
1169
        }
1170
1171 1067
        if ( ! $this->isIdentifierComposite) {
1172 1062
            return $fieldName === $this->identifier[0];
1173
        }
1174
1175 93
        return in_array($fieldName, $this->identifier);
1176
    }
1177
1178
    /**
1179
     * Checks if the field is unique.
1180
     *
1181
     * @param string $fieldName The field name.
1182
     *
1183
     * @return boolean TRUE if the field is unique, FALSE otherwise.
1184
     */
1185
    public function isUniqueField($fieldName)
1186
    {
1187
        $mapping = $this->getFieldMapping($fieldName);
1188
1189
        if ($mapping !== false) {
1190
            return isset($mapping['unique']) && $mapping['unique'] == true;
1191
        }
1192
1193
        return false;
1194
    }
1195
1196
    /**
1197
     * Checks if the field is not null.
1198
     *
1199
     * @param string $fieldName The field name.
1200
     *
1201
     * @return boolean TRUE if the field is not null, FALSE otherwise.
1202
     */
1203 1
    public function isNullable($fieldName)
1204
    {
1205 1
        $mapping = $this->getFieldMapping($fieldName);
1206
1207 1
        if ($mapping !== false) {
1208 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1209
        }
1210
1211
        return false;
1212
    }
1213
1214
    /**
1215
     * Gets a column name for a field name.
1216
     * If the column name for the field cannot be found, the given field name
1217
     * is returned.
1218
     *
1219
     * @param string $fieldName The field name.
1220
     *
1221
     * @return string The column name.
1222
     */
1223 16
    public function getColumnName($fieldName)
1224
    {
1225 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 with message: 3.0 Remove this.

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...
1226 16
            ? $this->columnNames[$fieldName]
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ORM\Mapping\Cla...adataInfo::$columnNames has been deprecated with message: 3.0 Remove this.

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

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

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1596 91
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName()
1597
                    )
1598
                );
1599
            }
1600
1601 280
            $uniqueConstraintColumns = array();
1602
1603 280
            foreach ($mapping['joinColumns'] as &$joinColumn) {
1604 280
                if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
1605 150
                    if (count($mapping['joinColumns']) == 1) {
1606 148
                        if ( ! isset($mapping['id']) || ! $mapping['id']) {
1607 148
                            $joinColumn['unique'] = true;
1608
                        }
1609
                    } else {
1610 2
                        $uniqueConstraintColumns[] = $joinColumn['name'];
1611
                    }
1612
                }
1613
1614 280
                if (empty($joinColumn['name'])) {
1615 29
                    $joinColumn['name'] = $this->namingStrategy->joinColumnName($mapping['fieldName'], $this->name);
0 ignored issues
show
Unused Code introduced by
The call to NamingStrategy::joinColumnName() has too many arguments starting with $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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1616
                }
1617
1618 280
                if (empty($joinColumn['referencedColumnName'])) {
1619 5
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1620
                }
1621
1622 280
                if ($joinColumn['name'][0] === '`') {
1623 7
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1624 7
                    $joinColumn['quoted'] = true;
1625
                }
1626
1627 280
                if ($joinColumn['referencedColumnName'][0] === '`') {
1628 4
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1629 4
                    $joinColumn['quoted']               = true;
1630
                }
1631
1632 280
                $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1633 280
                $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
1634
                    ? $joinColumn['fieldName']
1635 280
                    : $joinColumn['name'];
1636
            }
1637
1638 280
            if ($uniqueConstraintColumns) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $uniqueConstraintColumns 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...
1639 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...
1640
                    throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
1641
                }
1642
1643 2
                $this->table['uniqueConstraints'][$mapping['fieldName'] . "_uniq"] = array(
1644 2
                    'columns' => $uniqueConstraintColumns
1645
                );
1646
            }
1647
1648 280
            $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
1649
        }
1650
1651 293
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1652 293
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1653
1654 293
        if ($mapping['orphanRemoval']) {
1655 21
            unset($mapping['unique']);
1656
        }
1657
1658 293
        if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
1659 2
            throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']);
1660
        }
1661
1662 291
        return $mapping;
1663
    }
1664
1665
    /**
1666
     * Validates & completes a one-to-many association mapping.
1667
     *
1668
     * @param array $mapping The mapping to validate and complete.
1669
     *
1670
     * @return array The validated and completed mapping.
1671
     *
1672
     * @throws MappingException
1673
     * @throws InvalidArgumentException
1674
     */
1675 129
    protected function _validateAndCompleteOneToManyMapping(array $mapping)
1676
    {
1677 129
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1678
1679
        // OneToMany-side MUST be inverse (must have mappedBy)
1680 128
        if ( ! isset($mapping['mappedBy'])) {
1681
            throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
1682
        }
1683
1684 128
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1685 128
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1686
1687 128
        if (isset($mapping['orderBy'])) {
1688 37
            if ( ! is_array($mapping['orderBy'])) {
1689
                throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
1690
            }
1691
        }
1692
1693 128
        return $mapping;
1694
    }
1695
1696
    /**
1697
     * Validates & completes a many-to-many association mapping.
1698
     *
1699
     * @param array $mapping The mapping to validate & complete.
1700
     *
1701
     * @return array The validated & completed mapping.
1702
     *
1703
     * @throws \InvalidArgumentException
1704
     */
1705 150
    protected function _validateAndCompleteManyToManyMapping(array $mapping)
1706
    {
1707 150
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1708
1709 148
        if ($mapping['isOwningSide']) {
1710
            // owning side MUST have a join table
1711 130
            if ( ! isset($mapping['joinTable']['name'])) {
1712 22
                $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']);
1713
            }
1714
1715 130
            $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity']
1716 130
                && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns'])));
1717
1718 130
            if ( ! isset($mapping['joinTable']['joinColumns'])) {
1719 22
                $mapping['joinTable']['joinColumns'] = array(
1720
                    array(
1721 22
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null),
1722 22
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1723 22
                        'onDelete' => 'CASCADE'
1724
                    )
1725
                );
1726
            }
1727
1728 130
            if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
1729 23
                $mapping['joinTable']['inverseJoinColumns'] = array(
1730
                    array(
1731 23
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null),
1732 23
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1733 23
                        'onDelete' => 'CASCADE'
1734
                    )
1735
                );
1736
            }
1737
1738 130
            $mapping['joinTableColumns'] = array();
1739
1740 130
            foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
1741 130
                if (empty($joinColumn['name'])) {
1742 2
                    $joinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $joinColumn['referencedColumnName']);
0 ignored issues
show
Documentation introduced by
$mapping['sourceEntity'] is of type array|boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1743
                }
1744
1745 130
                if (empty($joinColumn['referencedColumnName'])) {
1746 6
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1747
                }
1748
1749 130
                if ($joinColumn['name'][0] === '`') {
1750 3
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1751 3
                    $joinColumn['quoted'] = true;
1752
                }
1753
1754 130
                if ($joinColumn['referencedColumnName'][0] === '`') {
1755 3
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1756 3
                    $joinColumn['quoted']               = true;
1757
                }
1758
1759 130
                if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
1760 31
                    $mapping['isOnDeleteCascade'] = true;
1761
                }
1762
1763 130
                $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1764 130
                $mapping['joinTableColumns'][] = $joinColumn['name'];
1765
            }
1766
1767 130
            foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
1768 130
                if (empty($inverseJoinColumn['name'])) {
1769 2
                    $inverseJoinColumn['name'] = $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $inverseJoinColumn['referencedColumnName']);
0 ignored issues
show
Documentation introduced by
$mapping['targetEntity'] is of type array|boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1770
                }
1771
1772 130
                if (empty($inverseJoinColumn['referencedColumnName'])) {
1773 6
                    $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1774
                }
1775
1776 130
                if ($inverseJoinColumn['name'][0] === '`') {
1777 3
                    $inverseJoinColumn['name']   = trim($inverseJoinColumn['name'], '`');
1778 3
                    $inverseJoinColumn['quoted'] = true;
1779
                }
1780
1781 130
                if ($inverseJoinColumn['referencedColumnName'][0] === '`') {
1782 3
                    $inverseJoinColumn['referencedColumnName']  = trim($inverseJoinColumn['referencedColumnName'], '`');
1783 3
                    $inverseJoinColumn['quoted']                = true;
1784
                }
1785
1786 130
                if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
1787 27
                    $mapping['isOnDeleteCascade'] = true;
1788
                }
1789
1790 130
                $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
1791 130
                $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
1792
            }
1793
        }
1794
1795 148
        $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1796
1797 148
        if (isset($mapping['orderBy'])) {
1798 3
            if ( ! is_array($mapping['orderBy'])) {
1799
                throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
1800
            }
1801
        }
1802
1803 148
        return $mapping;
1804
    }
1805
1806
    /**
1807
     * {@inheritDoc}
1808
     */
1809 593
    public function getIdentifierFieldNames()
1810
    {
1811 593
        return $this->identifier;
1812
    }
1813
1814
    /**
1815
     * Gets the name of the single id field. Note that this only works on
1816
     * entity classes that have a single-field pk.
1817
     *
1818
     * @return string
1819
     *
1820
     * @throws MappingException If the class has a composite primary key.
1821
     */
1822 398
    public function getSingleIdentifierFieldName()
1823
    {
1824 398
        if ($this->isIdentifierComposite) {
1825 1
            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
1826
        }
1827
1828 397
        return $this->identifier[0];
1829
    }
1830
1831
    /**
1832
     * Gets the column name of the single id column. Note that this only works on
1833
     * entity classes that have a single-field pk.
1834
     *
1835
     * @return string
1836
     *
1837
     * @throws MappingException If the class has a composite primary key.
1838
     */
1839 3
    public function getSingleIdentifierColumnName()
1840
    {
1841 3
        return $this->getColumnName($this->getSingleIdentifierFieldName());
1842
    }
1843
1844
    /**
1845
     * INTERNAL:
1846
     * Sets the mapped identifier/primary key fields of this class.
1847
     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1848
     *
1849
     * @param array $identifier
1850
     *
1851
     * @return void
1852
     */
1853 123
    public function setIdentifier(array $identifier)
1854
    {
1855 123
        $this->identifier = $identifier;
1856 123
        $this->isIdentifierComposite = (count($this->identifier) > 1);
1857 123
    }
1858
1859
    /**
1860
     * {@inheritDoc}
1861
     */
1862 61
    public function getIdentifier()
1863
    {
1864 61
        return $this->identifier;
1865
    }
1866
1867
    /**
1868
     * {@inheritDoc}
1869
     */
1870 292
    public function hasField($fieldName)
1871
    {
1872 292
        return isset($this->fieldMappings[$fieldName]);
1873
    }
1874
1875
    /**
1876
     * Gets an array containing all the column names.
1877
     *
1878
     * @param array|null $fieldNames
1879
     *
1880
     * @return array
1881
     */
1882 42
    public function getColumnNames(array $fieldNames = null)
1883
    {
1884 42
        if (null === $fieldNames) {
1885 41
            return array_keys($this->fieldNames);
1886
        }
1887
1888 1
        return array_values(array_map([$this, 'getColumnName'], $fieldNames));
1889
    }
1890
1891
    /**
1892
     * Returns an array with all the identifier column names.
1893
     *
1894
     * @return array
1895
     */
1896 322
    public function getIdentifierColumnNames()
1897
    {
1898 322
        $columnNames = array();
1899
1900 322
        foreach ($this->identifier as $idProperty) {
1901 322
            if (isset($this->fieldMappings[$idProperty])) {
1902 318
                $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
1903
1904 318
                continue;
1905
            }
1906
1907
            // Association defined as Id field
1908 22
            $joinColumns      = $this->associationMappings[$idProperty]['joinColumns'];
1909
            $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
1910
1911 22
            $columnNames = array_merge($columnNames, $assocColumnNames);
1912
        }
1913
1914 322
        return $columnNames;
1915
    }
1916
1917
    /**
1918
     * Sets the type of Id generator to use for the mapped class.
1919
     *
1920
     * @param int $generatorType
1921
     *
1922
     * @return void
1923
     */
1924 458
    public function setIdGeneratorType($generatorType)
1925
    {
1926 458
        $this->generatorType = $generatorType;
1927 458
    }
1928
1929
    /**
1930
     * Checks whether the mapped class uses an Id generator.
1931
     *
1932
     * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
1933
     */
1934 393
    public function usesIdGenerator()
1935
    {
1936 393
        return $this->generatorType != self::GENERATOR_TYPE_NONE;
1937
    }
1938
1939
    /**
1940
     * @return boolean
1941
     */
1942 1324
    public function isInheritanceTypeNone()
1943
    {
1944 1324
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1945
    }
1946
1947
    /**
1948
     * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
1949
     *
1950
     * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
1951
     *                 FALSE otherwise.
1952
     */
1953 1049
    public function isInheritanceTypeJoined()
1954
    {
1955 1049
        return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
1956
    }
1957
1958
    /**
1959
     * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
1960
     *
1961
     * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
1962
     *                 FALSE otherwise.
1963
     */
1964 1221
    public function isInheritanceTypeSingleTable()
1965
    {
1966 1221
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
1967
    }
1968
1969
    /**
1970
     * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
1971
     *
1972
     * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
1973
     *                 FALSE otherwise.
1974
     */
1975 259
    public function isInheritanceTypeTablePerClass()
1976
    {
1977 259
        return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
1978
    }
1979
1980
    /**
1981
     * Checks whether the class uses an identity column for the Id generation.
1982
     *
1983
     * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
1984
     */
1985 1061
    public function isIdGeneratorIdentity()
1986
    {
1987 1061
        return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
1988
    }
1989
1990
    /**
1991
     * Checks whether the class uses a sequence for id generation.
1992
     *
1993
     * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
1994
     */
1995 312
    public function isIdGeneratorSequence()
1996
    {
1997 312
        return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
1998
    }
1999
2000
    /**
2001
     * Checks whether the class uses a table for id generation.
2002
     *
2003
     * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
2004
     */
2005 80
    public function isIdGeneratorTable()
2006
    {
2007 80
        return $this->generatorType == self::GENERATOR_TYPE_TABLE;
2008
    }
2009
2010
    /**
2011
     * Checks whether the class has a natural identifier/pk (which means it does
2012
     * not use any Id generator.
2013
     *
2014
     * @return boolean
2015
     */
2016 73
    public function isIdentifierNatural()
2017
    {
2018 73
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
2019
    }
2020
2021
    /**
2022
     * Checks whether the class use a UUID for id generation.
2023
     *
2024
     * @return boolean
2025
     */
2026
    public function isIdentifierUuid()
2027
    {
2028
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
2029
    }
2030
2031
    /**
2032
     * Gets the type of a field.
2033
     *
2034
     * @param string $fieldName
2035
     *
2036
     * @return \Doctrine\DBAL\Types\Type|string|null
2037
     *
2038
     * @todo 3.0 Remove this. PersisterHelper should fix it somehow
2039
     */
2040 38
    public function getTypeOfField($fieldName)
2041
    {
2042 38
        return isset($this->fieldMappings[$fieldName])
2043 38
            ? $this->fieldMappings[$fieldName]['type']
2044 38
            : null;
2045
    }
2046
2047
    /**
2048
     * Gets the type of a column.
2049
     *
2050
     * @param string $columnName
2051
     *
2052
     * @return \Doctrine\DBAL\Types\Type|string|null
2053
     *
2054
     * @deprecated 3.0 remove this. this method is bogous and unreliable, since it cannot resolve the type of a column
2055
     *             that is derived by a referenced field on a different entity.
2056
     */
2057
    public function getTypeOfColumn($columnName)
2058
    {
2059
        return $this->getTypeOfField($this->getFieldName($columnName));
2060
    }
2061
2062
    /**
2063
     * Gets the name of the primary table.
2064
     *
2065
     * @return string
2066
     */
2067 1463
    public function getTableName()
2068
    {
2069 1463
        return $this->table['name'];
2070
    }
2071
2072
    /**
2073
     * Gets primary table's schema name.
2074
     *
2075
     * @return string|null
2076
     */
2077 13
    public function getSchemaName()
2078
    {
2079 13
        return isset($this->table['schema']) ? $this->table['schema'] : null;
2080
    }
2081
2082
    /**
2083
     * Gets the table name to use for temporary identifier tables of this class.
2084
     *
2085
     * @return string
2086
     */
2087 7
    public function getTemporaryIdTableName()
2088
    {
2089
        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
2090 7
        return str_replace('.', '_', $this->getTableName() . '_id_tmp');
2091
    }
2092
2093
    /**
2094
     * Sets the mapped subclasses of this class.
2095
     *
2096
     * @param array $subclasses The names of all mapped subclasses.
2097
     *
2098
     * @return void
2099
     */
2100 2
    public function setSubclasses(array $subclasses)
2101
    {
2102 2
        foreach ($subclasses as $subclass) {
2103 2
            $this->subClasses[] = $this->fullyQualifiedClassName($subclass);
2104
        }
2105 2
    }
2106
2107
    /**
2108
     * Sets the parent class names.
2109
     * Assumes that the class names in the passed array are in the order:
2110
     * directParent -> directParentParent -> directParentParentParent ... -> root.
2111
     *
2112
     * @param array $classNames
2113
     *
2114
     * @return void
2115
     */
2116 410
    public function setParentClasses(array $classNames)
2117
    {
2118 410
        $this->parentClasses = $classNames;
2119
2120 410
        if (count($classNames) > 0) {
2121 79
            $this->rootEntityName = array_pop($classNames);
2122
        }
2123 410
    }
2124
2125
    /**
2126
     * Sets the inheritance type used by the class and its subclasses.
2127
     *
2128
     * @param integer $type
2129
     *
2130
     * @return void
2131
     *
2132
     * @throws MappingException
2133
     */
2134 169
    public function setInheritanceType($type)
2135
    {
2136 169
        if ( ! $this->_isInheritanceType($type)) {
2137
            throw MappingException::invalidInheritanceType($this->name, $type);
2138
        }
2139
2140 169
        $this->inheritanceType = $type;
2141 169
    }
2142
2143
    /**
2144
     * Sets the association to override association mapping of property for an entity relationship.
2145
     *
2146
     * @param string $fieldName
2147
     * @param array  $overrideMapping
2148
     *
2149
     * @return void
2150
     *
2151
     * @throws MappingException
2152
     */
2153 20
    public function setAssociationOverride($fieldName, array $overrideMapping)
2154
    {
2155 20
        if ( ! isset($this->associationMappings[$fieldName])) {
2156 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2157
        }
2158
2159 19
        $mapping = $this->associationMappings[$fieldName];
2160
2161 19
        if (isset($overrideMapping['joinColumns'])) {
2162 13
            $mapping['joinColumns'] = $overrideMapping['joinColumns'];
2163
        }
2164
2165 19
        if (isset($overrideMapping['inversedBy'])) {
2166 6
            $mapping['inversedBy'] = $overrideMapping['inversedBy'];
2167
        }
2168
2169 19
        if (isset($overrideMapping['joinTable'])) {
2170 12
            $mapping['joinTable'] = $overrideMapping['joinTable'];
2171
        }
2172
2173 19
        $mapping['joinColumnFieldNames']        = null;
2174 19
        $mapping['joinTableColumns']            = null;
2175 19
        $mapping['sourceToTargetKeyColumns']    = null;
2176 19
        $mapping['relationToSourceKeyColumns']  = null;
2177 19
        $mapping['relationToTargetKeyColumns']  = null;
2178
2179 19
        switch ($mapping['type']) {
2180 19
            case self::ONE_TO_ONE:
2181 1
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2182 1
                break;
2183 18
            case self::ONE_TO_MANY:
2184
                $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2185
                break;
2186 18
            case self::MANY_TO_ONE:
2187 12
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2188 12
                break;
2189 18
            case self::MANY_TO_MANY:
2190 18
                $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2191 18
                break;
2192
        }
2193
2194 19
        $this->associationMappings[$fieldName] = $mapping;
2195 19
    }
2196
2197
    /**
2198
     * Sets the override for a mapped field.
2199
     *
2200
     * @param string $fieldName
2201
     * @param array  $overrideMapping
2202
     *
2203
     * @return void
2204
     *
2205
     * @throws MappingException
2206
     */
2207 15
    public function setAttributeOverride($fieldName, array $overrideMapping)
2208
    {
2209 15
        if ( ! isset($this->fieldMappings[$fieldName])) {
2210 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2211
        }
2212
2213 14
        $mapping = $this->fieldMappings[$fieldName];
2214
2215 14
        if (isset($mapping['id'])) {
2216 12
            $overrideMapping['id'] = $mapping['id'];
2217
        }
2218
2219 14
        if ( ! isset($overrideMapping['type']) || $overrideMapping['type'] === null) {
2220 6
            $overrideMapping['type'] = $mapping['type'];
2221
        }
2222
2223 14
        if ( ! isset($overrideMapping['fieldName']) || $overrideMapping['fieldName'] === null) {
2224 5
            $overrideMapping['fieldName'] = $mapping['fieldName'];
2225
        }
2226
2227 14
        if ($overrideMapping['type'] !== $mapping['type']) {
2228 1
            throw MappingException::invalidOverrideFieldType($this->name, $fieldName);
2229
        }
2230
2231 13
        unset($this->fieldMappings[$fieldName]);
2232 13
        unset($this->fieldNames[$mapping['columnName']]);
2233 13
        unset($this->columnNames[$mapping['fieldName']]);
2234
2235 13
        $this->_validateAndCompleteFieldMapping($overrideMapping);
2236
2237 13
        $this->fieldMappings[$fieldName] = $overrideMapping;
2238 13
    }
2239
2240
    /**
2241
     * Checks whether a mapped field is inherited from an entity superclass.
2242
     *
2243
     * @param string $fieldName
2244
     *
2245
     * @return bool TRUE if the field is inherited, FALSE otherwise.
2246
     */
2247 370
    public function isInheritedField($fieldName)
2248
    {
2249 370
        return isset($this->fieldMappings[$fieldName]['inherited']);
2250
    }
2251
2252
    /**
2253
     * Checks if this entity is the root in any entity-inheritance-hierarchy.
2254
     *
2255
     * @return bool
2256
     */
2257 409
    public function isRootEntity()
2258
    {
2259 409
        return $this->name == $this->rootEntityName;
2260
    }
2261
2262
    /**
2263
     * Checks whether a mapped association field is inherited from a superclass.
2264
     *
2265
     * @param string $fieldName
2266
     *
2267
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
2268
     */
2269 349
    public function isInheritedAssociation($fieldName)
2270
    {
2271 349
        return isset($this->associationMappings[$fieldName]['inherited']);
2272
    }
2273
2274 349
    public function isInheritedEmbeddedClass($fieldName)
2275
    {
2276 349
        return isset($this->embeddedClasses[$fieldName]['inherited']);
2277
    }
2278
2279
    /**
2280
     * Sets the name of the primary table the class is mapped to.
2281
     *
2282
     * @param string $tableName The table name.
2283
     *
2284
     * @return void
2285
     *
2286
     * @deprecated Use {@link setPrimaryTable}.
2287
     */
2288
    public function setTableName($tableName)
2289
    {
2290
        $this->table['name'] = $tableName;
2291
    }
2292
2293
    /**
2294
     * Sets the primary table definition. The provided array supports the
2295
     * following structure:
2296
     *
2297
     * name => <tableName> (optional, defaults to class name)
2298
     * indexes => array of indexes (optional)
2299
     * uniqueConstraints => array of constraints (optional)
2300
     *
2301
     * If a key is omitted, the current value is kept.
2302
     *
2303
     * @param array $table The table description.
2304
     *
2305
     * @return void
2306
     */
2307 325
    public function setPrimaryTable(array $table)
2308
    {
2309 325
        if (isset($table['name'])) {
2310
            // Split schema and table name from a table name like "myschema.mytable"
2311 262
            if (strpos($table['name'], '.') !== false) {
2312 9
                list($this->table['schema'], $table['name']) = explode('.', $table['name'], 2);
2313
            }
2314
2315 262
            if ($table['name'][0] === '`') {
2316 17
                $table['name']          = trim($table['name'], '`');
2317 17
                $this->table['quoted']  = true;
2318
            }
2319
2320 262
            $this->table['name'] = $table['name'];
2321
        }
2322
2323 325
        if (isset($table['schema'])) {
2324 6
            $this->table['schema'] = $table['schema'];
2325
        }
2326
2327 325
        if (isset($table['indexes'])) {
2328 15
            $this->table['indexes'] = $table['indexes'];
2329
        }
2330
2331 325
        if (isset($table['uniqueConstraints'])) {
2332 8
            $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
2333
        }
2334
2335 325
        if (isset($table['options'])) {
2336 9
            $this->table['options'] = $table['options'];
2337
        }
2338 325
    }
2339
2340
    /**
2341
     * Checks whether the given type identifies an inheritance type.
2342
     *
2343
     * @param integer $type
2344
     *
2345
     * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
2346
     */
2347 169
    private function _isInheritanceType($type)
2348
    {
2349 169
        return $type == self::INHERITANCE_TYPE_NONE ||
2350 105
                $type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
2351 50
                $type == self::INHERITANCE_TYPE_JOINED ||
2352 169
                $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
2353
    }
2354
2355
    /**
2356
     * Adds a mapped field to the class.
2357
     *
2358
     * @param array $mapping The field mapping.
2359
     *
2360
     * @return void
2361
     *
2362
     * @throws MappingException
2363
     */
2364 529
    public function mapField(array $mapping)
2365
    {
2366 529
        $this->_validateAndCompleteFieldMapping($mapping);
2367 527
        $this->assertFieldNotMapped($mapping['fieldName']);
2368
2369 526
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
2370 526
    }
2371
2372
    /**
2373
     * INTERNAL:
2374
     * Adds an association mapping without completing/validating it.
2375
     * This is mainly used to add inherited association mappings to derived classes.
2376
     *
2377
     * @param array $mapping
2378
     *
2379
     * @return void
2380
     *
2381
     * @throws MappingException
2382
     */
2383 48
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
2384
    {
2385 48
        if (isset($this->associationMappings[$mapping['fieldName']])) {
2386 1
            throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
2387
        }
2388 48
        $this->associationMappings[$mapping['fieldName']] = $mapping;
2389 48
    }
2390
2391
    /**
2392
     * INTERNAL:
2393
     * Adds a field mapping without completing/validating it.
2394
     * This is mainly used to add inherited field mappings to derived classes.
2395
     *
2396
     * @param array $fieldMapping
2397
     *
2398
     * @return void
2399
     */
2400 107
    public function addInheritedFieldMapping(array $fieldMapping)
2401
    {
2402 107
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
2403 107
        $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 with message: 3.0 Remove this.

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...
2404 107
        $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
2405 107
    }
2406
2407
    /**
2408
     * INTERNAL:
2409
     * Adds a named query to this class.
2410
     *
2411
     * @param array $queryMapping
2412
     *
2413
     * @return void
2414
     *
2415
     * @throws MappingException
2416
     */
2417 29
    public function addNamedQuery(array $queryMapping)
2418
    {
2419 29
        if (!isset($queryMapping['name'])) {
2420 2
            throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2421
        }
2422
2423 27
        if (isset($this->namedQueries[$queryMapping['name']])) {
2424 1
            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2425
        }
2426
2427 27
        if (!isset($queryMapping['query'])) {
2428
            throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2429
        }
2430
2431 27
        $name   = $queryMapping['name'];
2432 27
        $query  = $queryMapping['query'];
2433 27
        $dql    = str_replace('__CLASS__', $this->name, $query);
2434
2435 27
        $this->namedQueries[$name] = array(
2436 27
            'name'  => $name,
2437 27
            'query' => $query,
2438 27
            'dql'   => $dql,
2439
        );
2440 27
    }
2441
2442
    /**
2443
     * INTERNAL:
2444
     * Adds a named native query to this class.
2445
     *
2446
     * @param array $queryMapping
2447
     *
2448
     * @return void
2449
     *
2450
     * @throws MappingException
2451
     */
2452 39
    public function addNamedNativeQuery(array $queryMapping)
2453
    {
2454 39
        if (!isset($queryMapping['name'])) {
2455
            throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2456
        }
2457
2458 39
        if (isset($this->namedNativeQueries[$queryMapping['name']])) {
2459 1
            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2460
        }
2461
2462 39
        if (!isset($queryMapping['query'])) {
2463
            throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2464
        }
2465
2466 39
        if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) {
2467
            throw MappingException::missingQueryMapping($this->name, $queryMapping['name']);
2468
        }
2469
2470 39
        $queryMapping['isSelfClass'] = false;
2471
2472 39
        if (isset($queryMapping['resultClass'])) {
2473 37
            if ($queryMapping['resultClass'] === '__CLASS__') {
2474
2475 11
                $queryMapping['isSelfClass'] = true;
2476 11
                $queryMapping['resultClass'] = $this->name;
2477
            }
2478
2479 37
            $queryMapping['resultClass'] = $this->fullyQualifiedClassName($queryMapping['resultClass']);
0 ignored issues
show
Bug introduced by
It seems like $queryMapping['resultClass'] can also be of type boolean; however, Doctrine\ORM\Mapping\Cla...llyQualifiedClassName() does only seem to accept string|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
2480 37
            $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\');
2481
        }
2482
2483 39
        $this->namedNativeQueries[$queryMapping['name']] = $queryMapping;
2484 39
    }
2485
2486
    /**
2487
     * INTERNAL:
2488
     * Adds a sql result set mapping to this class.
2489
     *
2490
     * @param array $resultMapping
2491
     *
2492
     * @return void
2493
     *
2494
     * @throws MappingException
2495
     */
2496 39
    public function addSqlResultSetMapping(array $resultMapping)
2497
    {
2498 39
        if (!isset($resultMapping['name'])) {
2499
            throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name);
2500
        }
2501
2502 39
        if (isset($this->sqlResultSetMappings[$resultMapping['name']])) {
2503 1
            throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']);
2504
        }
2505
2506 39
        if (isset($resultMapping['entities'])) {
2507 39
            foreach ($resultMapping['entities'] as $key => $entityResult) {
2508 39
                if (!isset($entityResult['entityClass'])) {
2509 1
                    throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']);
2510
                }
2511
2512 38
                $entityResult['isSelfClass'] = false;
2513 38
                if ($entityResult['entityClass'] === '__CLASS__') {
2514
2515 20
                    $entityResult['isSelfClass'] = true;
2516 20
                    $entityResult['entityClass'] = $this->name;
2517
2518
                }
2519
2520 38
                $entityResult['entityClass'] = $this->fullyQualifiedClassName($entityResult['entityClass']);
0 ignored issues
show
Bug introduced by
It seems like $entityResult['entityClass'] can also be of type boolean; however, Doctrine\ORM\Mapping\Cla...llyQualifiedClassName() does only seem to accept string|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
2521
2522 38
                $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\');
2523 38
                $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass'];
2524
2525 38
                if (isset($entityResult['fields'])) {
2526 34
                    foreach ($entityResult['fields'] as $k => $field) {
0 ignored issues
show
Bug introduced by
The expression $entityResult['fields'] of type boolean|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
2527 34
                        if (!isset($field['name'])) {
2528
                            throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']);
2529
                        }
2530
2531 34
                        if (!isset($field['column'])) {
2532 14
                            $fieldName = $field['name'];
2533 14
                            if (strpos($fieldName, '.')) {
2534 9
                                list(, $fieldName) = explode('.', $fieldName);
2535
                            }
2536
2537 38
                            $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName;
2538
                        }
2539
                    }
2540
                }
2541
            }
2542
        }
2543
2544 38
        $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping;
2545 38
    }
2546
2547
    /**
2548
     * Adds a one-to-one mapping.
2549
     *
2550
     * @param array $mapping The mapping.
2551
     *
2552
     * @return void
2553
     */
2554 167
    public function mapOneToOne(array $mapping)
2555
    {
2556 167
        $mapping['type'] = self::ONE_TO_ONE;
2557
2558 167
        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2559
2560 164
        $this->_storeAssociationMapping($mapping);
2561 163
    }
2562
2563
    /**
2564
     * Adds a one-to-many mapping.
2565
     *
2566
     * @param array $mapping The mapping.
2567
     *
2568
     * @return void
2569
     */
2570 129
    public function mapOneToMany(array $mapping)
2571
    {
2572 129
        $mapping['type'] = self::ONE_TO_MANY;
2573
2574 129
        $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2575
2576 128
        $this->_storeAssociationMapping($mapping);
2577 128
    }
2578
2579
    /**
2580
     * Adds a many-to-one mapping.
2581
     *
2582
     * @param array $mapping The mapping.
2583
     *
2584
     * @return void
2585
     */
2586 164
    public function mapManyToOne(array $mapping)
2587
    {
2588 164
        $mapping['type'] = self::MANY_TO_ONE;
2589
2590
        // A many-to-one mapping is essentially a one-one backreference
2591 164
        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2592
2593 159
        $this->_storeAssociationMapping($mapping);
2594 159
    }
2595
2596
    /**
2597
     * Adds a many-to-many mapping.
2598
     *
2599
     * @param array $mapping The mapping.
2600
     *
2601
     * @return void
2602
     */
2603 150
    public function mapManyToMany(array $mapping)
2604
    {
2605 150
        $mapping['type'] = self::MANY_TO_MANY;
2606
2607 150
        $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2608
2609 148
        $this->_storeAssociationMapping($mapping);
2610 148
    }
2611
2612
    /**
2613
     * Stores the association mapping.
2614
     *
2615
     * @param array $assocMapping
2616
     *
2617
     * @return void
2618
     *
2619
     * @throws MappingException
2620
     */
2621 341
    protected function _storeAssociationMapping(array $assocMapping)
2622
    {
2623 341
        $sourceFieldName = $assocMapping['fieldName'];
2624
2625 341
        $this->assertFieldNotMapped($sourceFieldName);
2626
2627 340
        $this->associationMappings[$sourceFieldName] = $assocMapping;
2628 340
    }
2629
2630
    /**
2631
     * Registers a custom repository class for the entity class.
2632
     *
2633
     * @param string $repositoryClassName The class name of the custom mapper.
2634
     *
2635
     * @return void
2636
     */
2637 62
    public function setCustomRepositoryClass($repositoryClassName)
2638
    {
2639 62
        $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName);
2640 62
    }
2641
2642
    /**
2643
     * @param string $collectionClassName
2644
     *
2645
     * @return void
2646
     */
2647 8
    public function setCustomCollectionClass($collectionClassName)
2648
    {
2649 8
        $this->customCollectionClassName = $this->fullyQualifiedClassName($collectionClassName);
2650 8
    }
2651
2652
    /**
2653
     * Dispatches the lifecycle event of the given entity to the registered
2654
     * lifecycle callbacks and lifecycle listeners.
2655
     *
2656
     * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker
2657
     *
2658
     * @param string $lifecycleEvent The lifecycle event.
2659
     * @param object $entity         The Entity on which the event occurred.
2660
     *
2661
     * @return void
2662
     */
2663
    public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
2664
    {
2665
        foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
2666
            $entity->$callback();
2667
        }
2668
    }
2669
2670
    /**
2671
     * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
2672
     *
2673
     * @param string $lifecycleEvent
2674
     *
2675
     * @return boolean
2676
     */
2677
    public function hasLifecycleCallbacks($lifecycleEvent)
2678
    {
2679
        return isset($this->lifecycleCallbacks[$lifecycleEvent]);
2680
    }
2681
2682
    /**
2683
     * Gets the registered lifecycle callbacks for an event.
2684
     *
2685
     * @param string $event
2686
     *
2687
     * @return array
2688
     */
2689
    public function getLifecycleCallbacks($event)
2690
    {
2691
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
2692
    }
2693
2694
    /**
2695
     * Adds a lifecycle callback for entities of this class.
2696
     *
2697
     * @param string $callback
2698
     * @param string $event
2699
     *
2700
     * @return void
2701
     */
2702 41
    public function addLifecycleCallback($callback, $event)
2703
    {
2704 41
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
2705 3
            return;
2706
        }
2707
2708 41
        $this->lifecycleCallbacks[$event][] = $callback;
2709 41
    }
2710
2711
    /**
2712
     * Sets the lifecycle callbacks for entities of this class.
2713
     * Any previously registered callbacks are overwritten.
2714
     *
2715
     * @param array $callbacks
2716
     *
2717
     * @return void
2718
     */
2719 122
    public function setLifecycleCallbacks(array $callbacks)
2720
    {
2721 122
        $this->lifecycleCallbacks = $callbacks;
2722 122
    }
2723
2724
    /**
2725
     * Adds a entity listener for entities of this class.
2726
     *
2727
     * @param string $eventName The entity lifecycle event.
2728
     * @param string $class     The listener class.
2729
     * @param string $method    The listener callback method.
2730
     *
2731
     * @throws \Doctrine\ORM\Mapping\MappingException
2732
     */
2733 35
    public function addEntityListener($eventName, $class, $method)
2734
    {
2735 35
        $class    = $this->fullyQualifiedClassName($class);
2736
2737
        $listener = array(
2738 35
            'class'  => $class,
2739 35
            'method' => $method,
2740
        );
2741
2742 35
        if ( ! class_exists($class)) {
2743 1
            throw MappingException::entityListenerClassNotFound($class, $this->name);
2744
        }
2745
2746 34
        if ( ! method_exists($class, $method)) {
2747 1
            throw MappingException::entityListenerMethodNotFound($class, $method, $this->name);
2748
        }
2749
2750 33
        if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName])) {
2751 1
            throw MappingException::duplicateEntityListener($class, $method, $this->name);
2752
        }
2753
2754 33
        $this->entityListeners[$eventName][] = $listener;
2755 33
    }
2756
2757
    /**
2758
     * Sets the discriminator column definition.
2759
     *
2760
     * @param array $columnDef
2761
     *
2762
     * @return void
2763
     *
2764
     * @throws MappingException
2765
     *
2766
     * @see getDiscriminatorColumn()
2767
     */
2768 163
    public function setDiscriminatorColumn($columnDef)
2769
    {
2770 163
        if ($columnDef !== null) {
2771 109
            if ( ! isset($columnDef['name'])) {
2772 1
                throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name);
2773
            }
2774
2775 108
            if (isset($this->fieldNames[$columnDef['name']])) {
2776 1
                throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
2777
            }
2778
2779 107
            if ( ! isset($columnDef['fieldName'])) {
2780 102
                $columnDef['fieldName'] = $columnDef['name'];
2781
            }
2782
2783 107
            if ( ! isset($columnDef['type'])) {
2784 2
                $columnDef['type'] = "string";
2785
            }
2786
2787 107
            if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
2788
                throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
2789
            }
2790
2791 107
            $this->discriminatorColumn = $columnDef;
2792
        }
2793 161
    }
2794
2795
    /**
2796
     * Sets the discriminator values used by this class.
2797
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
2798
     *
2799
     * @param array $map
2800
     *
2801
     * @return void
2802
     */
2803 156
    public function setDiscriminatorMap(array $map)
2804
    {
2805 156
        foreach ($map as $value => $className) {
2806 103
            $this->addDiscriminatorMapClass($value, $className);
2807
        }
2808 156
    }
2809
2810
    /**
2811
     * Adds one entry of the discriminator map with a new class and corresponding name.
2812
     *
2813
     * @param string $name
2814
     * @param string $className
2815
     *
2816
     * @return void
2817
     *
2818
     * @throws MappingException
2819
     */
2820 104
    public function addDiscriminatorMapClass($name, $className)
2821
    {
2822 104
        $className = $this->fullyQualifiedClassName($className);
2823 104
        $className = ltrim($className, '\\');
2824
2825 104
        $this->discriminatorMap[$name] = $className;
2826
2827 104
        if ($this->name === $className) {
2828 75
            $this->discriminatorValue = $name;
2829
2830 75
            return;
2831
        }
2832
2833 103
        if ( ! (class_exists($className) || interface_exists($className))) {
2834
            throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
2835
        }
2836
2837 103
        if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
2838 94
            $this->subClasses[] = $className;
2839
        }
2840 103
    }
2841
2842
    /**
2843
     * Checks whether the class has a named query with the given query name.
2844
     *
2845
     * @param string $queryName
2846
     *
2847
     * @return boolean
2848
     */
2849 1
    public function hasNamedQuery($queryName)
2850
    {
2851 1
        return isset($this->namedQueries[$queryName]);
2852
    }
2853
2854
    /**
2855
     * Checks whether the class has a named native query with the given query name.
2856
     *
2857
     * @param string $queryName
2858
     *
2859
     * @return boolean
2860
     */
2861 1
    public function hasNamedNativeQuery($queryName)
2862
    {
2863 1
        return isset($this->namedNativeQueries[$queryName]);
2864
    }
2865
2866
    /**
2867
     * Checks whether the class has a named native query with the given query name.
2868
     *
2869
     * @param string $name
2870
     *
2871
     * @return boolean
2872
     */
2873 1
    public function hasSqlResultSetMapping($name)
2874
    {
2875 1
        return isset($this->sqlResultSetMappings[$name]);
2876
    }
2877
2878
    /**
2879
     * {@inheritDoc}
2880
     */
2881 341
    public function hasAssociation($fieldName)
2882
    {
2883 341
        return isset($this->associationMappings[$fieldName]);
2884
    }
2885
2886
    /**
2887
     * {@inheritDoc}
2888
     */
2889 1
    public function isSingleValuedAssociation($fieldName)
2890
    {
2891 1
        return isset($this->associationMappings[$fieldName])
2892 1
            && ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2893
    }
2894
2895
    /**
2896
     * {@inheritDoc}
2897
     */
2898 1024
    public function isCollectionValuedAssociation($fieldName)
2899
    {
2900 1024
        return isset($this->associationMappings[$fieldName])
2901 1024
            && ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2902
    }
2903
2904
    /**
2905
     * Is this an association that only has a single join column?
2906
     *
2907
     * @param string $fieldName
2908
     *
2909
     * @return bool
2910
     */
2911 35
    public function isAssociationWithSingleJoinColumn($fieldName)
2912
    {
2913 35
        return isset($this->associationMappings[$fieldName])
2914 35
            && isset($this->associationMappings[$fieldName]['joinColumns'][0])
2915 35
            && ! isset($this->associationMappings[$fieldName]['joinColumns'][1]);
2916
    }
2917
2918
    /**
2919
     * Returns the single association join column (if any).
2920
     *
2921
     * @param string $fieldName
2922
     *
2923
     * @return string
2924
     *
2925
     * @throws MappingException
2926
     */
2927 9
    public function getSingleAssociationJoinColumnName($fieldName)
2928
    {
2929 9
        if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2930
            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2931
        }
2932
2933 9
        return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
2934
    }
2935
2936
    /**
2937
     * Returns the single association referenced join column name (if any).
2938
     *
2939
     * @param string $fieldName
2940
     *
2941
     * @return string
2942
     *
2943
     * @throws MappingException
2944
     */
2945 9
    public function getSingleAssociationReferencedJoinColumnName($fieldName)
2946
    {
2947 9
        if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2948
            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2949
        }
2950
2951 9
        return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
2952
    }
2953
2954
    /**
2955
     * Used to retrieve a fieldname for either field or association from a given column.
2956
     *
2957
     * This method is used in foreign-key as primary-key contexts.
2958
     *
2959
     * @param string $columnName
2960
     *
2961
     * @return string
2962
     *
2963
     * @throws MappingException
2964
     */
2965 633
    public function getFieldForColumn($columnName)
2966
    {
2967 633
        if (isset($this->fieldNames[$columnName])) {
2968 633
            return $this->fieldNames[$columnName];
2969
        } else {
2970 33
            foreach ($this->associationMappings as $assocName => $mapping) {
2971 33
                if ($this->isAssociationWithSingleJoinColumn($assocName) &&
2972 33
                    $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
2973
2974 33
                    return $assocName;
2975
                }
2976
            }
2977
2978
            throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
2979
        }
2980
    }
2981
2982
    /**
2983
     * Sets the ID generator used to generate IDs for instances of this class.
2984
     *
2985
     * @param \Doctrine\ORM\Id\AbstractIdGenerator $generator
2986
     *
2987
     * @return void
2988
     */
2989 412
    public function setIdGenerator($generator)
2990
    {
2991 412
        $this->idGenerator = $generator;
2992 412
    }
2993
2994
    /**
2995
     * Sets definition.
2996
     *
2997
     * @param array $definition
2998
     *
2999
     * @return void
3000
     */
3001 12
    public function setCustomGeneratorDefinition(array $definition)
3002
    {
3003 12
        $this->customGeneratorDefinition = $definition;
3004 12
    }
3005
3006
    /**
3007
     * Sets the definition of the sequence ID generator for this class.
3008
     *
3009
     * The definition must have the following structure:
3010
     * <code>
3011
     * array(
3012
     *     'sequenceName'   => 'name',
3013
     *     'allocationSize' => 20,
3014
     *     'initialValue'   => 1
3015
     *     'quoted'         => 1
3016
     * )
3017
     * </code>
3018
     *
3019
     * @param array $definition
3020
     *
3021
     * @return void
3022
     *
3023
     * @throws MappingException
3024
     */
3025 23
    public function setSequenceGeneratorDefinition(array $definition)
3026
    {
3027 23
        if ( ! isset($definition['sequenceName'])) {
3028 1
            throw MappingException::missingSequenceName($this->name);
3029
        }
3030
3031 22
        if ($definition['sequenceName'][0] == '`') {
3032 1
            $definition['sequenceName']   = trim($definition['sequenceName'], '`');
3033 1
            $definition['quoted'] = true;
3034
        }
3035
3036 22
        $this->sequenceGeneratorDefinition = $definition;
3037 22
    }
3038
3039
    /**
3040
     * Sets the version field mapping used for versioning. Sets the default
3041
     * value to use depending on the column type.
3042
     *
3043
     * @param array $mapping The version field mapping array.
3044
     *
3045
     * @return void
3046
     *
3047
     * @throws MappingException
3048
     */
3049 25
    public function setVersionMapping(array &$mapping)
3050
    {
3051 25
        $this->isVersioned = true;
3052 25
        $this->versionField = $mapping['fieldName'];
3053
3054 25
        if ( ! isset($mapping['default'])) {
3055 25
            if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) {
3056 24
                $mapping['default'] = 1;
3057 2
            } else if ($mapping['type'] == 'datetime') {
3058 1
                $mapping['default'] = 'CURRENT_TIMESTAMP';
3059
            } else {
3060 1
                throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
3061
            }
3062
        }
3063 24
    }
3064
3065
    /**
3066
     * Sets whether this class is to be versioned for optimistic locking.
3067
     *
3068
     * @param boolean $bool
3069
     *
3070
     * @return void
3071
     */
3072 122
    public function setVersioned($bool)
3073
    {
3074 122
        $this->isVersioned = $bool;
3075 122
    }
3076
3077
    /**
3078
     * Sets the name of the field that is to be used for versioning if this class is
3079
     * versioned for optimistic locking.
3080
     *
3081
     * @param string $versionField
3082
     *
3083
     * @return void
3084
     */
3085 122
    public function setVersionField($versionField)
3086
    {
3087 122
        $this->versionField = $versionField;
3088 122
    }
3089
3090
    /**
3091
     * Marks this class as read only, no change tracking is applied to it.
3092
     *
3093
     * @return void
3094
     */
3095 3
    public function markReadOnly()
3096
    {
3097 3
        $this->isReadOnly = true;
3098 3
    }
3099
3100
    /**
3101
     * {@inheritDoc}
3102
     */
3103
    public function getFieldNames()
3104
    {
3105
        return array_keys($this->fieldMappings);
3106
    }
3107
3108
    /**
3109
     * {@inheritDoc}
3110
     */
3111
    public function getAssociationNames()
3112
    {
3113
        return array_keys($this->associationMappings);
3114
    }
3115
3116
    /**
3117
     * {@inheritDoc}
3118
     *
3119
     * @throws InvalidArgumentException
3120
     */
3121 1
    public function getAssociationTargetClass($assocName)
3122
    {
3123 1
        if ( ! isset($this->associationMappings[$assocName])) {
3124
            throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
3125
        }
3126
3127 1
        return $this->associationMappings[$assocName]['targetEntity'];
3128
    }
3129
3130
    /**
3131
     * {@inheritDoc}
3132
     */
3133 709
    public function getName()
3134
    {
3135 709
        return $this->name;
3136
    }
3137
3138
    /**
3139
     * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
3140
     *
3141
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3142
     *
3143
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3144
     *
3145
     * @return array
3146
     */
3147
    public function getQuotedIdentifierColumnNames($platform)
3148
    {
3149
        $quotedColumnNames = array();
3150
3151
        foreach ($this->identifier as $idProperty) {
3152
            if (isset($this->fieldMappings[$idProperty])) {
3153
                $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
3154
                    ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
3155
                    : $this->fieldMappings[$idProperty]['columnName'];
3156
3157
                continue;
3158
            }
3159
3160
            // Association defined as Id field
3161
            $joinColumns            = $this->associationMappings[$idProperty]['joinColumns'];
3162
            $assocQuotedColumnNames = array_map(
3163
                function ($joinColumn) use ($platform) {
3164
                    return isset($joinColumn['quoted'])
3165
                        ? $platform->quoteIdentifier($joinColumn['name'])
3166
                        : $joinColumn['name'];
3167
                },
3168
                $joinColumns
3169
            );
3170
3171
            $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
3172
        }
3173
3174
        return $quotedColumnNames;
3175
    }
3176
3177
    /**
3178
     * Gets the (possibly quoted) column name of a mapped field for safe use  in an SQL statement.
3179
     *
3180
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3181
     *
3182
     * @param string                                    $field
3183
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3184
     *
3185
     * @return string
3186
     */
3187
    public function getQuotedColumnName($field, $platform)
3188
    {
3189
        return isset($this->fieldMappings[$field]['quoted'])
3190
            ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
3191
            : $this->fieldMappings[$field]['columnName'];
3192
    }
3193
3194
    /**
3195
     * Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement.
3196
     *
3197
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3198
     *
3199
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3200
     *
3201
     * @return string
3202
     */
3203
    public function getQuotedTableName($platform)
3204
    {
3205
        return isset($this->table['quoted'])
3206
            ? $platform->quoteIdentifier($this->table['name'])
3207
            : $this->table['name'];
3208
    }
3209
3210
    /**
3211
     * Gets the (possibly quoted) name of the join table.
3212
     *
3213
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3214
     *
3215
     * @param array                                     $assoc
3216
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3217
     *
3218
     * @return string
3219
     */
3220
    public function getQuotedJoinTableName(array $assoc, $platform)
3221
    {
3222
        return isset($assoc['joinTable']['quoted'])
3223
            ? $platform->quoteIdentifier($assoc['joinTable']['name'])
3224
            : $assoc['joinTable']['name'];
3225
    }
3226
3227
    /**
3228
     * {@inheritDoc}
3229
     */
3230 12
    public function isAssociationInverseSide($fieldName)
3231
    {
3232 12
        return isset($this->associationMappings[$fieldName])
3233 12
            && ! $this->associationMappings[$fieldName]['isOwningSide'];
3234
    }
3235
3236
    /**
3237
     * {@inheritDoc}
3238
     */
3239
    public function getAssociationMappedByTargetField($fieldName)
3240
    {
3241
        return $this->associationMappings[$fieldName]['mappedBy'];
3242
    }
3243
3244
    /**
3245
     * @param string $targetClass
3246
     *
3247
     * @return array
3248
     */
3249 2
    public function getAssociationsByTargetClass($targetClass)
3250
    {
3251 2
        $relations = array();
3252
3253 2
        foreach ($this->associationMappings as $mapping) {
3254 2
            if ($mapping['targetEntity'] == $targetClass) {
3255 2
                $relations[$mapping['fieldName']] = $mapping;
3256
            }
3257
        }
3258
3259 2
        return $relations;
3260
    }
3261
3262
    /**
3263
     * @param  string|null $className
3264
     *
3265
     * @return string|null null if the input value is null
3266
     */
3267 482
    public function fullyQualifiedClassName($className)
3268
    {
3269 482
        if (empty($className)) {
3270 46
            return $className;
3271
        }
3272
3273 466
        if ($className !== null && strpos($className, '\\') === false && strlen($this->namespace) > 0) {
3274 351
            return $this->namespace . '\\' . $className;
3275
        }
3276
3277 237
        return $className;
3278
    }
3279
3280
    /**
3281
     * @param string $name
3282
     *
3283
     * @return mixed
3284
     */
3285 2
    public function getMetadataValue($name)
3286
    {
3287
3288 2
        if (isset($this->$name)) {
3289 2
            return $this->$name;
3290
        }
3291
3292
        return null;
3293
    }
3294
3295
    /**
3296
     * Map Embedded Class
3297
     *
3298
     * @param array $mapping
3299
     *
3300
     * @throws MappingException
3301
     * @return void
3302
     */
3303 26
    public function mapEmbedded(array $mapping)
3304
    {
3305 26
        $this->assertFieldNotMapped($mapping['fieldName']);
3306
3307 26
        $this->embeddedClasses[$mapping['fieldName']] = array(
3308 26
            'class' => $this->fullyQualifiedClassName($mapping['class']),
3309 26
            'columnPrefix' => $mapping['columnPrefix'],
3310 26
            'declaredField' => isset($mapping['declaredField']) ? $mapping['declaredField'] : null,
3311 26
            'originalField' => isset($mapping['originalField']) ? $mapping['originalField'] : null,
3312
        );
3313 26
    }
3314
3315
    /**
3316
     * Inline the embeddable class
3317
     *
3318
     * @param string            $property
3319
     * @param ClassMetadataInfo $embeddable
3320
     */
3321 10
    public function inlineEmbeddable($property, ClassMetadataInfo $embeddable)
3322
    {
3323 10
        foreach ($embeddable->fieldMappings as $fieldMapping) {
3324 10
            $fieldMapping['originalClass'] = isset($fieldMapping['originalClass'])
3325 4
                ? $fieldMapping['originalClass']
3326 10
                : $embeddable->name;
3327 10
            $fieldMapping['declaredField'] = isset($fieldMapping['declaredField'])
3328 4
                ? $property . '.' . $fieldMapping['declaredField']
3329 10
                : $property;
3330 10
            $fieldMapping['originalField'] = isset($fieldMapping['originalField'])
3331 4
                ? $fieldMapping['originalField']
3332 10
                : $fieldMapping['fieldName'];
3333 10
            $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName'];
3334
3335 10
            if (! empty($this->embeddedClasses[$property]['columnPrefix'])) {
3336 2
                $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName'];
3337 9
            } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) {
3338 6
                $fieldMapping['columnName'] = $this->namingStrategy
3339 6
                    ->embeddedFieldToColumnName(
3340
                        $property,
3341 6
                        $fieldMapping['columnName'],
3342 6
                        $this->reflClass->name,
3343 6
                        $embeddable->reflClass->name
3344
                    );
3345
            }
3346
3347 10
            $this->mapField($fieldMapping);
3348
        }
3349 10
    }
3350
3351
    /**
3352
     * @param string $fieldName
3353
     * @throws MappingException
3354
     */
3355 564
    private function assertFieldNotMapped($fieldName)
3356
    {
3357 564
        if (isset($this->fieldMappings[$fieldName]) ||
3358 564
            isset($this->associationMappings[$fieldName]) ||
3359 564
            isset($this->embeddedClasses[$fieldName])) {
3360
3361 2
            throw MappingException::duplicateFieldMapping($this->name, $fieldName);
3362
        }
3363 564
    }
3364
3365
    /**
3366
     * Gets the sequence name 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 getSequenceName(AbstractPlatform $platform)
3374
    {
3375 3
        $sequencePrefix = $this->getSequencePrefix($platform);
3376 3
        $columnName     = $this->getSingleIdentifierColumnName();
3377 3
        $sequenceName   = $sequencePrefix . '_' . $columnName . '_seq';
3378
3379 3
        return $sequenceName;
3380
    }
3381
3382
    /**
3383
     * Gets the sequence name prefix based on class metadata.
3384
     *
3385
     * @param AbstractPlatform $platform
3386
     * @return string
3387
     *
3388
     * @todo Sequence names should be computed in DBAL depending on the platform
3389
     */
3390 3
    public function getSequencePrefix(AbstractPlatform $platform)
3391
    {
3392 3
        $tableName      = $this->getTableName();
3393 3
        $sequencePrefix = $tableName;
3394
3395
        // Prepend the schema name to the table name if there is one
3396 3
        if ($schemaName = $this->getSchemaName()) {
3397 3
            $sequencePrefix = $schemaName . '.' . $tableName;
3398
3399 3
            if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) {
3400 3
                $sequencePrefix = $schemaName . '__' . $tableName;
3401
            }
3402
        }
3403
3404 3
        return $sequencePrefix;
3405
    }
3406
}
3407