Completed
Pull Request — master (#5728)
by Sebastiaan de
11:27
created

ClassMetadataInfo::isCollectionValuedAssociation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

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

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

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

Loading history...
874
            $serialized[] = 'lifecycleCallbacks';
875
        }
876
877 6
        if ($this->entityListeners) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entityListeners of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
878 1
            $serialized[] = 'entityListeners';
879
        }
880
881 6
        if ($this->namedQueries) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->namedQueries of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
882 1
            $serialized[] = 'namedQueries';
883
        }
884
885 6
        if ($this->namedNativeQueries) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->namedNativeQueries of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
886
            $serialized[] = 'namedNativeQueries';
887
        }
888
889 6
        if ($this->sqlResultSetMappings) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->sqlResultSetMappings of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
890
            $serialized[] = 'sqlResultSetMappings';
891
        }
892
893 6
        if ($this->isReadOnly) {
894 1
            $serialized[] = 'isReadOnly';
895
        }
896
897 6
        if ($this->customGeneratorDefinition) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->customGeneratorDefinition of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
898
            $serialized[] = "customGeneratorDefinition";
899
        }
900
901 6
        if ($this->cache) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cache of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
902
            $serialized[] = 'cache';
903
        }
904
905 6
        return $serialized;
906
    }
907
908
    /**
909
     * Creates a new instance of the mapped class, without invoking the constructor.
910
     *
911
     * @return object
912
     */
913 670
    public function newInstance()
914
    {
915 670
        return $this->instantiator->instantiate($this->name);
916
    }
917
918
    /**
919
     * Restores some state that can not be serialized/unserialized.
920
     *
921
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
922
     *
923
     * @return void
924
     */
925 2006
    public function wakeupReflection($reflService)
926
    {
927
        // Restore ReflectionClass and properties
928 2006
        $this->reflClass    = $reflService->getClass($this->name);
929 2006
        $this->instantiator = $this->instantiator ?: new Instantiator();
930
931 2006
        $parentReflFields = array();
932
933 2006
        foreach ($this->embeddedClasses as $property => $embeddedClass) {
934 20
            if (isset($embeddedClass['declaredField'])) {
935 15
                $parentReflFields[$property] = new ReflectionEmbeddedProperty(
936 15
                    $parentReflFields[$embeddedClass['declaredField']],
937 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...
938 15
                        $this->embeddedClasses[$embeddedClass['declaredField']]['class'],
939 15
                        $embeddedClass['originalField']
940
                    ),
941 15
                    $this->embeddedClasses[$embeddedClass['declaredField']]['class']
942
                );
943
944 15
                continue;
945
            }
946
947 20
            $parentReflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
948 20
            $this->reflFields[$property] = $reflService->getAccessibleProperty($this->name, $property);
949
        }
950
951 2006
        foreach ($this->fieldMappings as $field => $mapping) {
952 2001
            if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
953 19
                $this->reflFields[$field] = new ReflectionEmbeddedProperty(
954 19
                    $parentReflFields[$mapping['declaredField']],
955 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...
956 19
                    $mapping['originalClass']
957
                );
958 19
                continue;
959
            }
960
961 2001
            $this->reflFields[$field] = isset($mapping['declared'])
962 493
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
963 2001
                : $reflService->getAccessibleProperty($this->name, $field);
964
        }
965
966 2006
        foreach ($this->associationMappings as $field => $mapping) {
967 1691
            $this->reflFields[$field] = isset($mapping['declared'])
968 401
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
969 1691
                : $reflService->getAccessibleProperty($this->name, $field);
970
        }
971 2006
    }
972
973
    /**
974
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
975
     * metadata of the class with the given name.
976
     *
977
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service.
978
     *
979
     * @return void
980
     */
981 610
    public function initializeReflection($reflService)
982
    {
983 610
        $this->reflClass = $reflService->getClass($this->name);
984 610
        $this->namespace = $reflService->getClassNamespace($this->name);
985
986 610
        if ($this->reflClass) {
987 603
            $this->name = $this->rootEntityName = $this->reflClass->getName();
988
        }
989
990 610
        $this->table['name'] = $this->namingStrategy->classToTableName($this->name);
991 610
    }
992
993
    /**
994
     * Validates Identifier.
995
     *
996
     * @return void
997
     *
998
     * @throws MappingException
999
     */
1000 399
    public function validateIdentifier()
1001
    {
1002 399
        if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
1003 59
            return;
1004
        }
1005
1006
        // Verify & complete identifier mapping
1007 397
        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...
1008 6
            throw MappingException::identifierRequired($this->name);
1009
        }
1010
1011 391
        if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
1012
            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
1013
        }
1014 391
    }
1015
1016
    /**
1017
     * Validates association targets actually exist.
1018
     *
1019
     * @return void
1020
     *
1021
     * @throws MappingException
1022
     */
1023 400
    public function validateAssociations()
1024
    {
1025 400
        foreach ($this->associationMappings as $mapping) {
1026 266
            if ( ! ClassLoader::classExists($mapping['targetEntity']) ) {
1027 266
                throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
1028
            }
1029
        }
1030 399
    }
1031
1032
    /**
1033
     * Validates lifecycle callbacks.
1034
     *
1035
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
1036
     *
1037
     * @return void
1038
     *
1039
     * @throws MappingException
1040
     */
1041 400
    public function validateLifecycleCallbacks($reflService)
1042
    {
1043 400
        foreach ($this->lifecycleCallbacks as $callbacks) {
1044 13
            foreach ($callbacks as $callbackFuncName) {
1045 13
                if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
1046 13
                    throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
1047
                }
1048
            }
1049
        }
1050 399
    }
1051
1052
    /**
1053
     * {@inheritDoc}
1054
     */
1055 527
    public function getReflectionClass()
1056
    {
1057 527
        return $this->reflClass;
1058
    }
1059
1060
    /**
1061
     * @param array $cache
1062
     *
1063
     * @return void
1064
     */
1065 20
    public function enableCache(array $cache)
1066
    {
1067 20
        if ( ! isset($cache['usage'])) {
1068
            $cache['usage'] = self::CACHE_USAGE_READ_ONLY;
1069
        }
1070
1071 20
        if ( ! isset($cache['region'])) {
1072 20
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName));
1073
        }
1074
1075 20
        $this->cache = $cache;
1076 20
    }
1077
1078
    /**
1079
     * @param string $fieldName
1080
     * @param array  $cache
1081
     *
1082
     * @return void
1083
     */
1084 2
    public function enableAssociationCache($fieldName, array $cache)
1085
    {
1086 2
        $this->associationMappings[$fieldName]['cache'] = $this->getAssociationCacheDefaults ($fieldName, $cache);
1087 2
    }
1088
1089
    /**
1090
     * @param string $fieldName
1091
     * @param array  $cache
1092
     *
1093
     * @return array
1094
     */
1095 16
    public function getAssociationCacheDefaults($fieldName, array $cache)
1096
    {
1097 16
        if ( ! isset($cache['usage'])) {
1098 1
            $cache['usage'] = isset($this->cache['usage'])
1099 1
                ? $this->cache['usage']
1100
                : self::CACHE_USAGE_READ_ONLY;
1101
        }
1102
1103 16
        if ( ! isset($cache['region'])) {
1104 16
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName;
1105
        }
1106
1107 16
        return $cache;
1108
    }
1109
1110
    /**
1111
     * Sets the change tracking policy used by this class.
1112
     *
1113
     * @param integer $policy
1114
     *
1115
     * @return void
1116
     */
1117 138
    public function setChangeTrackingPolicy($policy)
1118
    {
1119 138
        $this->changeTrackingPolicy = $policy;
1120 138
    }
1121
1122
    /**
1123
     * Whether the change tracking policy of this class is "deferred explicit".
1124
     *
1125
     * @return boolean
1126
     */
1127 267
    public function isChangeTrackingDeferredExplicit()
1128
    {
1129 267
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
1130
    }
1131
1132
    /**
1133
     * Whether the change tracking policy of this class is "deferred implicit".
1134
     *
1135
     * @return boolean
1136
     */
1137 458
    public function isChangeTrackingDeferredImplicit()
1138
    {
1139 458
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1140
    }
1141
1142
    /**
1143
     * Whether the change tracking policy of this class is "notify".
1144
     *
1145
     * @return boolean
1146
     */
1147 290
    public function isChangeTrackingNotify()
1148
    {
1149 290
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1150
    }
1151
1152
    /**
1153
     * Checks whether a field is part of the identifier/primary key field(s).
1154
     *
1155
     * @param string $fieldName The field name.
1156
     *
1157
     * @return boolean TRUE if the field is part of the table identifier/primary key field(s),
1158
     *                 FALSE otherwise.
1159
     */
1160 1069
    public function isIdentifier($fieldName)
1161
    {
1162 1069
        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...
1163 1
            return false;
1164
        }
1165
1166 1068
        if ( ! $this->isIdentifierComposite) {
1167 1063
            return $fieldName === $this->identifier[0];
1168
        }
1169
1170 93
        return in_array($fieldName, $this->identifier);
1171
    }
1172
1173
    /**
1174
     * Checks if the field is unique.
1175
     *
1176
     * @param string $fieldName The field name.
1177
     *
1178
     * @return boolean TRUE if the field is unique, FALSE otherwise.
1179
     */
1180
    public function isUniqueField($fieldName)
1181
    {
1182
        $mapping = $this->getFieldMapping($fieldName);
1183
1184
        if ($mapping !== false) {
1185
            return isset($mapping['unique']) && $mapping['unique'] == true;
1186
        }
1187
1188
        return false;
1189
    }
1190
1191
    /**
1192
     * Checks if the field is not null.
1193
     *
1194
     * @param string $fieldName The field name.
1195
     *
1196
     * @return boolean TRUE if the field is not null, FALSE otherwise.
1197
     */
1198 1
    public function isNullable($fieldName)
1199
    {
1200 1
        $mapping = $this->getFieldMapping($fieldName);
1201
1202 1
        if ($mapping !== false) {
1203 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1204
        }
1205
1206
        return false;
1207
    }
1208
1209
    /**
1210
     * Checks if the field is readOnly.
1211
     *
1212
     * @param string $fieldName The field name.
1213
     *
1214
     * @return boolean TRUE if the field is readOnly, FALSE otherwise.
1215
     */
1216 1001
    public function isReadOnly($fieldName)
1217
    {
1218 1001
        $mapping = $this->getFieldMapping($fieldName);
1219
1220 1001
        if ($mapping !== false) {
1221 1001
            return isset($mapping['readOnly']) && $mapping['readOnly'] == true;
1222
        }
1223
1224
        return false;
1225
    }
1226
1227
    /**
1228
     * Gets a column name for a field name.
1229
     * If the column name for the field cannot be found, the given field name
1230
     * is returned.
1231
     *
1232
     * @param string $fieldName The field name.
1233
     *
1234
     * @return string The column name.
1235
     */
1236 16
    public function getColumnName($fieldName)
1237
    {
1238 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...
1239 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...
1240 16
            : $fieldName;
1241
    }
1242
1243
    /**
1244
     * Gets the mapping of a (regular) field that holds some data but not a
1245
     * reference to another object.
1246
     *
1247
     * @param string $fieldName The field name.
1248
     *
1249
     * @return array The field mapping.
1250
     *
1251
     * @throws MappingException
1252
     */
1253 1034
    public function getFieldMapping($fieldName)
1254
    {
1255 1034
        if ( ! isset($this->fieldMappings[$fieldName])) {
1256 1
            throw MappingException::mappingNotFound($this->name, $fieldName);
1257
        }
1258
1259 1033
        return $this->fieldMappings[$fieldName];
1260
    }
1261
1262
    /**
1263
     * Gets the mapping of an association.
1264
     *
1265
     * @see ClassMetadataInfo::$associationMappings
1266
     *
1267
     * @param string $fieldName The field name that represents the association in
1268
     *                          the object model.
1269
     *
1270
     * @return array The mapping.
1271
     *
1272
     * @throws MappingException
1273
     */
1274 484
    public function getAssociationMapping($fieldName)
1275
    {
1276 484
        if ( ! isset($this->associationMappings[$fieldName])) {
1277
            throw MappingException::mappingNotFound($this->name, $fieldName);
1278
        }
1279
1280 484
        return $this->associationMappings[$fieldName];
1281
    }
1282
1283
    /**
1284
     * Gets all association mappings of the class.
1285
     *
1286
     * @return array
1287
     */
1288
    public function getAssociationMappings()
1289
    {
1290
        return $this->associationMappings;
1291
    }
1292
1293
    /**
1294
     * Gets the field name for a column name.
1295
     * If no field name can be found the column name is returned.
1296
     *
1297
     * @param string $columnName The column name.
1298
     *
1299
     * @return string The column alias.
1300
     */
1301 236
    public function getFieldName($columnName)
1302
    {
1303 236
        return isset($this->fieldNames[$columnName])
1304 236
            ? $this->fieldNames[$columnName]
1305 236
            : $columnName;
1306
    }
1307
1308
    /**
1309
     * Gets the named query.
1310
     *
1311
     * @see ClassMetadataInfo::$namedQueries
1312
     *
1313
     * @param string $queryName The query name.
1314
     *
1315
     * @return string
1316
     *
1317
     * @throws MappingException
1318
     */
1319 4
    public function getNamedQuery($queryName)
1320
    {
1321 4
        if ( ! isset($this->namedQueries[$queryName])) {
1322 1
            throw MappingException::queryNotFound($this->name, $queryName);
1323
        }
1324
1325 3
        return $this->namedQueries[$queryName]['dql'];
1326
    }
1327
1328
    /**
1329
     * Gets all named queries of the class.
1330
     *
1331
     * @return array
1332
     */
1333 7
    public function getNamedQueries()
1334
    {
1335 7
        return $this->namedQueries;
1336
    }
1337
1338
    /**
1339
     * Gets the named native query.
1340
     *
1341
     * @see ClassMetadataInfo::$namedNativeQueries
1342
     *
1343
     * @param string $queryName The query name.
1344
     *
1345
     * @return array
1346
     *
1347
     * @throws MappingException
1348
     */
1349 17
    public function getNamedNativeQuery($queryName)
1350
    {
1351 17
        if ( ! isset($this->namedNativeQueries[$queryName])) {
1352
            throw MappingException::queryNotFound($this->name, $queryName);
1353
        }
1354
1355 17
        return $this->namedNativeQueries[$queryName];
1356
    }
1357
1358
    /**
1359
     * Gets all named native queries of the class.
1360
     *
1361
     * @return array
1362
     */
1363 2
    public function getNamedNativeQueries()
1364
    {
1365 2
        return $this->namedNativeQueries;
1366
    }
1367
1368
    /**
1369
     * Gets the result set mapping.
1370
     *
1371
     * @see ClassMetadataInfo::$sqlResultSetMappings
1372
     *
1373
     * @param string $name The result set mapping name.
1374
     *
1375
     * @return array
1376
     *
1377
     * @throws MappingException
1378
     */
1379 21
    public function getSqlResultSetMapping($name)
1380
    {
1381 21
        if ( ! isset($this->sqlResultSetMappings[$name])) {
1382
            throw MappingException::resultMappingNotFound($this->name, $name);
1383
        }
1384
1385 21
        return $this->sqlResultSetMappings[$name];
1386
    }
1387
1388
    /**
1389
     * Gets all sql result set mappings of the class.
1390
     *
1391
     * @return array
1392
     */
1393 8
    public function getSqlResultSetMappings()
1394
    {
1395 8
        return $this->sqlResultSetMappings;
1396
    }
1397
1398
    /**
1399
     * Validates & completes the given field mapping.
1400
     *
1401
     * @param array $mapping The field mapping to validate & complete.
1402
     *
1403
     * @return array The validated and completed field mapping.
1404
     *
1405
     * @throws MappingException
1406
     */
1407 527
    protected function _validateAndCompleteFieldMapping(array &$mapping)
1408
    {
1409
        // Check mandatory fields
1410 527
        if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
1411 1
            throw MappingException::missingFieldName($this->name);
1412
        }
1413
1414 526
        if ( ! isset($mapping['type'])) {
1415
            // Default to string
1416 64
            $mapping['type'] = 'string';
1417
        }
1418
1419
        // Complete fieldName and columnName mapping
1420 526
        if ( ! isset($mapping['columnName'])) {
1421 434
            $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name);
1422
        }
1423
1424 526
        if ($mapping['columnName'][0] === '`') {
1425 11
            $mapping['columnName']  = trim($mapping['columnName'], '`');
1426 11
            $mapping['quoted']      = true;
1427
        }
1428
1429 526
        $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...
1430
1431 526
        if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn != null && $this->discriminatorColumn['name'] == $mapping['columnName'])) {
1432 2
            throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);
1433
        }
1434
1435 525
        $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
1436
1437
        // Complete id mapping
1438 525
        if (isset($mapping['id']) && $mapping['id'] === true) {
1439 487
            if ($this->versionField == $mapping['fieldName']) {
1440
                throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
1441
            }
1442
1443 487
            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1444 487
                $this->identifier[] = $mapping['fieldName'];
1445
            }
1446
1447
            // Check for composite key
1448 487
            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1449 17
                $this->isIdentifierComposite = true;
1450
            }
1451
        }
1452
1453 525
        if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
1454 5
            if (isset($mapping['id']) && $mapping['id'] === true) {
1455
                 throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
1456
            }
1457
1458 5
            $mapping['requireSQLConversion'] = true;
1459
        }
1460 525
    }
1461
1462
    /**
1463
     * Validates & completes the basic mapping information that is common to all
1464
     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
1465
     *
1466
     * @param array $mapping The mapping.
1467
     *
1468
     * @return array The updated mapping.
1469
     *
1470
     * @throws MappingException If something is wrong with the mapping.
1471
     */
1472 349
    protected function _validateAndCompleteAssociationMapping(array $mapping)
1473
    {
1474 349
        if ( ! isset($mapping['mappedBy'])) {
1475 335
            $mapping['mappedBy'] = null;
1476
        }
1477
1478 349
        if ( ! isset($mapping['inversedBy'])) {
1479 322
            $mapping['inversedBy'] = null;
1480
        }
1481
1482 349
        $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
1483
1484
        // unset optional indexBy attribute if its empty
1485 349
        if ( ! isset($mapping['indexBy']) || !$mapping['indexBy']) {
1486 346
            unset($mapping['indexBy']);
1487
        }
1488
1489
        // If targetEntity is unqualified, assume it is in the same namespace as
1490
        // the sourceEntity.
1491 349
        $mapping['sourceEntity'] = $this->name;
1492
1493 349
        if (isset($mapping['targetEntity'])) {
1494 349
            $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']);
1495 349
            $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
1496
        }
1497
1498 349
        if (($mapping['type'] & self::MANY_TO_ONE) > 0 && isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
1499 1
            throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
1500
        }
1501
1502
        // Complete id mapping
1503 348
        if (isset($mapping['id']) && $mapping['id'] === true) {
1504 53
            if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'] == true) {
1505 1
                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
1506
            }
1507
1508 52
            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1509 52
                if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) >= 2) {
1510
                    throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
1511
                        $mapping['targetEntity'], $this->name, $mapping['fieldName']
1512
                    );
1513
                }
1514
1515 52
                $this->identifier[] = $mapping['fieldName'];
1516 52
                $this->containsForeignIdentifier = true;
1517
            }
1518
1519
            // Check for composite key
1520 52
            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1521 24
                $this->isIdentifierComposite = true;
1522
            }
1523
1524 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...
1525 3
                throw CacheException::nonCacheableEntityAssociation($this->name, $mapping['fieldName']);
1526
            }
1527
        }
1528
1529
        // Mandatory attributes for both sides
1530
        // Mandatory: fieldName, targetEntity
1531 344
        if ( ! isset($mapping['fieldName']) || strlen($mapping['fieldName']) == 0) {
1532
            throw MappingException::missingFieldName($this->name);
1533
        }
1534
1535 344
        if ( ! isset($mapping['targetEntity'])) {
1536
            throw MappingException::missingTargetEntity($mapping['fieldName']);
1537
        }
1538
1539
        // Mandatory and optional attributes for either side
1540 344
        if ( ! $mapping['mappedBy']) {
1541 330
            if (isset($mapping['joinTable']) && $mapping['joinTable']) {
1542 119
                if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') {
1543 4
                    $mapping['joinTable']['name']   = trim($mapping['joinTable']['name'], '`');
1544 330
                    $mapping['joinTable']['quoted'] = true;
1545
                }
1546
            }
1547
        } else {
1548 182
            $mapping['isOwningSide'] = false;
1549
        }
1550
1551 344
        if (isset($mapping['id']) && $mapping['id'] === true && $mapping['type'] & self::TO_MANY) {
1552 3
            throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']);
1553
        }
1554
1555
        // Fetch mode. Default fetch mode to LAZY, if not set.
1556 341
        if ( ! isset($mapping['fetch'])) {
1557 95
            $mapping['fetch'] = self::FETCH_LAZY;
1558
        }
1559
1560
        // Cascades
1561 341
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
1562
1563 341
        if (in_array('all', $cascades)) {
1564 36
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1565
        }
1566
1567 341
        if (count($cascades) !== count(array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach')))) {
1568 1
            throw MappingException::invalidCascadeOption(
1569 1
                array_diff($cascades, array_intersect($cascades, array('remove', 'persist', 'refresh', 'merge', 'detach'))),
1570 1
                $this->name,
1571 1
                $mapping['fieldName']
1572
            );
1573
        }
1574
1575 340
        $mapping['cascade'] = $cascades;
1576 340
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1577 340
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1578 340
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1579 340
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1580 340
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1581
1582 340
        return $mapping;
1583
    }
1584
1585
    /**
1586
     * Validates & completes a one-to-one association mapping.
1587
     *
1588
     * @param array $mapping The mapping to validate & complete.
1589
     *
1590
     * @return array The validated & completed mapping.
1591
     *
1592
     * @throws RuntimeException
1593
     * @throws MappingException
1594
     */
1595 296
    protected function _validateAndCompleteOneToOneMapping(array $mapping)
1596
    {
1597 296
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1598
1599 290
        if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
1600 209
            $mapping['isOwningSide'] = true;
1601
        }
1602
1603 290
        if ($mapping['isOwningSide']) {
1604 277
            if ( ! isset($mapping['joinColumns']) || ! $mapping['joinColumns']) {
1605
                // Apply default join column
1606 89
                $mapping['joinColumns'] = array(
1607
                    array(
1608 89
                        '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...
1609 89
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName()
1610
                    )
1611
                );
1612
            }
1613
1614 277
            $uniqueConstraintColumns = array();
1615
1616 277
            foreach ($mapping['joinColumns'] as &$joinColumn) {
1617 277
                if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
1618 150
                    if (count($mapping['joinColumns']) == 1) {
1619 148
                        if ( ! isset($mapping['id']) || ! $mapping['id']) {
1620 148
                            $joinColumn['unique'] = true;
1621
                        }
1622
                    } else {
1623 2
                        $uniqueConstraintColumns[] = $joinColumn['name'];
1624
                    }
1625
                }
1626
1627 277
                if (empty($joinColumn['name'])) {
1628 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...
1629
                }
1630
1631 277
                if (empty($joinColumn['referencedColumnName'])) {
1632 5
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1633
                }
1634
1635 277
                if ($joinColumn['name'][0] === '`') {
1636 7
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1637 7
                    $joinColumn['quoted'] = true;
1638
                }
1639
1640 277
                if ($joinColumn['referencedColumnName'][0] === '`') {
1641 4
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1642 4
                    $joinColumn['quoted']               = true;
1643
                }
1644
1645 277
                $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1646 277
                $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
1647
                    ? $joinColumn['fieldName']
1648 277
                    : $joinColumn['name'];
1649
            }
1650
1651 277
            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...
1652 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...
1653
                    throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
1654
                }
1655
1656 2
                $this->table['uniqueConstraints'][$mapping['fieldName'] . "_uniq"] = array(
1657 2
                    'columns' => $uniqueConstraintColumns
1658
                );
1659
            }
1660
1661 277
            $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
1662
        }
1663
1664 290
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1665 290
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1666
1667 290
        if ($mapping['orphanRemoval']) {
1668 21
            unset($mapping['unique']);
1669
        }
1670
1671 290
        if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
1672 2
            throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']);
1673
        }
1674
1675 288
        return $mapping;
1676
    }
1677
1678
    /**
1679
     * Validates & completes a one-to-many association mapping.
1680
     *
1681
     * @param array $mapping The mapping to validate and complete.
1682
     *
1683
     * @return array The validated and completed mapping.
1684
     *
1685
     * @throws MappingException
1686
     * @throws InvalidArgumentException
1687
     */
1688 128
    protected function _validateAndCompleteOneToManyMapping(array $mapping)
1689
    {
1690 128
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1691
1692
        // OneToMany-side MUST be inverse (must have mappedBy)
1693 127
        if ( ! isset($mapping['mappedBy'])) {
1694
            throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
1695
        }
1696
1697 127
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1698 127
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] ? true : $mapping['isCascadeRemove'];
1699
1700 127
        if (isset($mapping['orderBy'])) {
1701 37
            if ( ! is_array($mapping['orderBy'])) {
1702
                throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
1703
            }
1704
        }
1705
1706 127
        return $mapping;
1707
    }
1708
1709
    /**
1710
     * Validates & completes a many-to-many association mapping.
1711
     *
1712
     * @param array $mapping The mapping to validate & complete.
1713
     *
1714
     * @return array The validated & completed mapping.
1715
     *
1716
     * @throws \InvalidArgumentException
1717
     */
1718 149
    protected function _validateAndCompleteManyToManyMapping(array $mapping)
1719
    {
1720 149
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1721
1722 147
        if ($mapping['isOwningSide']) {
1723
            // owning side MUST have a join table
1724 129
            if ( ! isset($mapping['joinTable']['name'])) {
1725 22
                $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']);
1726
            }
1727
1728 129
            $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity']
1729 129
                && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns'])));
1730
1731 129
            if ( ! isset($mapping['joinTable']['joinColumns'])) {
1732 21
                $mapping['joinTable']['joinColumns'] = array(
1733
                    array(
1734 21
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null),
1735 21
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1736 21
                        'onDelete' => 'CASCADE'
1737
                    )
1738
                );
1739
            }
1740
1741 129
            if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
1742 22
                $mapping['joinTable']['inverseJoinColumns'] = array(
1743
                    array(
1744 22
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null),
1745 22
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1746 22
                        'onDelete' => 'CASCADE'
1747
                    )
1748
                );
1749
            }
1750
1751 129
            $mapping['joinTableColumns'] = array();
1752
1753 129
            foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
1754 129
                if (empty($joinColumn['name'])) {
1755 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...
1756
                }
1757
1758 129
                if (empty($joinColumn['referencedColumnName'])) {
1759 6
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1760
                }
1761
1762 129
                if ($joinColumn['name'][0] === '`') {
1763 3
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1764 3
                    $joinColumn['quoted'] = true;
1765
                }
1766
1767 129
                if ($joinColumn['referencedColumnName'][0] === '`') {
1768 3
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1769 3
                    $joinColumn['quoted']               = true;
1770
                }
1771
1772 129
                if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
1773 30
                    $mapping['isOnDeleteCascade'] = true;
1774
                }
1775
1776 129
                $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1777 129
                $mapping['joinTableColumns'][] = $joinColumn['name'];
1778
            }
1779
1780 129
            foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
1781 129
                if (empty($inverseJoinColumn['name'])) {
1782 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...
1783
                }
1784
1785 129
                if (empty($inverseJoinColumn['referencedColumnName'])) {
1786 6
                    $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1787
                }
1788
1789 129
                if ($inverseJoinColumn['name'][0] === '`') {
1790 3
                    $inverseJoinColumn['name']   = trim($inverseJoinColumn['name'], '`');
1791 3
                    $inverseJoinColumn['quoted'] = true;
1792
                }
1793
1794 129
                if ($inverseJoinColumn['referencedColumnName'][0] === '`') {
1795 3
                    $inverseJoinColumn['referencedColumnName']  = trim($inverseJoinColumn['referencedColumnName'], '`');
1796 3
                    $inverseJoinColumn['quoted']                = true;
1797
                }
1798
1799 129
                if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
1800 26
                    $mapping['isOnDeleteCascade'] = true;
1801
                }
1802
1803 129
                $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
1804 129
                $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
1805
            }
1806
        }
1807
1808 147
        $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) ? (bool) $mapping['orphanRemoval'] : false;
1809
1810 147
        if (isset($mapping['orderBy'])) {
1811 3
            if ( ! is_array($mapping['orderBy'])) {
1812
                throw new InvalidArgumentException("'orderBy' is expected to be an array, not ".gettype($mapping['orderBy']));
1813
            }
1814
        }
1815
1816 147
        return $mapping;
1817
    }
1818
1819
    /**
1820
     * {@inheritDoc}
1821
     */
1822 593
    public function getIdentifierFieldNames()
1823
    {
1824 593
        return $this->identifier;
1825
    }
1826
1827
    /**
1828
     * Gets the name of the single id field. Note that this only works on
1829
     * entity classes that have a single-field pk.
1830
     *
1831
     * @return string
1832
     *
1833
     * @throws MappingException If the class has a composite primary key.
1834
     */
1835 397
    public function getSingleIdentifierFieldName()
1836
    {
1837 397
        if ($this->isIdentifierComposite) {
1838 1
            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
1839
        }
1840
1841 396
        return $this->identifier[0];
1842
    }
1843
1844
    /**
1845
     * Gets the column name of the single id column. Note that this only works on
1846
     * entity classes that have a single-field pk.
1847
     *
1848
     * @return string
1849
     *
1850
     * @throws MappingException If the class has a composite primary key.
1851
     */
1852 3
    public function getSingleIdentifierColumnName()
1853
    {
1854 3
        return $this->getColumnName($this->getSingleIdentifierFieldName());
1855
    }
1856
1857
    /**
1858
     * INTERNAL:
1859
     * Sets the mapped identifier/primary key fields of this class.
1860
     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1861
     *
1862
     * @param array $identifier
1863
     *
1864
     * @return void
1865
     */
1866 123
    public function setIdentifier(array $identifier)
1867
    {
1868 123
        $this->identifier = $identifier;
1869 123
        $this->isIdentifierComposite = (count($this->identifier) > 1);
1870 123
    }
1871
1872
    /**
1873
     * {@inheritDoc}
1874
     */
1875 61
    public function getIdentifier()
1876
    {
1877 61
        return $this->identifier;
1878
    }
1879
1880
    /**
1881
     * {@inheritDoc}
1882
     */
1883 1080
    public function hasField($fieldName)
1884
    {
1885 1080
        return isset($this->fieldMappings[$fieldName]);
1886
    }
1887
1888
    /**
1889
     * Gets an array containing all the column names.
1890
     *
1891
     * @param array|null $fieldNames
1892
     *
1893
     * @return array
1894
     */
1895 42
    public function getColumnNames(array $fieldNames = null)
1896
    {
1897 42
        if (null === $fieldNames) {
1898 41
            return array_keys($this->fieldNames);
1899
        }
1900
1901 1
        return array_values(array_map([$this, 'getColumnName'], $fieldNames));
1902
    }
1903
1904
    /**
1905
     * Returns an array with all the identifier column names.
1906
     *
1907
     * @return array
1908
     */
1909 322
    public function getIdentifierColumnNames()
1910
    {
1911 322
        $columnNames = array();
1912
1913 322
        foreach ($this->identifier as $idProperty) {
1914 322
            if (isset($this->fieldMappings[$idProperty])) {
1915 318
                $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
1916
1917 318
                continue;
1918
            }
1919
1920
            // Association defined as Id field
1921 22
            $joinColumns      = $this->associationMappings[$idProperty]['joinColumns'];
1922
            $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
1923
1924 22
            $columnNames = array_merge($columnNames, $assocColumnNames);
1925
        }
1926
1927 322
        return $columnNames;
1928
    }
1929
1930
    /**
1931
     * Sets the type of Id generator to use for the mapped class.
1932
     *
1933
     * @param int $generatorType
1934
     *
1935
     * @return void
1936
     */
1937 456
    public function setIdGeneratorType($generatorType)
1938
    {
1939 456
        $this->generatorType = $generatorType;
1940 456
    }
1941
1942
    /**
1943
     * Checks whether the mapped class uses an Id generator.
1944
     *
1945
     * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
1946
     */
1947 391
    public function usesIdGenerator()
1948
    {
1949 391
        return $this->generatorType != self::GENERATOR_TYPE_NONE;
1950
    }
1951
1952
    /**
1953
     * @return boolean
1954
     */
1955 1323
    public function isInheritanceTypeNone()
1956
    {
1957 1323
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1958
    }
1959
1960
    /**
1961
     * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
1962
     *
1963
     * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
1964
     *                 FALSE otherwise.
1965
     */
1966 1049
    public function isInheritanceTypeJoined()
1967
    {
1968 1049
        return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
1969
    }
1970
1971
    /**
1972
     * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
1973
     *
1974
     * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
1975
     *                 FALSE otherwise.
1976
     */
1977 1221
    public function isInheritanceTypeSingleTable()
1978
    {
1979 1221
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
1980
    }
1981
1982
    /**
1983
     * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
1984
     *
1985
     * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
1986
     *                 FALSE otherwise.
1987
     */
1988 259
    public function isInheritanceTypeTablePerClass()
1989
    {
1990 259
        return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
1991
    }
1992
1993
    /**
1994
     * Checks whether the class uses an identity column for the Id generation.
1995
     *
1996
     * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
1997
     */
1998 1062
    public function isIdGeneratorIdentity()
1999
    {
2000 1062
        return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
2001
    }
2002
2003
    /**
2004
     * Checks whether the class uses a sequence for id generation.
2005
     *
2006
     * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
2007
     */
2008 312
    public function isIdGeneratorSequence()
2009
    {
2010 312
        return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
2011
    }
2012
2013
    /**
2014
     * Checks whether the class uses a table for id generation.
2015
     *
2016
     * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
2017
     */
2018 80
    public function isIdGeneratorTable()
2019
    {
2020 80
        return $this->generatorType == self::GENERATOR_TYPE_TABLE;
2021
    }
2022
2023
    /**
2024
     * Checks whether the class has a natural identifier/pk (which means it does
2025
     * not use any Id generator.
2026
     *
2027
     * @return boolean
2028
     */
2029 73
    public function isIdentifierNatural()
2030
    {
2031 73
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
2032
    }
2033
2034
    /**
2035
     * Checks whether the class use a UUID for id generation.
2036
     *
2037
     * @return boolean
2038
     */
2039
    public function isIdentifierUuid()
2040
    {
2041
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
2042
    }
2043
2044
    /**
2045
     * Gets the type of a field.
2046
     *
2047
     * @param string $fieldName
2048
     *
2049
     * @return \Doctrine\DBAL\Types\Type|string|null
2050
     *
2051
     * @todo 3.0 Remove this. PersisterHelper should fix it somehow
2052
     */
2053 38
    public function getTypeOfField($fieldName)
2054
    {
2055 38
        return isset($this->fieldMappings[$fieldName])
2056 38
            ? $this->fieldMappings[$fieldName]['type']
2057 38
            : null;
2058
    }
2059
2060
    /**
2061
     * Gets the type of a column.
2062
     *
2063
     * @param string $columnName
2064
     *
2065
     * @return \Doctrine\DBAL\Types\Type|string|null
2066
     *
2067
     * @deprecated 3.0 remove this. this method is bogous and unreliable, since it cannot resolve the type of a column
2068
     *             that is derived by a referenced field on a different entity.
2069
     */
2070
    public function getTypeOfColumn($columnName)
2071
    {
2072
        return $this->getTypeOfField($this->getFieldName($columnName));
2073
    }
2074
2075
    /**
2076
     * Gets the name of the primary table.
2077
     *
2078
     * @return string
2079
     */
2080 1464
    public function getTableName()
2081
    {
2082 1464
        return $this->table['name'];
2083
    }
2084
2085
    /**
2086
     * Gets primary table's schema name.
2087
     *
2088
     * @return string|null
2089
     */
2090 13
    public function getSchemaName()
2091
    {
2092 13
        return isset($this->table['schema']) ? $this->table['schema'] : null;
2093
    }
2094
2095
    /**
2096
     * Gets the table name to use for temporary identifier tables of this class.
2097
     *
2098
     * @return string
2099
     */
2100 7
    public function getTemporaryIdTableName()
2101
    {
2102
        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
2103 7
        return str_replace('.', '_', $this->getTableName() . '_id_tmp');
2104
    }
2105
2106
    /**
2107
     * Sets the mapped subclasses of this class.
2108
     *
2109
     * @param array $subclasses The names of all mapped subclasses.
2110
     *
2111
     * @return void
2112
     */
2113 2
    public function setSubclasses(array $subclasses)
2114
    {
2115 2
        foreach ($subclasses as $subclass) {
2116 2
            $this->subClasses[] = $this->fullyQualifiedClassName($subclass);
2117
        }
2118 2
    }
2119
2120
    /**
2121
     * Sets the parent class names.
2122
     * Assumes that the class names in the passed array are in the order:
2123
     * directParent -> directParentParent -> directParentParentParent ... -> root.
2124
     *
2125
     * @param array $classNames
2126
     *
2127
     * @return void
2128
     */
2129 408
    public function setParentClasses(array $classNames)
2130
    {
2131 408
        $this->parentClasses = $classNames;
2132
2133 408
        if (count($classNames) > 0) {
2134 79
            $this->rootEntityName = array_pop($classNames);
2135
        }
2136 408
    }
2137
2138
    /**
2139
     * Sets the inheritance type used by the class and its subclasses.
2140
     *
2141
     * @param integer $type
2142
     *
2143
     * @return void
2144
     *
2145
     * @throws MappingException
2146
     */
2147 169
    public function setInheritanceType($type)
2148
    {
2149 169
        if ( ! $this->_isInheritanceType($type)) {
2150
            throw MappingException::invalidInheritanceType($this->name, $type);
2151
        }
2152
2153 169
        $this->inheritanceType = $type;
2154 169
    }
2155
2156
    /**
2157
     * Sets the association to override association mapping of property for an entity relationship.
2158
     *
2159
     * @param string $fieldName
2160
     * @param array  $overrideMapping
2161
     *
2162
     * @return void
2163
     *
2164
     * @throws MappingException
2165
     */
2166 20
    public function setAssociationOverride($fieldName, array $overrideMapping)
2167
    {
2168 20
        if ( ! isset($this->associationMappings[$fieldName])) {
2169 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2170
        }
2171
2172 19
        $mapping = $this->associationMappings[$fieldName];
2173
2174 19
        if (isset($overrideMapping['joinColumns'])) {
2175 13
            $mapping['joinColumns'] = $overrideMapping['joinColumns'];
2176
        }
2177
2178 19
        if (isset($overrideMapping['inversedBy'])) {
2179 6
            $mapping['inversedBy'] = $overrideMapping['inversedBy'];
2180
        }
2181
2182 19
        if (isset($overrideMapping['joinTable'])) {
2183 12
            $mapping['joinTable'] = $overrideMapping['joinTable'];
2184
        }
2185
2186 19
        $mapping['joinColumnFieldNames']        = null;
2187 19
        $mapping['joinTableColumns']            = null;
2188 19
        $mapping['sourceToTargetKeyColumns']    = null;
2189 19
        $mapping['relationToSourceKeyColumns']  = null;
2190 19
        $mapping['relationToTargetKeyColumns']  = null;
2191
2192 19
        switch ($mapping['type']) {
2193 19
            case self::ONE_TO_ONE:
2194 1
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2195 1
                break;
2196 18
            case self::ONE_TO_MANY:
2197
                $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2198
                break;
2199 18
            case self::MANY_TO_ONE:
2200 12
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2201 12
                break;
2202 18
            case self::MANY_TO_MANY:
2203 18
                $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2204 18
                break;
2205
        }
2206
2207 19
        $this->associationMappings[$fieldName] = $mapping;
2208 19
    }
2209
2210
    /**
2211
     * Sets the override for a mapped field.
2212
     *
2213
     * @param string $fieldName
2214
     * @param array  $overrideMapping
2215
     *
2216
     * @return void
2217
     *
2218
     * @throws MappingException
2219
     */
2220 15
    public function setAttributeOverride($fieldName, array $overrideMapping)
2221
    {
2222 15
        if ( ! isset($this->fieldMappings[$fieldName])) {
2223 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2224
        }
2225
2226 14
        $mapping = $this->fieldMappings[$fieldName];
2227
2228 14
        if (isset($mapping['id'])) {
2229 12
            $overrideMapping['id'] = $mapping['id'];
2230
        }
2231
2232 14
        if ( ! isset($overrideMapping['type']) || $overrideMapping['type'] === null) {
2233 6
            $overrideMapping['type'] = $mapping['type'];
2234
        }
2235
2236 14
        if ( ! isset($overrideMapping['fieldName']) || $overrideMapping['fieldName'] === null) {
2237 5
            $overrideMapping['fieldName'] = $mapping['fieldName'];
2238
        }
2239
2240 14
        if ($overrideMapping['type'] !== $mapping['type']) {
2241 1
            throw MappingException::invalidOverrideFieldType($this->name, $fieldName);
2242
        }
2243
2244 13
        unset($this->fieldMappings[$fieldName]);
2245 13
        unset($this->fieldNames[$mapping['columnName']]);
2246 13
        unset($this->columnNames[$mapping['fieldName']]);
2247
2248 13
        $this->_validateAndCompleteFieldMapping($overrideMapping);
2249
2250 13
        $this->fieldMappings[$fieldName] = $overrideMapping;
2251 13
    }
2252
2253
    /**
2254
     * Checks whether a mapped field is inherited from an entity superclass.
2255
     *
2256
     * @param string $fieldName
2257
     *
2258
     * @return bool TRUE if the field is inherited, FALSE otherwise.
2259
     */
2260 368
    public function isInheritedField($fieldName)
2261
    {
2262 368
        return isset($this->fieldMappings[$fieldName]['inherited']);
2263
    }
2264
2265
    /**
2266
     * Checks if this entity is the root in any entity-inheritance-hierarchy.
2267
     *
2268
     * @return bool
2269
     */
2270 407
    public function isRootEntity()
2271
    {
2272 407
        return $this->name == $this->rootEntityName;
2273
    }
2274
2275
    /**
2276
     * Checks whether a mapped association field is inherited from a superclass.
2277
     *
2278
     * @param string $fieldName
2279
     *
2280
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
2281
     */
2282 347
    public function isInheritedAssociation($fieldName)
2283
    {
2284 347
        return isset($this->associationMappings[$fieldName]['inherited']);
2285
    }
2286
2287 347
    public function isInheritedEmbeddedClass($fieldName)
2288
    {
2289 347
        return isset($this->embeddedClasses[$fieldName]['inherited']);
2290
    }
2291
2292
    /**
2293
     * Sets the name of the primary table the class is mapped to.
2294
     *
2295
     * @param string $tableName The table name.
2296
     *
2297
     * @return void
2298
     *
2299
     * @deprecated Use {@link setPrimaryTable}.
2300
     */
2301
    public function setTableName($tableName)
2302
    {
2303
        $this->table['name'] = $tableName;
2304
    }
2305
2306
    /**
2307
     * Sets the primary table definition. The provided array supports the
2308
     * following structure:
2309
     *
2310
     * name => <tableName> (optional, defaults to class name)
2311
     * indexes => array of indexes (optional)
2312
     * uniqueConstraints => array of constraints (optional)
2313
     *
2314
     * If a key is omitted, the current value is kept.
2315
     *
2316
     * @param array $table The table description.
2317
     *
2318
     * @return void
2319
     */
2320 323
    public function setPrimaryTable(array $table)
2321
    {
2322 323
        if (isset($table['name'])) {
2323
            // Split schema and table name from a table name like "myschema.mytable"
2324 260
            if (strpos($table['name'], '.') !== false) {
2325 9
                list($this->table['schema'], $table['name']) = explode('.', $table['name'], 2);
2326
            }
2327
2328 260
            if ($table['name'][0] === '`') {
2329 17
                $table['name']          = trim($table['name'], '`');
2330 17
                $this->table['quoted']  = true;
2331
            }
2332
2333 260
            $this->table['name'] = $table['name'];
2334
        }
2335
2336 323
        if (isset($table['schema'])) {
2337 6
            $this->table['schema'] = $table['schema'];
2338
        }
2339
2340 323
        if (isset($table['indexes'])) {
2341 15
            $this->table['indexes'] = $table['indexes'];
2342
        }
2343
2344 323
        if (isset($table['uniqueConstraints'])) {
2345 8
            $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
2346
        }
2347
2348 323
        if (isset($table['options'])) {
2349 9
            $this->table['options'] = $table['options'];
2350
        }
2351 323
    }
2352
2353
    /**
2354
     * Checks whether the given type identifies an inheritance type.
2355
     *
2356
     * @param integer $type
2357
     *
2358
     * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
2359
     */
2360 169
    private function _isInheritanceType($type)
2361
    {
2362 169
        return $type == self::INHERITANCE_TYPE_NONE ||
2363 105
                $type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
2364 50
                $type == self::INHERITANCE_TYPE_JOINED ||
2365 169
                $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
2366
    }
2367
2368
    /**
2369
     * Adds a mapped field to the class.
2370
     *
2371
     * @param array $mapping The field mapping.
2372
     *
2373
     * @return void
2374
     *
2375
     * @throws MappingException
2376
     */
2377 527
    public function mapField(array $mapping)
2378
    {
2379 527
        $this->_validateAndCompleteFieldMapping($mapping);
2380 525
        $this->assertFieldNotMapped($mapping['fieldName']);
2381
2382 524
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
2383 524
    }
2384
2385
    /**
2386
     * INTERNAL:
2387
     * Adds an association mapping without completing/validating it.
2388
     * This is mainly used to add inherited association mappings to derived classes.
2389
     *
2390
     * @param array $mapping
2391
     *
2392
     * @return void
2393
     *
2394
     * @throws MappingException
2395
     */
2396 48
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
2397
    {
2398 48
        if (isset($this->associationMappings[$mapping['fieldName']])) {
2399 1
            throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
2400
        }
2401 48
        $this->associationMappings[$mapping['fieldName']] = $mapping;
2402 48
    }
2403
2404
    /**
2405
     * INTERNAL:
2406
     * Adds a field mapping without completing/validating it.
2407
     * This is mainly used to add inherited field mappings to derived classes.
2408
     *
2409
     * @param array $fieldMapping
2410
     *
2411
     * @return void
2412
     */
2413 107
    public function addInheritedFieldMapping(array $fieldMapping)
2414
    {
2415 107
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
2416 107
        $this->columnNames[$fieldMapping['fieldName']] = $fieldMapping['columnName'];
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ORM\Mapping\Cla...adataInfo::$columnNames has been deprecated with message: 3.0 Remove this.

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

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

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