Completed
Pull Request — master (#5586)
by Mihai
25:46 queued 05:14
created

ClassMetadataInfo::setLifecycleCallbacks()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
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 persister class used for the entity class.
261
     * (Optional).
262
     *
263
     * @var string
264
     */
265
    public $customPersisterClassName;
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 643
    public function __construct($entityName, NamingStrategy $namingStrategy = null)
668
    {
669 643
        $this->name = $entityName;
670 643
        $this->rootEntityName = $entityName;
671 643
        $this->namingStrategy = $namingStrategy ?: new DefaultNamingStrategy();
672 643
        $this->instantiator   = new Instantiator();
673 643
    }
674
675
    /**
676
     * Gets the ReflectionProperties of the mapped class.
677
     *
678
     * @return array An array of ReflectionProperty instances.
679
     */
680 221
    public function getReflectionProperties()
681
    {
682 221
        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 459
    public function getIdentifierValues($entity)
724
    {
725 459
        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 440
        $id = $this->identifier[0];
740 440
        $value = $this->reflFields[$id]->getValue($entity);
741
742 440
        if (null === $value) {
743 28
            return array();
744
        }
745
746 417
        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 300
    public function getFieldValue($entity, $field)
789
    {
790 300
        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 5
    public function __sleep()
819
    {
820
        // This metadata is always serialized/cached.
821
        $serialized = array(
822 5
            '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 5
        if ($this->changeTrackingPolicy != self::CHANGETRACKING_DEFERRED_IMPLICIT) {
838
            $serialized[] = 'changeTrackingPolicy';
839
        }
840
841 5
        if ($this->customRepositoryClassName) {
842 1
            $serialized[] = 'customRepositoryClassName';
843
        }
844
845 5
        if ($this->customPersisterClassName) {
846
            $serialized[] = 'customPersisterClassName';
847
        }
848
849 5
        if ($this->inheritanceType != self::INHERITANCE_TYPE_NONE) {
850 1
            $serialized[] = 'inheritanceType';
851 1
            $serialized[] = 'discriminatorColumn';
852 1
            $serialized[] = 'discriminatorValue';
853 1
            $serialized[] = 'discriminatorMap';
854 1
            $serialized[] = 'parentClasses';
855 1
            $serialized[] = 'subClasses';
856
        }
857
858 5
        if ($this->generatorType != self::GENERATOR_TYPE_NONE) {
859 1
            $serialized[] = 'generatorType';
860 1
            if ($this->generatorType == self::GENERATOR_TYPE_SEQUENCE) {
861
                $serialized[] = 'sequenceGeneratorDefinition';
862
            }
863
        }
864
865 5
        if ($this->isMappedSuperclass) {
866
            $serialized[] = 'isMappedSuperclass';
867
        }
868
869 5
        if ($this->isEmbeddedClass) {
870 1
            $serialized[] = 'isEmbeddedClass';
871
        }
872
873 5
        if ($this->containsForeignIdentifier) {
874
            $serialized[] = 'containsForeignIdentifier';
875
        }
876
877 5
        if ($this->isVersioned) {
878
            $serialized[] = 'isVersioned';
879
            $serialized[] = 'versionField';
880
        }
881
882 5
        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...
883
            $serialized[] = 'lifecycleCallbacks';
884
        }
885
886 5
        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...
887 1
            $serialized[] = 'entityListeners';
888
        }
889
890 5
        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...
891 1
            $serialized[] = 'namedQueries';
892
        }
893
894 5
        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...
895
            $serialized[] = 'namedNativeQueries';
896
        }
897
898 5
        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...
899
            $serialized[] = 'sqlResultSetMappings';
900
        }
901
902 5
        if ($this->isReadOnly) {
903 1
            $serialized[] = 'isReadOnly';
904
        }
905
906 5
        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...
907
            $serialized[] = "customGeneratorDefinition";
908
        }
909
910 5
        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...
911
            $serialized[] = 'cache';
912
        }
913
914 5
        return $serialized;
915
    }
916
917
    /**
918
     * Creates a new instance of the mapped class, without invoking the constructor.
919
     *
920
     * @return object
921
     */
922 658
    public function newInstance()
923
    {
924 658
        return $this->instantiator->instantiate($this->name);
925
    }
926
927
    /**
928
     * Restores some state that can not be serialized/unserialized.
929
     *
930
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
931
     *
932
     * @return void
933
     */
934 1969
    public function wakeupReflection($reflService)
935
    {
936
        // Restore ReflectionClass and properties
937 1969
        $this->reflClass    = $reflService->getClass($this->name);
938 1969
        $this->instantiator = $this->instantiator ?: new Instantiator();
939
940 1969
        $parentReflFields = array();
941
942 1969
        foreach ($this->embeddedClasses as $property => $embeddedClass) {
943 20
            if (isset($embeddedClass['declaredField'])) {
944 15
                $parentReflFields[$property] = new ReflectionEmbeddedProperty(
945 15
                    $parentReflFields[$embeddedClass['declaredField']],
946 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...
947 15
                        $this->embeddedClasses[$embeddedClass['declaredField']]['class'],
948 15
                        $embeddedClass['originalField']
949
                    ),
950 15
                    $this->embeddedClasses[$embeddedClass['declaredField']]['class']
951
                );
952
953 15
                continue;
954
            }
955
956 20
            $parentReflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
957 20
            $this->reflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
958
        }
959
960 1969
        foreach ($this->fieldMappings as $field => $mapping) {
961 1966
            if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
962 19
                $this->reflFields[$field] = new ReflectionEmbeddedProperty(
963 19
                    $parentReflFields[$mapping['declaredField']],
964 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...
965 19
                    $mapping['originalClass']
966
                );
967 19
                continue;
968
            }
969
970 1966
            $this->reflFields[$field] = isset($mapping['declared'])
971 479
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
972 1966
                : $reflService->getAccessibleProperty($this->name, $field);
973
        }
974
975 1969
        foreach ($this->associationMappings as $field => $mapping) {
976 1667
            $this->reflFields[$field] = isset($mapping['declared'])
977 398
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
978 1667
                : $reflService->getAccessibleProperty($this->name, $field);
979
        }
980 1969
    }
981
982
    /**
983
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
984
     * metadata of the class with the given name.
985
     *
986
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service.
987
     *
988
     * @return void
989
     */
990 607
    public function initializeReflection($reflService)
991
    {
992 607
        $this->reflClass = $reflService->getClass($this->name);
993 607
        $this->namespace = $reflService->getClassNamespace($this->name);
994
995 607
        if ($this->reflClass) {
996 600
            $this->name = $this->rootEntityName = $this->reflClass->getName();
997
        }
998
999 607
        $this->table['name'] = $this->namingStrategy->classToTableName($this->name);
1000 607
    }
1001
1002
    /**
1003
     * Validates Identifier.
1004
     *
1005
     * @return void
1006
     *
1007
     * @throws MappingException
1008
     */
1009 373
    public function validateIdentifier()
1010
    {
1011 373
        if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
1012 30
            return;
1013
        }
1014
1015
        // Verify & complete identifier mapping
1016 373
        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...
1017 5
            throw MappingException::identifierRequired($this->name);
1018
        }
1019
1020 368
        if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
1021
            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
1022
        }
1023 368
    }
1024
1025
    /**
1026
     * Validates association targets actually exist.
1027
     *
1028
     * @return void
1029
     *
1030
     * @throws MappingException
1031
     */
1032 374
    public function validateAssociations()
1033
    {
1034 374
        foreach ($this->associationMappings as $mapping) {
1035 254
            if ( ! ClassLoader::classExists($mapping['targetEntity']) ) {
1036 254
                throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
1037
            }
1038
        }
1039 373
    }
1040
1041
    /**
1042
     * Validates lifecycle callbacks.
1043
     *
1044
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
1045
     *
1046
     * @return void
1047
     *
1048
     * @throws MappingException
1049
     */
1050 374
    public function validateLifecycleCallbacks($reflService)
1051
    {
1052 374
        foreach ($this->lifecycleCallbacks as $callbacks) {
1053 11
            foreach ($callbacks as $callbackFuncName) {
1054 11
                if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
1055 11
                    throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
1056
                }
1057
            }
1058
        }
1059 373
    }
1060
1061
    /**
1062
     * {@inheritDoc}
1063
     */
1064 532
    public function getReflectionClass()
1065
    {
1066 532
        return $this->reflClass;
1067
    }
1068
1069
    /**
1070
     * @param array $cache
1071
     *
1072
     * @return void
1073
     */
1074 20
    public function enableCache(array $cache)
1075
    {
1076 20
        if ( ! isset($cache['usage'])) {
1077
            $cache['usage'] = self::CACHE_USAGE_READ_ONLY;
1078
        }
1079
1080 20
        if ( ! isset($cache['region'])) {
1081 20
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName));
1082
        }
1083
1084 20
        $this->cache = $cache;
1085 20
    }
1086
1087
    /**
1088
     * @param string $fieldName
1089
     * @param array  $cache
1090
     *
1091
     * @return void
1092
     */
1093 2
    public function enableAssociationCache($fieldName, array $cache)
1094
    {
1095 2
        $this->associationMappings[$fieldName]['cache'] = $this->getAssociationCacheDefaults ($fieldName, $cache);
1096 2
    }
1097
1098
    /**
1099
     * @param string $fieldName
1100
     * @param array  $cache
1101
     *
1102
     * @return array
1103
     */
1104 16
    public function getAssociationCacheDefaults($fieldName, array $cache)
1105
    {
1106 16
        if ( ! isset($cache['usage'])) {
1107 1
            $cache['usage'] = isset($this->cache['usage'])
1108 1
                ? $this->cache['usage']
1109
                : self::CACHE_USAGE_READ_ONLY;
1110
        }
1111
1112 16
        if ( ! isset($cache['region'])) {
1113 16
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName;
1114
        }
1115
1116 16
        return $cache;
1117
    }
1118
1119
    /**
1120
     * Sets the change tracking policy used by this class.
1121
     *
1122
     * @param integer $policy
1123
     *
1124
     * @return void
1125
     */
1126 116
    public function setChangeTrackingPolicy($policy)
1127
    {
1128 116
        $this->changeTrackingPolicy = $policy;
1129 116
    }
1130
1131
    /**
1132
     * Whether the change tracking policy of this class is "deferred explicit".
1133
     *
1134
     * @return boolean
1135
     */
1136 263
    public function isChangeTrackingDeferredExplicit()
1137
    {
1138 263
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
1139
    }
1140
1141
    /**
1142
     * Whether the change tracking policy of this class is "deferred implicit".
1143
     *
1144
     * @return boolean
1145
     */
1146 453
    public function isChangeTrackingDeferredImplicit()
1147
    {
1148 453
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1149
    }
1150
1151
    /**
1152
     * Whether the change tracking policy of this class is "notify".
1153
     *
1154
     * @return boolean
1155
     */
1156 284
    public function isChangeTrackingNotify()
1157
    {
1158 284
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1159
    }
1160
1161
    /**
1162
     * Checks whether a field is part of the identifier/primary key field(s).
1163
     *
1164
     * @param string $fieldName The field name.
1165
     *
1166
     * @return boolean TRUE if the field is part of the table identifier/primary key field(s),
1167
     *                 FALSE otherwise.
1168
     */
1169 1047
    public function isIdentifier($fieldName)
1170
    {
1171 1047
        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...
1172 1
            return false;
1173
        }
1174
1175 1046
        if ( ! $this->isIdentifierComposite) {
1176 1041
            return $fieldName === $this->identifier[0];
1177
        }
1178
1179 93
        return in_array($fieldName, $this->identifier);
1180
    }
1181
1182
    /**
1183
     * Checks if the field is unique.
1184
     *
1185
     * @param string $fieldName The field name.
1186
     *
1187
     * @return boolean TRUE if the field is unique, FALSE otherwise.
1188
     */
1189
    public function isUniqueField($fieldName)
1190
    {
1191
        $mapping = $this->getFieldMapping($fieldName);
1192
1193
        if ($mapping !== false) {
1194
            return isset($mapping['unique']) && $mapping['unique'] == true;
1195
        }
1196
1197
        return false;
1198
    }
1199
1200
    /**
1201
     * Checks if the field is not null.
1202
     *
1203
     * @param string $fieldName The field name.
1204
     *
1205
     * @return boolean TRUE if the field is not null, FALSE otherwise.
1206
     */
1207 1
    public function isNullable($fieldName)
1208
    {
1209 1
        $mapping = $this->getFieldMapping($fieldName);
1210
1211 1
        if ($mapping !== false) {
1212 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1213
        }
1214
1215
        return false;
1216
    }
1217
1218
    /**
1219
     * Gets a column name for a field name.
1220
     * If the column name for the field cannot be found, the given field name
1221
     * is returned.
1222
     *
1223
     * @param string $fieldName The field name.
1224
     *
1225
     * @return string The column name.
1226
     */
1227 16
    public function getColumnName($fieldName)
1228
    {
1229 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...
1230 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...
1231 16
            : $fieldName;
1232
    }
1233
1234
    /**
1235
     * Gets the mapping of a (regular) field that holds some data but not a
1236
     * reference to another object.
1237
     *
1238
     * @param string $fieldName The field name.
1239
     *
1240
     * @return array The field mapping.
1241
     *
1242
     * @throws MappingException
1243
     */
1244 196
    public function getFieldMapping($fieldName)
1245
    {
1246 196
        if ( ! isset($this->fieldMappings[$fieldName])) {
1247 1
            throw MappingException::mappingNotFound($this->name, $fieldName);
1248
        }
1249
1250 195
        return $this->fieldMappings[$fieldName];
1251
    }
1252
1253
    /**
1254
     * Gets the mapping of an association.
1255
     *
1256
     * @see ClassMetadataInfo::$associationMappings
1257
     *
1258
     * @param string $fieldName The field name that represents the association in
1259
     *                          the object model.
1260
     *
1261
     * @return array The mapping.
1262
     *
1263
     * @throws MappingException
1264
     */
1265 477
    public function getAssociationMapping($fieldName)
1266
    {
1267 477
        if ( ! isset($this->associationMappings[$fieldName])) {
1268
            throw MappingException::mappingNotFound($this->name, $fieldName);
1269
        }
1270
1271 477
        return $this->associationMappings[$fieldName];
1272
    }
1273
1274
    /**
1275
     * Gets all association mappings of the class.
1276
     *
1277
     * @return array
1278
     */
1279
    public function getAssociationMappings()
1280
    {
1281
        return $this->associationMappings;
1282
    }
1283
1284
    /**
1285
     * Gets the field name for a column name.
1286
     * If no field name can be found the column name is returned.
1287
     *
1288
     * @param string $columnName The column name.
1289
     *
1290
     * @return string The column alias.
1291
     */
1292 227
    public function getFieldName($columnName)
1293
    {
1294 227
        return isset($this->fieldNames[$columnName])
1295 227
            ? $this->fieldNames[$columnName]
1296 227
            : $columnName;
1297
    }
1298
1299
    /**
1300
     * Gets the named query.
1301
     *
1302
     * @see ClassMetadataInfo::$namedQueries
1303
     *
1304
     * @param string $queryName The query name.
1305
     *
1306
     * @return string
1307
     *
1308
     * @throws MappingException
1309
     */
1310 3
    public function getNamedQuery($queryName)
1311
    {
1312 3
        if ( ! isset($this->namedQueries[$queryName])) {
1313 1
            throw MappingException::queryNotFound($this->name, $queryName);
1314
        }
1315
1316 2
        return $this->namedQueries[$queryName]['dql'];
1317
    }
1318
1319
    /**
1320
     * Gets all named queries of the class.
1321
     *
1322
     * @return array
1323
     */
1324 7
    public function getNamedQueries()
1325
    {
1326 7
        return $this->namedQueries;
1327
    }
1328
1329
    /**
1330
     * Gets the named native query.
1331
     *
1332
     * @see ClassMetadataInfo::$namedNativeQueries
1333
     *
1334
     * @param string $queryName The query name.
1335
     *
1336
     * @return array
1337
     *
1338
     * @throws MappingException
1339
     */
1340 17
    public function getNamedNativeQuery($queryName)
1341
    {
1342 17
        if ( ! isset($this->namedNativeQueries[$queryName])) {
1343
            throw MappingException::queryNotFound($this->name, $queryName);
1344
        }
1345
1346 17
        return $this->namedNativeQueries[$queryName];
1347
    }
1348
1349
    /**
1350
     * Gets all named native queries of the class.
1351
     *
1352
     * @return array
1353
     */
1354 2
    public function getNamedNativeQueries()
1355
    {
1356 2
        return $this->namedNativeQueries;
1357
    }
1358
1359
    /**
1360
     * Gets the result set mapping.
1361
     *
1362
     * @see ClassMetadataInfo::$sqlResultSetMappings
1363
     *
1364
     * @param string $name The result set mapping name.
1365
     *
1366
     * @return array
1367
     *
1368
     * @throws MappingException
1369
     */
1370 21
    public function getSqlResultSetMapping($name)
1371
    {
1372 21
        if ( ! isset($this->sqlResultSetMappings[$name])) {
1373
            throw MappingException::resultMappingNotFound($this->name, $name);
1374
        }
1375
1376 21
        return $this->sqlResultSetMappings[$name];
1377
    }
1378
1379
    /**
1380
     * Gets all sql result set mappings of the class.
1381
     *
1382
     * @return array
1383
     */
1384 8
    public function getSqlResultSetMappings()
1385
    {
1386 8
        return $this->sqlResultSetMappings;
1387
    }
1388
1389
    /**
1390
     * Validates & completes the given field mapping.
1391
     *
1392
     * @param array $mapping The field mapping to validate & complete.
1393
     *
1394
     * @return array The validated and completed field mapping.
1395
     *
1396
     * @throws MappingException
1397
     */
1398 491
    protected function _validateAndCompleteFieldMapping(array &$mapping)
1399
    {
1400
        // Check mandatory fields
1401 491
        if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
1402 1
            throw MappingException::missingFieldName($this->name);
1403
        }
1404
1405 490
        if ( ! isset($mapping['type'])) {
1406
            // Default to string
1407 55
            $mapping['type'] = 'string';
1408
        }
1409
1410
        // Complete fieldName and columnName mapping
1411 490
        if ( ! isset($mapping['columnName'])) {
1412 401
            $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name);
1413
        }
1414
1415 490
        if ($mapping['columnName'][0] === '`') {
1416 11
            $mapping['columnName']  = trim($mapping['columnName'], '`');
1417 11
            $mapping['quoted']      = true;
1418
        }
1419
1420 490
        $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...
1421
1422 490
        if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn != null && $this->discriminatorColumn['name'] == $mapping['columnName'])) {
1423 2
            throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);
1424
        }
1425
1426 489
        $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
1427
1428
        // Complete id mapping
1429 489
        if (isset($mapping['id']) && $mapping['id'] === true) {
1430 452
            if ($this->versionField == $mapping['fieldName']) {
1431
                throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
1432
            }
1433
1434 452
            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1435 452
                $this->identifier[] = $mapping['fieldName'];
1436
            }
1437
1438
            // Check for composite key
1439 452
            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1440 17
                $this->isIdentifierComposite = true;
1441
            }
1442
        }
1443
1444 489
        if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
1445 5
            if (isset($mapping['id']) && $mapping['id'] === true) {
1446
                 throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
1447
            }
1448
1449 5
            $mapping['requireSQLConversion'] = true;
1450
        }
1451 489
    }
1452
1453
    /**
1454
     * Validates & completes the basic mapping information that is common to all
1455
     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
1456
     *
1457
     * @param array $mapping The mapping.
1458
     *
1459
     * @return array The updated mapping.
1460
     *
1461
     * @throws MappingException If something is wrong with the mapping.
1462
     */
1463 337
    protected function _validateAndCompleteAssociationMapping(array $mapping)
1464
    {
1465 337
        if ( ! isset($mapping['mappedBy'])) {
1466 325
            $mapping['mappedBy'] = null;
1467
        }
1468
1469 337
        if ( ! isset($mapping['inversedBy'])) {
1470 310
            $mapping['inversedBy'] = null;
1471
        }
1472
1473 337
        $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
1474
1475
        // unset optional indexBy attribute if its empty
1476 337
        if ( ! isset($mapping['indexBy']) || !$mapping['indexBy']) {
1477 334
            unset($mapping['indexBy']);
1478
        }
1479
1480
        // If targetEntity is unqualified, assume it is in the same namespace as
1481
        // the sourceEntity.
1482 337
        $mapping['sourceEntity'] = $this->name;
1483
1484 337
        if (isset($mapping['targetEntity'])) {
1485 337
            $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']);
1486 337
            $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
1487
        }
1488
1489 337
        if (isset($mapping['persister'])) {
1490
            $mapping['persister'] = $this->fullyQualifiedClassName($mapping['persister']);
1491
            $mapping['persister'] = ltrim($mapping['persister'], '\\');
1492
        }
1493
1494 337
        if (($mapping['type'] & self::MANY_TO_ONE) > 0 && isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
1495 1
            throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
1496
        }
1497
1498
        // Complete id mapping
1499 336
        if (isset($mapping['id']) && $mapping['id'] === true) {
1500 53
            if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
1501 1
                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
1502
            }
1503
1504 52
            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1505 52
                if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) >= 2) {
1506
                    throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
1507
                        $mapping['targetEntity'], $this->name, $mapping['fieldName']
1508
                    );
1509
                }
1510
1511 52
                $this->identifier[] = $mapping['fieldName'];
1512 52
                $this->containsForeignIdentifier = true;
1513
            }
1514
1515
            // Check for composite key
1516 52
            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1517 24
                $this->isIdentifierComposite = true;
1518
            }
1519
1520 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...
1521 3
                throw CacheException::nonCacheableEntityAssociation($this->name, $mapping['fieldName']);
1522
            }
1523
        }
1524
1525
        // Mandatory attributes for both sides
1526
        // Mandatory: fieldName, targetEntity
1527 332
        if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
1528
            throw MappingException::missingFieldName($this->name);
1529
        }
1530
1531 332
        if ( ! isset($mapping['targetEntity'])) {
1532
            throw MappingException::missingTargetEntity($mapping['fieldName']);
1533
        }
1534
1535
        // Mandatory and optional attributes for either side
1536 332
        if ( ! $mapping['mappedBy']) {
1537 320
            if (isset($mapping['joinTable']) && $mapping['joinTable']) {
1538 115
                if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') {
1539 4
                    $mapping['joinTable']['name']   = trim($mapping['joinTable']['name'], '`');
1540 320
                    $mapping['joinTable']['quoted'] = true;
1541
                }
1542
            }
1543
        } else {
1544 178
            $mapping['isOwningSide'] = false;
1545
        }
1546
1547 332
        if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
1548 3
            throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']);
1549
        }
1550
1551
        // Fetch mode. Default fetch mode to LAZY, if not set.
1552 329
        if ( ! isset($mapping['fetch'])) {
1553 95
            $mapping['fetch'] = self::FETCH_LAZY;
1554
        }
1555
1556
        // Cascades
1557 329
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
1558
1559 329
        if (in_array('all', $cascades)) {
1560 30
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1561
        }
1562
1563 329
        if (count($cascades) !== count(array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach')))) {
1564 1
            throw MappingException::invalidCascadeOption(
1565 1
                array_diff($cascades, array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach'))),
1566 1
                $this->name,
1567 1
                $mapping['fieldName']
1568
            );
1569
        }
1570
1571 328
        $mapping['cascade'] = $cascades;
1572 328
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1573 328
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1574 328
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1575 328
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1576 328
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1577
1578 328
        return $mapping;
1579
    }
1580
1581
    /**
1582
     * Validates & completes a one-to-one association mapping.
1583
     *
1584
     * @param array $mapping The mapping to validate & complete.
1585
     *
1586
     * @return array The validated & completed mapping.
1587
     *
1588
     * @throws RuntimeException
1589
     * @throws MappingException
1590
     */
1591 288
    protected function _validateAndCompleteOneToOneMapping(array $mapping)
1592
    {
1593 288
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1594
1595 282
        if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
1596 205
            $mapping['isOwningSide'] = true;
1597
        }
1598
1599 282
        if ($mapping['isOwningSide']) {
1600 269
            if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
1601
                // Apply default join column
1602 85
                $mapping['joinColumns'] = array(
1603
                    array(
1604 85
                        '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...
1605 85
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName()
1606
                    )
1607
                );
1608
            }
1609
1610 269
            $uniqueConstraintColumns = array();
1611
1612 269
            foreach ($mapping['joinColumns'] as &$joinColumn) {
1613 269
                if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
1614 147
                    if (count($mapping['joinColumns']) == 1) {
1615 145
                        if ( ! isset($mapping['id']) || ! $mapping['id']) {
1616 145
                            $joinColumn['unique'] = true;
1617
                        }
1618
                    } else {
1619 2
                        $uniqueConstraintColumns[] = $joinColumn['name'];
1620
                    }
1621
                }
1622
1623 269
                if (empty($joinColumn['name'])) {
1624 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...
1625
                }
1626
1627 269
                if (empty($joinColumn['referencedColumnName'])) {
1628 5
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1629
                }
1630
1631 269
                if ($joinColumn['name'][0] === '`') {
1632 7
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1633 7
                    $joinColumn['quoted'] = true;
1634
                }
1635
1636 269
                if ($joinColumn['referencedColumnName'][0] === '`') {
1637 4
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1638 4
                    $joinColumn['quoted']               = true;
1639
                }
1640
1641 269
                $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1642 269
                $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
1643
                    ? $joinColumn['fieldName']
1644 269
                    : $joinColumn['name'];
1645
            }
1646
1647 269
            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...
1648 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...
1649
                    throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
1650
                }
1651
1652 2
                $this->table['uniqueConstraints'][$mapping['fieldName'] . "_uniq"] = array(
1653 2
                    'columns' => $uniqueConstraintColumns
1654
                );
1655
            }
1656
1657 269
            $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
1658
        }
1659
1660 282
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1661 282
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1662
1663 282
        if ($mapping['orphanRemoval']) {
1664 21
            unset($mapping['unique']);
1665
        }
1666
1667 282
        if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
1668 2
            throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']);
1669
        }
1670
1671 280
        return $mapping;
1672
    }
1673
1674
    /**
1675
     * Validates & completes a one-to-many association mapping.
1676
     *
1677
     * @param array $mapping The mapping to validate and complete.
1678
     *
1679
     * @return array The validated and completed mapping.
1680
     *
1681
     * @throws MappingException
1682
     * @throws InvalidArgumentException
1683
     */
1684 127
    protected function _validateAndCompleteOneToManyMapping(array $mapping)
1685
    {
1686 127
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1687
1688
        // OneToMany-side MUST be inverse (must have mappedBy)
1689 126
        if ( ! isset($mapping['mappedBy'])) {
1690
            throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
1691
        }
1692
1693 126
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1694 126
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1695
1696 126
        if (isset($mapping['orderBy'])) {
1697 37
            if ( ! is_array($mapping['orderBy'])) {
1698
                throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
1699
            }
1700
        }
1701
1702 126
        return $mapping;
1703
    }
1704
1705
    /**
1706
     * Validates & completes a many-to-many association mapping.
1707
     *
1708
     * @param array $mapping The mapping to validate & complete.
1709
     *
1710
     * @return array The validated & completed mapping.
1711
     *
1712
     * @throws \InvalidArgumentException
1713
     */
1714 143
    protected function _validateAndCompleteManyToManyMapping(array $mapping)
1715
    {
1716 143
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1717
1718 141
        if ($mapping['isOwningSide']) {
1719
            // owning side MUST have a join table
1720 125
            if ( ! isset($mapping['joinTable']['name'])) {
1721 21
                $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']);
1722
            }
1723
1724 125
            $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity']
1725 125
                && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns'])));
1726
1727 125
            if ( ! isset($mapping['joinTable']['joinColumns'])) {
1728 20
                $mapping['joinTable']['joinColumns'] = array(
1729
                    array(
1730 20
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null),
1731 20
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1732 20
                        'onDelete' => 'CASCADE'
1733
                    )
1734
                );
1735
            }
1736
1737 125
            if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
1738 21
                $mapping['joinTable']['inverseJoinColumns'] = array(
1739
                    array(
1740 21
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null),
1741 21
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1742 21
                        'onDelete' => 'CASCADE'
1743
                    )
1744
                );
1745
            }
1746
1747 125
            $mapping['joinTableColumns'] = array();
1748
1749 125
            foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
1750 125
                if (empty($joinColumn['name'])) {
1751 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...
1752
                }
1753
1754 125
                if (empty($joinColumn['referencedColumnName'])) {
1755 6
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1756
                }
1757
1758 125
                if ($joinColumn['name'][0] === '`') {
1759 3
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1760 3
                    $joinColumn['quoted'] = true;
1761
                }
1762
1763 125
                if ($joinColumn['referencedColumnName'][0] === '`') {
1764 3
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1765 3
                    $joinColumn['quoted']               = true;
1766
                }
1767
1768 125
                if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
1769 29
                    $mapping['isOnDeleteCascade'] = true;
1770
                }
1771
1772 125
                $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1773 125
                $mapping['joinTableColumns'][] = $joinColumn['name'];
1774
            }
1775
1776 125
            foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
1777 125
                if (empty($inverseJoinColumn['name'])) {
1778 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...
1779
                }
1780
1781 125
                if (empty($inverseJoinColumn['referencedColumnName'])) {
1782 6
                    $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1783
                }
1784
1785 125
                if ($inverseJoinColumn['name'][0] === '`') {
1786 3
                    $inverseJoinColumn['name']   = trim($inverseJoinColumn['name'], '`');
1787 3
                    $inverseJoinColumn['quoted'] = true;
1788
                }
1789
1790 125
                if ($inverseJoinColumn['referencedColumnName'][0] === '`') {
1791 3
                    $inverseJoinColumn['referencedColumnName']  = trim($inverseJoinColumn['referencedColumnName'], '`');
1792 3
                    $inverseJoinColumn['quoted']                = true;
1793
                }
1794
1795 125
                if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
1796 25
                    $mapping['isOnDeleteCascade'] = true;
1797
                }
1798
1799 125
                $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
1800 125
                $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
1801
            }
1802
        }
1803
1804 141
        $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1805
1806 141
        if (isset($mapping['orderBy'])) {
1807 3
            if ( ! is_array($mapping['orderBy'])) {
1808
                throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
1809
            }
1810
        }
1811
1812 141
        return $mapping;
1813
    }
1814
1815
    /**
1816
     * {@inheritDoc}
1817
     */
1818 572
    public function getIdentifierFieldNames()
1819
    {
1820 572
        return $this->identifier;
1821
    }
1822
1823
    /**
1824
     * Gets the name of the single id field. Note that this only works on
1825
     * entity classes that have a single-field pk.
1826
     *
1827
     * @return string
1828
     *
1829
     * @throws MappingException If the class has a composite primary key.
1830
     */
1831 382
    public function getSingleIdentifierFieldName()
1832
    {
1833 382
        if ($this->isIdentifierComposite) {
1834 1
            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
1835
        }
1836
1837 381
        return $this->identifier[0];
1838
    }
1839
1840
    /**
1841
     * Gets the column name of the single id column. Note that this only works on
1842
     * entity classes that have a single-field pk.
1843
     *
1844
     * @return string
1845
     *
1846
     * @throws MappingException If the class has a composite primary key.
1847
     */
1848 3
    public function getSingleIdentifierColumnName()
1849
    {
1850 3
        return $this->getColumnName($this->getSingleIdentifierFieldName());
1851
    }
1852
1853
    /**
1854
     * INTERNAL:
1855
     * Sets the mapped identifier/primary key fields of this class.
1856
     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1857
     *
1858
     * @param array $identifier
1859
     *
1860
     * @return void
1861
     */
1862 101
    public function setIdentifier(array $identifier)
1863
    {
1864 101
        $this->identifier = $identifier;
1865 101
        $this->isIdentifierComposite = (count($this->identifier) > 1);
1866 101
    }
1867
1868
    /**
1869
     * {@inheritDoc}
1870
     */
1871 60
    public function getIdentifier()
1872
    {
1873 60
        return $this->identifier;
1874
    }
1875
1876
    /**
1877
     * {@inheritDoc}
1878
     */
1879 287
    public function hasField($fieldName)
1880
    {
1881 287
        return isset($this->fieldMappings[$fieldName]);
1882
    }
1883
1884
    /**
1885
     * Gets an array containing all the column names.
1886
     *
1887
     * @param array|null $fieldNames
1888
     *
1889
     * @return array
1890
     */
1891 36
    public function getColumnNames(array $fieldNames = null)
1892
    {
1893 36
        if (null === $fieldNames) {
1894 35
            return array_keys($this->fieldNames);
1895
        }
1896
1897 1
        return array_values(array_map([$this, 'getColumnName'], $fieldNames));
1898
    }
1899
1900
    /**
1901
     * Returns an array with all the identifier column names.
1902
     *
1903
     * @return array
1904
     */
1905 319
    public function getIdentifierColumnNames()
1906
    {
1907 319
        $columnNames = array();
1908
1909 319
        foreach ($this->identifier as $idProperty) {
1910 319
            if (isset($this->fieldMappings[$idProperty])) {
1911 315
                $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
1912
1913 315
                continue;
1914
            }
1915
1916
            // Association defined as Id field
1917 22
            $joinColumns      = $this->associationMappings[$idProperty]['joinColumns'];
1918
            $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
1919
1920 22
            $columnNames = array_merge($columnNames, $assocColumnNames);
1921
        }
1922
1923 319
        return $columnNames;
1924
    }
1925
1926
    /**
1927
     * Sets the type of Id generator to use for the mapped class.
1928
     *
1929
     * @param int $generatorType
1930
     *
1931
     * @return void
1932
     */
1933 419
    public function setIdGeneratorType($generatorType)
1934
    {
1935 419
        $this->generatorType = $generatorType;
1936 419
    }
1937
1938
    /**
1939
     * Checks whether the mapped class uses an Id generator.
1940
     *
1941
     * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
1942
     */
1943 368
    public function usesIdGenerator()
1944
    {
1945 368
        return $this->generatorType != self::GENERATOR_TYPE_NONE;
1946
    }
1947
1948
    /**
1949
     * @return boolean
1950
     */
1951 1289
    public function isInheritanceTypeNone()
1952
    {
1953 1289
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1954
    }
1955
1956
    /**
1957
     * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
1958
     *
1959
     * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
1960
     *                 FALSE otherwise.
1961
     */
1962 1018
    public function isInheritanceTypeJoined()
1963
    {
1964 1018
        return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
1965
    }
1966
1967
    /**
1968
     * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
1969
     *
1970
     * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
1971
     *                 FALSE otherwise.
1972
     */
1973 1174
    public function isInheritanceTypeSingleTable()
1974
    {
1975 1174
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
1976
    }
1977
1978
    /**
1979
     * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
1980
     *
1981
     * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
1982
     *                 FALSE otherwise.
1983
     */
1984 240
    public function isInheritanceTypeTablePerClass()
1985
    {
1986 240
        return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
1987
    }
1988
1989
    /**
1990
     * Checks whether the class uses an identity column for the Id generation.
1991
     *
1992
     * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
1993
     */
1994 1040
    public function isIdGeneratorIdentity()
1995
    {
1996 1040
        return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
1997
    }
1998
1999
    /**
2000
     * Checks whether the class uses a sequence for id generation.
2001
     *
2002
     * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
2003
     */
2004 294
    public function isIdGeneratorSequence()
2005
    {
2006 294
        return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
2007
    }
2008
2009
    /**
2010
     * Checks whether the class uses a table for id generation.
2011
     *
2012
     * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
2013
     */
2014 78
    public function isIdGeneratorTable()
2015
    {
2016 78
        return $this->generatorType == self::GENERATOR_TYPE_TABLE;
2017
    }
2018
2019
    /**
2020
     * Checks whether the class has a natural identifier/pk (which means it does
2021
     * not use any Id generator.
2022
     *
2023
     * @return boolean
2024
     */
2025 72
    public function isIdentifierNatural()
2026
    {
2027 72
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
2028
    }
2029
2030
    /**
2031
     * Checks whether the class use a UUID for id generation.
2032
     *
2033
     * @return boolean
2034
     */
2035
    public function isIdentifierUuid()
2036
    {
2037
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
2038
    }
2039
2040
    /**
2041
     * Gets the type of a field.
2042
     *
2043
     * @param string $fieldName
2044
     *
2045
     * @return \Doctrine\DBAL\Types\Type|string|null
2046
     *
2047
     * @todo 3.0 Remove this. PersisterHelper should fix it somehow
2048
     */
2049 37
    public function getTypeOfField($fieldName)
2050
    {
2051 37
        return isset($this->fieldMappings[$fieldName])
2052 37
            ? $this->fieldMappings[$fieldName]['type']
2053 37
            : null;
2054
    }
2055
2056
    /**
2057
     * Gets the type of a column.
2058
     *
2059
     * @param string $columnName
2060
     *
2061
     * @return \Doctrine\DBAL\Types\Type|string|null
2062
     *
2063
     * @deprecated 3.0 remove this. this method is bogous and unreliable, since it cannot resolve the type of a column
2064
     *             that is derived by a referenced field on a different entity.
2065
     */
2066
    public function getTypeOfColumn($columnName)
2067
    {
2068
        return $this->getTypeOfField($this->getFieldName($columnName));
2069
    }
2070
2071
    /**
2072
     * Gets the name of the primary table.
2073
     *
2074
     * @return string
2075
     */
2076 1440
    public function getTableName()
2077
    {
2078 1440
        return $this->table['name'];
2079
    }
2080
2081
    /**
2082
     * Gets primary table's schema name.
2083
     *
2084
     * @return string|null
2085
     */
2086 13
    public function getSchemaName()
2087
    {
2088 13
        return isset($this->table['schema']) ? $this->table['schema'] : null;
2089
    }
2090
2091
    /**
2092
     * Gets the table name to use for temporary identifier tables of this class.
2093
     *
2094
     * @return string
2095
     */
2096 7
    public function getTemporaryIdTableName()
2097
    {
2098
        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
2099 7
        return str_replace('.', '_', $this->getTableName() . '_id_tmp');
2100
    }
2101
2102
    /**
2103
     * Sets the mapped subclasses of this class.
2104
     *
2105
     * @param array $subclasses The names of all mapped subclasses.
2106
     *
2107
     * @return void
2108
     */
2109 2
    public function setSubclasses(array $subclasses)
2110
    {
2111 2
        foreach ($subclasses as $subclass) {
2112 2
            $this->subClasses[] = $this->fullyQualifiedClassName($subclass);
2113
        }
2114 2
    }
2115
2116
    /**
2117
     * Sets the parent class names.
2118
     * Assumes that the class names in the passed array are in the order:
2119
     * directParent -> directParentParent -> directParentParentParent ... -> root.
2120
     *
2121
     * @param array $classNames
2122
     *
2123
     * @return void
2124
     */
2125 382
    public function setParentClasses(array $classNames)
2126
    {
2127 382
        $this->parentClasses = $classNames;
2128
2129 382
        if (count($classNames) > 0) {
2130 75
            $this->rootEntityName = array_pop($classNames);
2131
        }
2132 382
    }
2133
2134
    /**
2135
     * Sets the inheritance type used by the class and its subclasses.
2136
     *
2137
     * @param integer $type
2138
     *
2139
     * @return void
2140
     *
2141
     * @throws MappingException
2142
     */
2143 133
    public function setInheritanceType($type)
2144
    {
2145 133
        if ( ! $this->_isInheritanceType($type)) {
2146
            throw MappingException::invalidInheritanceType($this->name, $type);
2147
        }
2148
2149 133
        $this->inheritanceType = $type;
2150 133
    }
2151
2152
    /**
2153
     * Sets the association to override association mapping of property for an entity relationship.
2154
     *
2155
     * @param string $fieldName
2156
     * @param array  $overrideMapping
2157
     *
2158
     * @return void
2159
     *
2160
     * @throws MappingException
2161
     */
2162 17
    public function setAssociationOverride($fieldName, array $overrideMapping)
2163
    {
2164 17
        if ( ! isset($this->associationMappings[$fieldName])) {
2165 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2166
        }
2167
2168 16
        $mapping = $this->associationMappings[$fieldName];
2169
2170 16
        if (isset($overrideMapping['joinColumns'])) {
2171 11
            $mapping['joinColumns'] = $overrideMapping['joinColumns'];
2172
        }
2173
2174 16
        if (isset($overrideMapping['inversedBy'])) {
2175 5
            $mapping['inversedBy'] = $overrideMapping['inversedBy'];
2176
        }
2177
2178 16
        if (isset($overrideMapping['joinTable'])) {
2179 10
            $mapping['joinTable'] = $overrideMapping['joinTable'];
2180
        }
2181
2182 16
        $mapping['joinColumnFieldNames']        = null;
2183 16
        $mapping['joinTableColumns']            = null;
2184 16
        $mapping['sourceToTargetKeyColumns']    = null;
2185 16
        $mapping['relationToSourceKeyColumns']  = null;
2186 16
        $mapping['relationToTargetKeyColumns']  = null;
2187
2188 16
        switch ($mapping['type']) {
2189 16
            case self::ONE_TO_ONE:
2190 1
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2191 1
                break;
2192 15
            case self::ONE_TO_MANY:
2193
                $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2194
                break;
2195 15
            case self::MANY_TO_ONE:
2196 10
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2197 10
                break;
2198 15
            case self::MANY_TO_MANY:
2199 15
                $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2200 15
                break;
2201
        }
2202
2203 16
        $this->associationMappings[$fieldName] = $mapping;
2204 16
    }
2205
2206
    /**
2207
     * Sets the override for a mapped field.
2208
     *
2209
     * @param string $fieldName
2210
     * @param array  $overrideMapping
2211
     *
2212
     * @return void
2213
     *
2214
     * @throws MappingException
2215
     */
2216 13
    public function setAttributeOverride($fieldName, array $overrideMapping)
2217
    {
2218 13
        if ( ! isset($this->fieldMappings[$fieldName])) {
2219 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2220
        }
2221
2222 12
        $mapping = $this->fieldMappings[$fieldName];
2223
2224 12
        if (isset($mapping['id'])) {
2225 10
            $overrideMapping['id'] = $mapping['id'];
2226
        }
2227
2228 12
        if ( ! isset($overrideMapping['type']) || $overrideMapping['type'] === null) {
2229 6
            $overrideMapping['type'] = $mapping['type'];
2230
        }
2231
2232 12
        if ( ! isset($overrideMapping['fieldName']) || $overrideMapping['fieldName'] === null) {
2233 5
            $overrideMapping['fieldName'] = $mapping['fieldName'];
2234
        }
2235
2236 12
        if ($overrideMapping['type'] !== $mapping['type']) {
2237 1
            throw MappingException::invalidOverrideFieldType($this->name, $fieldName);
2238
        }
2239
2240 11
        unset($this->fieldMappings[$fieldName]);
2241 11
        unset($this->fieldNames[$mapping['columnName']]);
2242 11
        unset($this->columnNames[$mapping['fieldName']]);
2243
2244 11
        $this->_validateAndCompleteFieldMapping($overrideMapping);
2245
2246 11
        $this->fieldMappings[$fieldName] = $overrideMapping;
2247 11
    }
2248
2249
    /**
2250
     * Checks whether a mapped field is inherited from an entity superclass.
2251
     *
2252
     * @param string $fieldName
2253
     *
2254
     * @return bool TRUE if the field is inherited, FALSE otherwise.
2255
     */
2256 340
    public function isInheritedField($fieldName)
2257
    {
2258 340
        return isset($this->fieldMappings[$fieldName]['inherited']);
2259
    }
2260
2261
    /**
2262
     * Checks if this entity is the root in any entity-inheritance-hierarchy.
2263
     *
2264
     * @return bool
2265
     */
2266 381
    public function isRootEntity()
2267
    {
2268 381
        return $this->name == $this->rootEntityName;
2269
    }
2270
2271
    /**
2272
     * Checks whether a mapped association field is inherited from a superclass.
2273
     *
2274
     * @param string $fieldName
2275
     *
2276
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
2277
     */
2278 319
    public function isInheritedAssociation($fieldName)
2279
    {
2280 319
        return isset($this->associationMappings[$fieldName]['inherited']);
2281
    }
2282
2283 319
    public function isInheritedEmbeddedClass($fieldName)
2284
    {
2285 319
        return isset($this->embeddedClasses[$fieldName]['inherited']);
2286
    }
2287
2288
    /**
2289
     * Sets the name of the primary table the class is mapped to.
2290
     *
2291
     * @param string $tableName The table name.
2292
     *
2293
     * @return void
2294
     *
2295
     * @deprecated Use {@link setPrimaryTable}.
2296
     */
2297
    public function setTableName($tableName)
2298
    {
2299
        $this->table['name'] = $tableName;
2300
    }
2301
2302
    /**
2303
     * Sets the primary table definition. The provided array supports the
2304
     * following structure:
2305
     *
2306
     * name => <tableName> (optional, defaults to class name)
2307
     * indexes => array of indexes (optional)
2308
     * uniqueConstraints => array of constraints (optional)
2309
     *
2310
     * If a key is omitted, the current value is kept.
2311
     *
2312
     * @param array $table The table description.
2313
     *
2314
     * @return void
2315
     */
2316 301
    public function setPrimaryTable(array $table)
2317
    {
2318 301
        if (isset($table['name'])) {
2319
            // Split schema and table name from a table name like "myschema.mytable"
2320 249
            if (strpos($table['name'], '.') !== false) {
2321 9
                list($this->table['schema'], $table['name']) = explode('.', $table['name'], 2);
2322
            }
2323
2324 249
            if ($table['name'][0] === '`') {
2325 14
                $table['name']          = trim($table['name'], '`');
2326 14
                $this->table['quoted']  = true;
2327
            }
2328
2329 249
            $this->table['name'] = $table['name'];
2330
        }
2331
2332 301
        if (isset($table['schema'])) {
2333 6
            $this->table['schema'] = $table['schema'];
2334
        }
2335
2336 301
        if (isset($table['indexes'])) {
2337 14
            $this->table['indexes'] = $table['indexes'];
2338
        }
2339
2340 301
        if (isset($table['uniqueConstraints'])) {
2341 7
            $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
2342
        }
2343
2344 301
        if (isset($table['options'])) {
2345 9
            $this->table['options'] = $table['options'];
2346
        }
2347 301
    }
2348
2349
    /**
2350
     * Checks whether the given type identifies an inheritance type.
2351
     *
2352
     * @param integer $type
2353
     *
2354
     * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
2355
     */
2356 133
    private function _isInheritanceType($type)
2357
    {
2358 133
        return $type == self::INHERITANCE_TYPE_NONE ||
2359 92
                $type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
2360 49
                $type == self::INHERITANCE_TYPE_JOINED ||
2361 133
                $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
2362
    }
2363
2364
    /**
2365
     * Adds a mapped field to the class.
2366
     *
2367
     * @param array $mapping The field mapping.
2368
     *
2369
     * @return void
2370
     *
2371
     * @throws MappingException
2372
     */
2373 491
    public function mapField(array $mapping)
2374
    {
2375 491
        $this->_validateAndCompleteFieldMapping($mapping);
2376 489
        $this->assertFieldNotMapped($mapping['fieldName']);
2377
2378 488
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
2379 488
    }
2380
2381
    /**
2382
     * INTERNAL:
2383
     * Adds an association mapping without completing/validating it.
2384
     * This is mainly used to add inherited association mappings to derived classes.
2385
     *
2386
     * @param array $mapping
2387
     *
2388
     * @return void
2389
     *
2390
     * @throws MappingException
2391
     */
2392 46
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
2393
    {
2394 46
        if (isset($this->associationMappings[$mapping['fieldName']])) {
2395 1
            throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
2396
        }
2397 46
        $this->associationMappings[$mapping['fieldName']] = $mapping;
2398 46
    }
2399
2400
    /**
2401
     * INTERNAL:
2402
     * Adds a field mapping without completing/validating it.
2403
     * This is mainly used to add inherited field mappings to derived classes.
2404
     *
2405
     * @param array $fieldMapping
2406
     *
2407
     * @return void
2408
     */
2409 100
    public function addInheritedFieldMapping(array $fieldMapping)
2410
    {
2411 100
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
2412 100
        $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...
2413 100
        $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
2414 100
    }
2415
2416
    /**
2417
     * INTERNAL:
2418
     * Adds a named query to this class.
2419
     *
2420
     * @param array $queryMapping
2421
     *
2422
     * @return void
2423
     *
2424
     * @throws MappingException
2425
     */
2426 28
    public function addNamedQuery(array $queryMapping)
2427
    {
2428 28
        if (!isset($queryMapping['name'])) {
2429 2
            throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2430
        }
2431
2432 26
        if (isset($this->namedQueries[$queryMapping['name']])) {
2433 1
            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2434
        }
2435
2436 26
        if (!isset($queryMapping['query'])) {
2437
            throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2438
        }
2439
2440 26
        $name   = $queryMapping['name'];
2441 26
        $query  = $queryMapping['query'];
2442 26
        $dql    = str_replace('__CLASS__', $this->name, $query);
2443
2444 26
        $this->namedQueries[$name] = array(
2445 26
            'name'  => $name,
2446 26
            'query' => $query,
2447 26
            'dql'   => $dql,
2448
        );
2449 26
    }
2450
2451
    /**
2452
     * INTERNAL:
2453
     * Adds a named native query to this class.
2454
     *
2455
     * @param array $queryMapping
2456
     *
2457
     * @return void
2458
     *
2459
     * @throws MappingException
2460
     */
2461 39
    public function addNamedNativeQuery(array $queryMapping)
2462
    {
2463 39
        if (!isset($queryMapping['name'])) {
2464
            throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2465
        }
2466
2467 39
        if (isset($this->namedNativeQueries[$queryMapping['name']])) {
2468 1
            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2469
        }
2470
2471 39
        if (!isset($queryMapping['query'])) {
2472
            throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2473
        }
2474
2475 39
        if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) {
2476
            throw MappingException::missingQueryMapping($this->name, $queryMapping['name']);
2477
        }
2478
2479 39
        $queryMapping['isSelfClass'] = false;
2480
2481 39
        if (isset($queryMapping['resultClass'])) {
2482 37
            if ($queryMapping['resultClass'] === '__CLASS__') {
2483
2484 11
                $queryMapping['isSelfClass'] = true;
2485 11
                $queryMapping['resultClass'] = $this->name;
2486
            }
2487
2488 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...
2489 37
            $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\');
2490
        }
2491
2492 39
        $this->namedNativeQueries[$queryMapping['name']] = $queryMapping;
2493 39
    }
2494
2495
    /**
2496
     * INTERNAL:
2497
     * Adds a sql result set mapping to this class.
2498
     *
2499
     * @param array $resultMapping
2500
     *
2501
     * @return void
2502
     *
2503
     * @throws MappingException
2504
     */
2505 39
    public function addSqlResultSetMapping(array $resultMapping)
2506
    {
2507 39
        if (!isset($resultMapping['name'])) {
2508
            throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name);
2509
        }
2510
2511 39
        if (isset($this->sqlResultSetMappings[$resultMapping['name']])) {
2512 1
            throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']);
2513
        }
2514
2515 39
        if (isset($resultMapping['entities'])) {
2516 39
            foreach ($resultMapping['entities'] as $key => $entityResult) {
2517 39
                if (!isset($entityResult['entityClass'])) {
2518 1
                    throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']);
2519
                }
2520
2521 38
                $entityResult['isSelfClass'] = false;
2522 38
                if ($entityResult['entityClass'] === '__CLASS__') {
2523
2524 20
                    $entityResult['isSelfClass'] = true;
2525 20
                    $entityResult['entityClass'] = $this->name;
2526
2527
                }
2528
2529 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...
2530
2531 38
                $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\');
2532 38
                $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass'];
2533
2534 38
                if (isset($entityResult['fields'])) {
2535 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...
2536 34
                        if (!isset($field['name'])) {
2537
                            throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']);
2538
                        }
2539
2540 34
                        if (!isset($field['column'])) {
2541 14
                            $fieldName = $field['name'];
2542 14
                            if (strpos($fieldName, '.')) {
2543 9
                                list(, $fieldName) = explode('.', $fieldName);
2544
                            }
2545
2546 38
                            $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName;
2547
                        }
2548
                    }
2549
                }
2550
            }
2551
        }
2552
2553 38
        $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping;
2554 38
    }
2555
2556
    /**
2557
     * Adds a one-to-one mapping.
2558
     *
2559
     * @param array $mapping The mapping.
2560
     *
2561
     * @return void
2562
     */
2563 164
    public function mapOneToOne(array $mapping)
2564
    {
2565 164
        $mapping['type'] = self::ONE_TO_ONE;
2566
2567 164
        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2568
2569 161
        $this->_storeAssociationMapping($mapping);
2570 160
    }
2571
2572
    /**
2573
     * Adds a one-to-many mapping.
2574
     *
2575
     * @param array $mapping The mapping.
2576
     *
2577
     * @return void
2578
     */
2579 127
    public function mapOneToMany(array $mapping)
2580
    {
2581 127
        $mapping['type'] = self::ONE_TO_MANY;
2582
2583 127
        $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2584
2585 126
        $this->_storeAssociationMapping($mapping);
2586 126
    }
2587
2588
    /**
2589
     * Adds a many-to-one mapping.
2590
     *
2591
     * @param array $mapping The mapping.
2592
     *
2593
     * @return void
2594
     */
2595 155
    public function mapManyToOne(array $mapping)
2596
    {
2597 155
        $mapping['type'] = self::MANY_TO_ONE;
2598
2599
        // A many-to-one mapping is essentially a one-one backreference
2600 155
        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2601
2602 150
        $this->_storeAssociationMapping($mapping);
2603 150
    }
2604
2605
    /**
2606
     * Adds a many-to-many mapping.
2607
     *
2608
     * @param array $mapping The mapping.
2609
     *
2610
     * @return void
2611
     */
2612 143
    public function mapManyToMany(array $mapping)
2613
    {
2614 143
        $mapping['type'] = self::MANY_TO_MANY;
2615
2616 143
        $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2617
2618 141
        $this->_storeAssociationMapping($mapping);
2619 141
    }
2620
2621
    /**
2622
     * Stores the association mapping.
2623
     *
2624
     * @param array $assocMapping
2625
     *
2626
     * @return void
2627
     *
2628
     * @throws MappingException
2629
     */
2630 326
    protected function _storeAssociationMapping(array $assocMapping)
2631
    {
2632 326
        $sourceFieldName = $assocMapping['fieldName'];
2633
2634 326
        $this->assertFieldNotMapped($sourceFieldName);
2635
2636 325
        $this->associationMappings[$sourceFieldName] = $assocMapping;
2637 325
    }
2638
2639
    /**
2640
     * Registers a custom repository class for the entity class.
2641
     *
2642
     * @param string $repositoryClassName The class name of the custom mapper.
2643
     *
2644
     * @return void
2645
     */
2646 78
    public function setCustomRepositoryClass($repositoryClassName)
2647
    {
2648 78
        $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName);
2649 78
    }
2650
2651
    /**
2652
     * Registers a custom persister class for the entity class.
2653
     *
2654
     * @param string $persisterClassName The class name of the custom mapper.
2655
     *
2656
     * @return void
2657
     */
2658 22
    public function setCustomPersisterClass($persisterClassName)
2659
    {
2660 22
        $this->customPersisterClassName = $this->fullyQualifiedClassName($persisterClassName);
2661 22
    }
2662
2663
    /**
2664
     * Dispatches the lifecycle event of the given entity to the registered
2665
     * lifecycle callbacks and lifecycle listeners.
2666
     *
2667
     * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker
2668
     *
2669
     * @param string $lifecycleEvent The lifecycle event.
2670
     * @param object $entity         The Entity on which the event occurred.
2671
     *
2672
     * @return void
2673
     */
2674
    public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
2675
    {
2676
        foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
2677
            $entity->$callback();
2678
        }
2679
    }
2680
2681
    /**
2682
     * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
2683
     *
2684
     * @param string $lifecycleEvent
2685
     *
2686
     * @return boolean
2687
     */
2688
    public function hasLifecycleCallbacks($lifecycleEvent)
2689
    {
2690
        return isset($this->lifecycleCallbacks[$lifecycleEvent]);
2691
    }
2692
2693
    /**
2694
     * Gets the registered lifecycle callbacks for an event.
2695
     *
2696
     * @param string $event
2697
     *
2698
     * @return array
2699
     */
2700
    public function getLifecycleCallbacks($event)
2701
    {
2702
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
2703
    }
2704
2705
    /**
2706
     * Adds a lifecycle callback for entities of this class.
2707
     *
2708
     * @param string $callback
2709
     * @param string $event
2710
     *
2711
     * @return void
2712
     */
2713 39
    public function addLifecycleCallback($callback, $event)
2714
    {
2715 39
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
2716 2
            return;
2717
        }
2718
2719 39
        $this->lifecycleCallbacks[$event][] = $callback;
2720 39
    }
2721
2722
    /**
2723
     * Sets the lifecycle callbacks for entities of this class.
2724
     * Any previously registered callbacks are overwritten.
2725
     *
2726
     * @param array $callbacks
2727
     *
2728
     * @return void
2729
     */
2730 100
    public function setLifecycleCallbacks(array $callbacks)
2731
    {
2732 100
        $this->lifecycleCallbacks = $callbacks;
2733 100
    }
2734
2735
    /**
2736
     * Adds a entity listener for entities of this class.
2737
     *
2738
     * @param string $eventName The entity lifecycle event.
2739
     * @param string $class     The listener class.
2740
     * @param string $method    The listener callback method.
2741
     *
2742
     * @throws \Doctrine\ORM\Mapping\MappingException
2743
     */
2744 35
    public function addEntityListener($eventName, $class, $method)
2745
    {
2746 35
        $class    = $this->fullyQualifiedClassName($class);
2747
2748
        $listener = array(
2749 35
            'class'  => $class,
2750 35
            'method' => $method,
2751
        );
2752
2753 35
        if ( ! class_exists($class)) {
2754 1
            throw MappingException::entityListenerClassNotFound($class, $this->name);
2755
        }
2756
2757 34
        if ( ! method_exists($class, $method)) {
2758 1
            throw MappingException::entityListenerMethodNotFound($class, $method, $this->name);
2759
        }
2760
2761 33
        if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName])) {
2762 1
            throw MappingException::duplicateEntityListener($class, $method, $this->name);
2763
        }
2764
2765 33
        $this->entityListeners[$eventName][] = $listener;
2766 33
    }
2767
2768
    /**
2769
     * Sets the discriminator column definition.
2770
     *
2771
     * @param array $columnDef
2772
     *
2773
     * @return void
2774
     *
2775
     * @throws MappingException
2776
     *
2777
     * @see getDiscriminatorColumn()
2778
     */
2779 127
    public function setDiscriminatorColumn($columnDef)
2780
    {
2781 127
        if ($columnDef !== null) {
2782 96
            if ( ! isset($columnDef['name'])) {
2783 1
                throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name);
2784
            }
2785
2786 95
            if (isset($this->fieldNames[$columnDef['name']])) {
2787 1
                throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
2788
            }
2789
2790 94
            if ( ! isset($columnDef['fieldName'])) {
2791 89
                $columnDef['fieldName'] = $columnDef['name'];
2792
            }
2793
2794 94
            if ( ! isset($columnDef['type'])) {
2795 6
                $columnDef['type'] = "string";
2796
            }
2797
2798 94
            if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
2799
                throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
2800
            }
2801
2802 94
            $this->discriminatorColumn = $columnDef;
2803
        }
2804 125
    }
2805
2806
    /**
2807
     * Sets the discriminator values used by this class.
2808
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
2809
     *
2810
     * @param array $map
2811
     *
2812
     * @return void
2813
     */
2814 121
    public function setDiscriminatorMap(array $map)
2815
    {
2816 121
        foreach ($map as $value => $className) {
2817 90
            $this->addDiscriminatorMapClass($value, $className);
2818
        }
2819 121
    }
2820
2821
    /**
2822
     * Adds one entry of the discriminator map with a new class and corresponding name.
2823
     *
2824
     * @param string $name
2825
     * @param string $className
2826
     *
2827
     * @return void
2828
     *
2829
     * @throws MappingException
2830
     */
2831 91
    public function addDiscriminatorMapClass($name, $className)
2832
    {
2833 91
        $className = $this->fullyQualifiedClassName($className);
2834 91
        $className = ltrim($className, '\\');
2835
2836 91
        $this->discriminatorMap[$name] = $className;
2837
2838 91
        if ($this->name === $className) {
2839 73
            $this->discriminatorValue = $name;
2840
2841 73
            return;
2842
        }
2843
2844 90
        if ( ! (class_exists($className) || interface_exists($className))) {
2845
            throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
2846
        }
2847
2848 90
        if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
2849 82
            $this->subClasses[] = $className;
2850
        }
2851 90
    }
2852
2853
    /**
2854
     * Checks whether the class has a named query with the given query name.
2855
     *
2856
     * @param string $queryName
2857
     *
2858
     * @return boolean
2859
     */
2860 1
    public function hasNamedQuery($queryName)
2861
    {
2862 1
        return isset($this->namedQueries[$queryName]);
2863
    }
2864
2865
    /**
2866
     * Checks whether the class has a named native query with the given query name.
2867
     *
2868
     * @param string $queryName
2869
     *
2870
     * @return boolean
2871
     */
2872 1
    public function hasNamedNativeQuery($queryName)
2873
    {
2874 1
        return isset($this->namedNativeQueries[$queryName]);
2875
    }
2876
2877
    /**
2878
     * Checks whether the class has a named native query with the given query name.
2879
     *
2880
     * @param string $name
2881
     *
2882
     * @return boolean
2883
     */
2884 1
    public function hasSqlResultSetMapping($name)
2885
    {
2886 1
        return isset($this->sqlResultSetMappings[$name]);
2887
    }
2888
2889
    /**
2890
     * {@inheritDoc}
2891
     */
2892 336
    public function hasAssociation($fieldName)
2893
    {
2894 336
        return isset($this->associationMappings[$fieldName]);
2895
    }
2896
2897
    /**
2898
     * {@inheritDoc}
2899
     */
2900 1
    public function isSingleValuedAssociation($fieldName)
2901
    {
2902 1
        return isset($this->associationMappings[$fieldName])
2903 1
            && ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2904
    }
2905
2906
    /**
2907
     * {@inheritDoc}
2908
     */
2909 1013
    public function isCollectionValuedAssociation($fieldName)
2910
    {
2911 1013
        return isset($this->associationMappings[$fieldName])
2912 1013
            && ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2913
    }
2914
2915
    /**
2916
     * Is this an association that only has a single join column?
2917
     *
2918
     * @param string $fieldName
2919
     *
2920
     * @return bool
2921
     */
2922 35
    public function isAssociationWithSingleJoinColumn($fieldName)
2923
    {
2924 35
        return isset($this->associationMappings[$fieldName])
2925 35
            && isset($this->associationMappings[$fieldName]['joinColumns'][0])
2926 35
            && ! isset($this->associationMappings[$fieldName]['joinColumns'][1]);
2927
    }
2928
2929
    /**
2930
     * Returns the single association join column (if any).
2931
     *
2932
     * @param string $fieldName
2933
     *
2934
     * @return string
2935
     *
2936
     * @throws MappingException
2937
     */
2938 9
    public function getSingleAssociationJoinColumnName($fieldName)
2939
    {
2940 9
        if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2941
            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2942
        }
2943
2944 9
        return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
2945
    }
2946
2947
    /**
2948
     * Returns the single association referenced join column name (if any).
2949
     *
2950
     * @param string $fieldName
2951
     *
2952
     * @return string
2953
     *
2954
     * @throws MappingException
2955
     */
2956 9
    public function getSingleAssociationReferencedJoinColumnName($fieldName)
2957
    {
2958 9
        if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2959
            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2960
        }
2961
2962 9
        return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
2963
    }
2964
2965
    /**
2966
     * Used to retrieve a fieldname for either field or association from a given column.
2967
     *
2968
     * This method is used in foreign-key as primary-key contexts.
2969
     *
2970
     * @param string $columnName
2971
     *
2972
     * @return string
2973
     *
2974
     * @throws MappingException
2975
     */
2976 629
    public function getFieldForColumn($columnName)
2977
    {
2978 629
        if (isset($this->fieldNames[$columnName])) {
2979 629
            return $this->fieldNames[$columnName];
2980
        } else {
2981 33
            foreach ($this->associationMappings as $assocName => $mapping) {
2982 33
                if ($this->isAssociationWithSingleJoinColumn($assocName) &&
2983 33
                    $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
2984
2985 33
                    return $assocName;
2986
                }
2987
            }
2988
2989
            throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
2990
        }
2991
    }
2992
2993
    /**
2994
     * Sets the ID generator used to generate IDs for instances of this class.
2995
     *
2996
     * @param \Doctrine\ORM\Id\AbstractIdGenerator $generator
2997
     *
2998
     * @return void
2999
     */
3000 384
    public function setIdGenerator($generator)
3001
    {
3002 384
        $this->idGenerator = $generator;
3003 384
    }
3004
3005
    /**
3006
     * Sets definition.
3007
     *
3008
     * @param array $definition
3009
     *
3010
     * @return void
3011
     */
3012 12
    public function setCustomGeneratorDefinition(array $definition)
3013
    {
3014 12
        $this->customGeneratorDefinition = $definition;
3015 12
    }
3016
3017
    /**
3018
     * Sets the definition of the sequence ID generator for this class.
3019
     *
3020
     * The definition must have the following structure:
3021
     * <code>
3022
     * array(
3023
     *     'sequenceName'   => 'name',
3024
     *     'allocationSize' => 20,
3025
     *     'initialValue'   => 1
3026
     *     'quoted'         => 1
3027
     * )
3028
     * </code>
3029
     *
3030
     * @param array $definition
3031
     *
3032
     * @return void
3033
     *
3034
     * @throws MappingException
3035
     */
3036 20
    public function setSequenceGeneratorDefinition(array $definition)
3037
    {
3038 20
        if ( ! isset($definition['sequenceName'])) {
3039 1
            throw MappingException::missingSequenceName($this->name);
3040
        }
3041
3042 19
        if ($definition['sequenceName'][0] == '`') {
3043 1
            $definition['sequenceName']   = trim($definition['sequenceName'], '`');
3044 1
            $definition['quoted'] = true;
3045
        }
3046
3047 19
        $this->sequenceGeneratorDefinition = $definition;
3048 19
    }
3049
3050
    /**
3051
     * Sets the version field mapping used for versioning. Sets the default
3052
     * value to use depending on the column type.
3053
     *
3054
     * @param array $mapping The version field mapping array.
3055
     *
3056
     * @return void
3057
     *
3058
     * @throws MappingException
3059
     */
3060 25
    public function setVersionMapping(array &$mapping)
3061
    {
3062 25
        $this->isVersioned = true;
3063 25
        $this->versionField = $mapping['fieldName'];
3064
3065 25
        if ( ! isset($mapping['default'])) {
3066 25
            if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) {
3067 24
                $mapping['default'] = 1;
3068 2
            } else if ($mapping['type'] == 'datetime') {
3069 1
                $mapping['default'] = 'CURRENT_TIMESTAMP';
3070
            } else {
3071 1
                throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
3072
            }
3073
        }
3074 24
    }
3075
3076
    /**
3077
     * Sets whether this class is to be versioned for optimistic locking.
3078
     *
3079
     * @param boolean $bool
3080
     *
3081
     * @return void
3082
     */
3083 100
    public function setVersioned($bool)
3084
    {
3085 100
        $this->isVersioned = $bool;
3086 100
    }
3087
3088
    /**
3089
     * Sets the name of the field that is to be used for versioning if this class is
3090
     * versioned for optimistic locking.
3091
     *
3092
     * @param string $versionField
3093
     *
3094
     * @return void
3095
     */
3096 100
    public function setVersionField($versionField)
3097
    {
3098 100
        $this->versionField = $versionField;
3099 100
    }
3100
3101
    /**
3102
     * Marks this class as read only, no change tracking is applied to it.
3103
     *
3104
     * @return void
3105
     */
3106 3
    public function markReadOnly()
3107
    {
3108 3
        $this->isReadOnly = true;
3109 3
    }
3110
3111
    /**
3112
     * {@inheritDoc}
3113
     */
3114
    public function getFieldNames()
3115
    {
3116
        return array_keys($this->fieldMappings);
3117
    }
3118
3119
    /**
3120
     * {@inheritDoc}
3121
     */
3122
    public function getAssociationNames()
3123
    {
3124
        return array_keys($this->associationMappings);
3125
    }
3126
3127
    /**
3128
     * {@inheritDoc}
3129
     *
3130
     * @throws InvalidArgumentException
3131
     */
3132 1
    public function getAssociationTargetClass($assocName)
3133
    {
3134 1
        if ( ! isset($this->associationMappings[$assocName])) {
3135
            throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
3136
        }
3137
3138 1
        return $this->associationMappings[$assocName]['targetEntity'];
3139
    }
3140
3141
    /**
3142
     * {@inheritDoc}
3143
     */
3144 712
    public function getName()
3145
    {
3146 712
        return $this->name;
3147
    }
3148
3149
    /**
3150
     * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
3151
     *
3152
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3153
     *
3154
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3155
     *
3156
     * @return array
3157
     */
3158
    public function getQuotedIdentifierColumnNames($platform)
3159
    {
3160
        $quotedColumnNames = array();
3161
3162
        foreach ($this->identifier as $idProperty) {
3163
            if (isset($this->fieldMappings[$idProperty])) {
3164
                $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
3165
                    ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
3166
                    : $this->fieldMappings[$idProperty]['columnName'];
3167
3168
                continue;
3169
            }
3170
3171
            // Association defined as Id field
3172
            $joinColumns            = $this->associationMappings[$idProperty]['joinColumns'];
3173
            $assocQuotedColumnNames = array_map(
3174
                function ($joinColumn) use ($platform) {
3175
                    return isset($joinColumn['quoted'])
3176
                        ? $platform->quoteIdentifier($joinColumn['name'])
3177
                        : $joinColumn['name'];
3178
                },
3179
                $joinColumns
3180
            );
3181
3182
            $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
3183
        }
3184
3185
        return $quotedColumnNames;
3186
    }
3187
3188
    /**
3189
     * Gets the (possibly quoted) column name of a mapped field for safe use  in an SQL statement.
3190
     *
3191
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3192
     *
3193
     * @param string                                    $field
3194
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3195
     *
3196
     * @return string
3197
     */
3198
    public function getQuotedColumnName($field, $platform)
3199
    {
3200
        return isset($this->fieldMappings[$field]['quoted'])
3201
            ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
3202
            : $this->fieldMappings[$field]['columnName'];
3203
    }
3204
3205
    /**
3206
     * Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement.
3207
     *
3208
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3209
     *
3210
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3211
     *
3212
     * @return string
3213
     */
3214
    public function getQuotedTableName($platform)
3215
    {
3216
        return isset($this->table['quoted'])
3217
            ? $platform->quoteIdentifier($this->table['name'])
3218
            : $this->table['name'];
3219
    }
3220
3221
    /**
3222
     * Gets the (possibly quoted) name of the join table.
3223
     *
3224
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3225
     *
3226
     * @param array                                     $assoc
3227
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3228
     *
3229
     * @return string
3230
     */
3231
    public function getQuotedJoinTableName(array $assoc, $platform)
3232
    {
3233
        return isset($assoc['joinTable']['quoted'])
3234
            ? $platform->quoteIdentifier($assoc['joinTable']['name'])
3235
            : $assoc['joinTable']['name'];
3236
    }
3237
3238
    /**
3239
     * {@inheritDoc}
3240
     */
3241 12
    public function isAssociationInverseSide($fieldName)
3242
    {
3243 12
        return isset($this->associationMappings[$fieldName])
3244 12
            && ! $this->associationMappings[$fieldName]['isOwningSide'];
3245
    }
3246
3247
    /**
3248
     * {@inheritDoc}
3249
     */
3250
    public function getAssociationMappedByTargetField($fieldName)
3251
    {
3252
        return $this->associationMappings[$fieldName]['mappedBy'];
3253
    }
3254
3255
    /**
3256
     * @param string $targetClass
3257
     *
3258
     * @return array
3259
     */
3260 2
    public function getAssociationsByTargetClass($targetClass)
3261
    {
3262 2
        $relations = array();
3263
3264 2
        foreach ($this->associationMappings as $mapping) {
3265 2
            if ($mapping['targetEntity'] == $targetClass) {
3266 2
                $relations[$mapping['fieldName']] = $mapping;
3267
            }
3268
        }
3269
3270 2
        return $relations;
3271
    }
3272
3273
    /**
3274
     * @param  string|null $className
3275
     *
3276
     * @return string|null null if the input value is null
3277
     */
3278 473
    public function fullyQualifiedClassName($className)
3279
    {
3280 473
        if (empty($className)) {
3281 68
            return $className;
3282
        }
3283
3284 434
        if ($className !== null && strpos($className, '\\') === false && strlen($this->namespace) > 0) {
3285 324
            return $this->namespace . '\\' . $className;
3286
        }
3287
3288 225
        return $className;
3289
    }
3290
3291
    /**
3292
     * @param string $name
3293
     *
3294
     * @return mixed
3295
     */
3296 2
    public function getMetadataValue($name)
3297
    {
3298
3299 2
        if (isset($this->$name)) {
3300 2
            return $this->$name;
3301
        }
3302
3303
        return null;
3304
    }
3305
3306
    /**
3307
     * Map Embedded Class
3308
     *
3309
     * @param array $mapping
3310
     *
3311
     * @throws MappingException
3312
     * @return void
3313
     */
3314 26
    public function mapEmbedded(array $mapping)
3315
    {
3316 26
        $this->assertFieldNotMapped($mapping['fieldName']);
3317
3318 26
        $this->embeddedClasses[$mapping['fieldName']] = array(
3319 26
            'class' => $this->fullyQualifiedClassName($mapping['class']),
3320 26
            'columnPrefix' => $mapping['columnPrefix'],
3321 26
            'declaredField' => isset($mapping['declaredField']) ? $mapping['declaredField'] : null,
3322 26
            'originalField' => isset($mapping['originalField']) ? $mapping['originalField'] : null,
3323
        );
3324 26
    }
3325
3326
    /**
3327
     * Inline the embeddable class
3328
     *
3329
     * @param string            $property
3330
     * @param ClassMetadataInfo $embeddable
3331
     */
3332 10
    public function inlineEmbeddable($property, ClassMetadataInfo $embeddable)
3333
    {
3334 10
        foreach ($embeddable->fieldMappings as $fieldMapping) {
3335 10
            $fieldMapping['originalClass'] = isset($fieldMapping['originalClass'])
3336 4
                ? $fieldMapping['originalClass']
3337 10
                : $embeddable->name;
3338 10
            $fieldMapping['declaredField'] = isset($fieldMapping['declaredField'])
3339 4
                ? $property . '.' . $fieldMapping['declaredField']
3340 10
                : $property;
3341 10
            $fieldMapping['originalField'] = isset($fieldMapping['originalField'])
3342 4
                ? $fieldMapping['originalField']
3343 10
                : $fieldMapping['fieldName'];
3344 10
            $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName'];
3345
3346 10
            if (! empty($this->embeddedClasses[$property]['columnPrefix'])) {
3347 2
                $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName'];
3348 9
            } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) {
3349 6
                $fieldMapping['columnName'] = $this->namingStrategy
3350 6
                    ->embeddedFieldToColumnName(
3351
                        $property,
3352 6
                        $fieldMapping['columnName'],
3353 6
                        $this->reflClass->name,
3354 6
                        $embeddable->reflClass->name
3355
                    );
3356
            }
3357
3358 10
            $this->mapField($fieldMapping);
3359
        }
3360 10
    }
3361
3362
    /**
3363
     * @param string $fieldName
3364
     * @throws MappingException
3365
     */
3366 525
    private function assertFieldNotMapped($fieldName)
3367
    {
3368 525
        if (isset($this->fieldMappings[$fieldName]) ||
3369 525
            isset($this->associationMappings[$fieldName]) ||
3370 525
            isset($this->embeddedClasses[$fieldName])) {
3371
3372 2
            throw MappingException::duplicateFieldMapping($this->name, $fieldName);
3373
        }
3374 525
    }
3375
3376
    /**
3377
     * Gets the sequence name based on class metadata.
3378
     *
3379
     * @param AbstractPlatform $platform
3380
     * @return string
3381
     *
3382
     * @todo Sequence names should be computed in DBAL depending on the platform
3383
     */
3384 3
    public function getSequenceName(AbstractPlatform $platform)
3385
    {
3386 3
        $sequencePrefix = $this->getSequencePrefix($platform);
3387 3
        $columnName     = $this->getSingleIdentifierColumnName();
3388 3
        $sequenceName   = $sequencePrefix . '_' . $columnName . '_seq';
3389
3390 3
        return $sequenceName;
3391
    }
3392
3393
    /**
3394
     * Gets the sequence name prefix based on class metadata.
3395
     *
3396
     * @param AbstractPlatform $platform
3397
     * @return string
3398
     *
3399
     * @todo Sequence names should be computed in DBAL depending on the platform
3400
     */
3401 3
    public function getSequencePrefix(AbstractPlatform $platform)
3402
    {
3403 3
        $tableName      = $this->getTableName();
3404 3
        $sequencePrefix = $tableName;
3405
3406
        // Prepend the schema name to the table name if there is one
3407 3
        if ($schemaName = $this->getSchemaName()) {
3408 3
            $sequencePrefix = $schemaName . '.' . $tableName;
3409
3410 3
            if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) {
3411 3
                $sequencePrefix = $schemaName . '__' . $tableName;
3412
            }
3413
        }
3414
3415 3
        return $sequencePrefix;
3416
    }
3417
}
3418