Completed
Pull Request — master (#6151)
by Andy
09:42
created

ClassMetadataInfo::removeFieldMapping()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading history...
899
            $serialized[] = 'cache';
900
        }
901
902 6
        return $serialized;
903
    }
904
905
    /**
906
     * Creates a new instance of the mapped class, without invoking the constructor.
907
     *
908
     * @return object
909
     */
910 680
    public function newInstance()
911
    {
912 680
        return $this->instantiator->instantiate($this->name);
913
    }
914
915
    /**
916
     * Restores some state that can not be serialized/unserialized.
917
     *
918
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
919
     *
920
     * @return void
921
     */
922 2033
    public function wakeupReflection($reflService)
923
    {
924
        // Restore ReflectionClass and properties
925 2033
        $this->reflClass    = $reflService->getClass($this->name);
926 2033
        $this->instantiator = $this->instantiator ?: new Instantiator();
927
928 2033
        $parentReflFields = array();
929
930 2033
        foreach ($this->embeddedClasses as $property => $embeddedClass) {
931 21
            if (isset($embeddedClass['declaredField'])) {
932 15
                $parentReflFields[$property] = new ReflectionEmbeddedProperty(
933 15
                    $parentReflFields[$embeddedClass['declaredField']],
934 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...
935 15
                        $this->embeddedClasses[$embeddedClass['declaredField']]['class'],
936 15
                        $embeddedClass['originalField']
937
                    ),
938 15
                    $this->embeddedClasses[$embeddedClass['declaredField']]['class']
939
                );
940
941 15
                continue;
942
            }
943
944 21
            $fieldRefl = $reflService->getAccessibleProperty(
945 21
                isset($embeddedClass['declared']) ? $embeddedClass['declared'] : $this->name,
946
                $property
947
            );
948
949 21
            $parentReflFields[$property] = $fieldRefl;
950 21
            $this->reflFields[$property] = $fieldRefl;
951
        }
952
953 2033
        foreach ($this->fieldMappings as $field => $mapping) {
954 2028
            if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
955 20
                $this->reflFields[$field] = new ReflectionEmbeddedProperty(
956 20
                    $parentReflFields[$mapping['declaredField']],
957 20
                    $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...
958 20
                    $mapping['originalClass']
959
                );
960 20
                continue;
961
            }
962
963 2028
            $this->reflFields[$field] = isset($mapping['declared'])
964 503
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
965 2028
                : $reflService->getAccessibleProperty($this->name, $field);
966
        }
967
968 2033
        foreach ($this->associationMappings as $field => $mapping) {
969 1704
            $this->reflFields[$field] = isset($mapping['declared'])
970 406
                ? $reflService->getAccessibleProperty($mapping['declared'], $field)
971 1704
                : $reflService->getAccessibleProperty($this->name, $field);
972
        }
973 2033
    }
974
975
    /**
976
     * Initializes a new ClassMetadata instance that will hold the object-relational mapping
977
     * metadata of the class with the given name.
978
     *
979
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService The reflection service.
980
     *
981
     * @return void
982
     */
983 647
    public function initializeReflection($reflService)
984
    {
985 647
        $this->reflClass = $reflService->getClass($this->name);
986 647
        $this->namespace = $reflService->getClassNamespace($this->name);
987
988 647
        if ($this->reflClass) {
989 640
            $this->name = $this->rootEntityName = $this->reflClass->getName();
0 ignored issues
show
Bug introduced by
Consider using $this->reflClass->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
990
        }
991
992 647
        $this->table['name'] = $this->namingStrategy->classToTableName($this->name);
993 647
    }
994
995
    /**
996
     * Validates Identifier.
997
     *
998
     * @return void
999
     *
1000
     * @throws MappingException
1001
     */
1002 420
    public function validateIdentifier()
1003
    {
1004 420
        if ($this->isMappedSuperclass || $this->isEmbeddedClass) {
1005 60
            return;
1006
        }
1007
1008
        // Verify & complete identifier mapping
1009 418
        if ( ! $this->identifier) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->identifier of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1010 6
            throw MappingException::identifierRequired($this->name);
1011
        }
1012
1013 412
        if ($this->usesIdGenerator() && $this->isIdentifierComposite) {
1014
            throw MappingException::compositeKeyAssignedIdGeneratorRequired($this->name);
1015
        }
1016 412
    }
1017
1018
    /**
1019
     * Validates association targets actually exist.
1020
     *
1021
     * @return void
1022
     *
1023
     * @throws MappingException
1024
     */
1025 421
    public function validateAssociations()
1026
    {
1027 421
        foreach ($this->associationMappings as $mapping) {
1028 273
            if ( ! ClassLoader::classExists($mapping['targetEntity']) ) {
1029 273
                throw MappingException::invalidTargetEntityClass($mapping['targetEntity'], $this->name, $mapping['fieldName']);
1030
            }
1031
        }
1032 420
    }
1033
1034
    /**
1035
     * Validates lifecycle callbacks.
1036
     *
1037
     * @param \Doctrine\Common\Persistence\Mapping\ReflectionService $reflService
1038
     *
1039
     * @return void
1040
     *
1041
     * @throws MappingException
1042
     */
1043 421
    public function validateLifecycleCallbacks($reflService)
1044
    {
1045 421
        foreach ($this->lifecycleCallbacks as $callbacks) {
1046 13
            foreach ($callbacks as $callbackFuncName) {
1047 13
                if ( ! $reflService->hasPublicMethod($this->name, $callbackFuncName)) {
1048 13
                    throw MappingException::lifecycleCallbackMethodNotFound($this->name, $callbackFuncName);
1049
                }
1050
            }
1051
        }
1052 420
    }
1053
1054
    /**
1055
     * {@inheritDoc}
1056
     */
1057 555
    public function getReflectionClass()
1058
    {
1059 555
        return $this->reflClass;
1060
    }
1061
1062
    /**
1063
     * @param array $cache
1064
     *
1065
     * @return void
1066
     */
1067 21
    public function enableCache(array $cache)
1068
    {
1069 21
        if ( ! isset($cache['usage'])) {
1070
            $cache['usage'] = self::CACHE_USAGE_READ_ONLY;
1071
        }
1072
1073 21
        if ( ! isset($cache['region'])) {
1074 21
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName));
1075
        }
1076
1077 21
        $this->cache = $cache;
1078 21
    }
1079
1080
    /**
1081
     * @param string $fieldName
1082
     * @param array  $cache
1083
     *
1084
     * @return void
1085
     */
1086 2
    public function enableAssociationCache($fieldName, array $cache)
1087
    {
1088 2
        $this->associationMappings[$fieldName]['cache'] = $this->getAssociationCacheDefaults($fieldName, $cache);
1089 2
    }
1090
1091
    /**
1092
     * @param string $fieldName
1093
     * @param array  $cache
1094
     *
1095
     * @return array
1096
     */
1097 17
    public function getAssociationCacheDefaults($fieldName, array $cache)
1098
    {
1099 17
        if ( ! isset($cache['usage'])) {
1100 1
            $cache['usage'] = isset($this->cache['usage'])
1101 1
                ? $this->cache['usage']
1102
                : self::CACHE_USAGE_READ_ONLY;
1103
        }
1104
1105 17
        if ( ! isset($cache['region'])) {
1106 17
            $cache['region'] = strtolower(str_replace('\\', '_', $this->rootEntityName)) . '__' . $fieldName;
1107
        }
1108
1109 17
        return $cache;
1110
    }
1111
1112
    /**
1113
     * Sets the change tracking policy used by this class.
1114
     *
1115
     * @param integer $policy
1116
     *
1117
     * @return void
1118
     */
1119 143
    public function setChangeTrackingPolicy($policy)
1120
    {
1121 143
        $this->changeTrackingPolicy = $policy;
1122 143
    }
1123
1124
    /**
1125
     * Whether the change tracking policy of this class is "deferred explicit".
1126
     *
1127
     * @return boolean
1128
     */
1129 268
    public function isChangeTrackingDeferredExplicit()
1130
    {
1131 268
        return self::CHANGETRACKING_DEFERRED_EXPLICIT === $this->changeTrackingPolicy;
1132
    }
1133
1134
    /**
1135
     * Whether the change tracking policy of this class is "deferred implicit".
1136
     *
1137
     * @return boolean
1138
     */
1139 463
    public function isChangeTrackingDeferredImplicit()
1140
    {
1141 463
        return self::CHANGETRACKING_DEFERRED_IMPLICIT === $this->changeTrackingPolicy;
1142
    }
1143
1144
    /**
1145
     * Whether the change tracking policy of this class is "notify".
1146
     *
1147
     * @return boolean
1148
     */
1149 297
    public function isChangeTrackingNotify()
1150
    {
1151 297
        return self::CHANGETRACKING_NOTIFY === $this->changeTrackingPolicy;
1152
    }
1153
1154
    /**
1155
     * Checks whether a field is part of the identifier/primary key field(s).
1156
     *
1157
     * @param string $fieldName The field name.
1158
     *
1159
     * @return boolean TRUE if the field is part of the table identifier/primary key field(s),
1160
     *                 FALSE otherwise.
1161
     */
1162 1080
    public function isIdentifier($fieldName)
1163
    {
1164 1080
        if ( ! $this->identifier) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->identifier of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1165 1
            return false;
1166
        }
1167
1168 1079
        if ( ! $this->isIdentifierComposite) {
1169 1074
            return $fieldName === $this->identifier[0];
1170
        }
1171
1172 94
        return in_array($fieldName, $this->identifier, true);
1173
    }
1174
1175
    /**
1176
     * Checks if the field is unique.
1177
     *
1178
     * @param string $fieldName The field name.
1179
     *
1180
     * @return boolean TRUE if the field is unique, FALSE otherwise.
1181
     */
1182
    public function isUniqueField($fieldName)
1183
    {
1184
        $mapping = $this->getFieldMapping($fieldName);
1185
1186
        return false !== $mapping && isset($mapping['unique']) && $mapping['unique'];
1187
    }
1188
1189
    /**
1190
     * Checks if the field is not null.
1191
     *
1192
     * @param string $fieldName The field name.
1193
     *
1194
     * @return boolean TRUE if the field is not null, FALSE otherwise.
1195
     */
1196 1
    public function isNullable($fieldName)
1197
    {
1198 1
        $mapping = $this->getFieldMapping($fieldName);
1199
1200 1
        return false !== $mapping && isset($mapping['nullable']) && $mapping['nullable'];
1201
    }
1202
1203
    /**
1204
     * Gets a column name for a field name.
1205
     * If the column name for the field cannot be found, the given field name
1206
     * is returned.
1207
     *
1208
     * @param string $fieldName The field name.
1209
     *
1210
     * @return string The column name.
1211
     */
1212 16
    public function getColumnName($fieldName)
1213
    {
1214 16
        return isset($this->columnNames[$fieldName])
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ORM\Mapping\Cla...adataInfo::$columnNames has been deprecated 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...
1215 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...
1216 16
            : $fieldName;
1217
    }
1218
1219
    /**
1220
     * Gets the mapping of a (regular) field that holds some data but not a
1221
     * reference to another object.
1222
     *
1223
     * @param string $fieldName The field name.
1224
     *
1225
     * @return array The field mapping.
1226
     *
1227
     * @throws MappingException
1228
     */
1229 200
    public function getFieldMapping($fieldName)
1230
    {
1231 200
        if ( ! isset($this->fieldMappings[$fieldName])) {
1232 1
            throw MappingException::mappingNotFound($this->name, $fieldName);
1233
        }
1234
1235 199
        return $this->fieldMappings[$fieldName];
1236
    }
1237
1238
    /**
1239
     * Gets the mapping of an association.
1240
     *
1241
     * @see ClassMetadataInfo::$associationMappings
1242
     *
1243
     * @param string $fieldName The field name that represents the association in
1244
     *                          the object model.
1245
     *
1246
     * @return array The mapping.
1247
     *
1248
     * @throws MappingException
1249
     */
1250 489
    public function getAssociationMapping($fieldName)
1251
    {
1252 489
        if ( ! isset($this->associationMappings[$fieldName])) {
1253
            throw MappingException::mappingNotFound($this->name, $fieldName);
1254
        }
1255
1256 489
        return $this->associationMappings[$fieldName];
1257
    }
1258
1259
    /**
1260
     * Gets all association mappings of the class.
1261
     *
1262
     * @return array
1263
     */
1264
    public function getAssociationMappings()
1265
    {
1266
        return $this->associationMappings;
1267
    }
1268
1269
    /**
1270
     * Gets the field name for a column name.
1271
     * If no field name can be found the column name is returned.
1272
     *
1273
     * @param string $columnName The column name.
1274
     *
1275
     * @return string The column alias.
1276
     */
1277 238
    public function getFieldName($columnName)
1278
    {
1279 238
        return isset($this->fieldNames[$columnName])
1280 238
            ? $this->fieldNames[$columnName]
1281 238
            : $columnName;
1282
    }
1283
1284
    /**
1285
     * Gets the named query.
1286
     *
1287
     * @see ClassMetadataInfo::$namedQueries
1288
     *
1289
     * @param string $queryName The query name.
1290
     *
1291
     * @return string
1292
     *
1293
     * @throws MappingException
1294
     */
1295 4
    public function getNamedQuery($queryName)
1296
    {
1297 4
        if ( ! isset($this->namedQueries[$queryName])) {
1298 1
            throw MappingException::queryNotFound($this->name, $queryName);
1299
        }
1300
1301 3
        return $this->namedQueries[$queryName]['dql'];
1302
    }
1303
1304
    /**
1305
     * Gets all named queries of the class.
1306
     *
1307
     * @return array
1308
     */
1309 7
    public function getNamedQueries()
1310
    {
1311 7
        return $this->namedQueries;
1312
    }
1313
1314
    /**
1315
     * Gets the named native query.
1316
     *
1317
     * @see ClassMetadataInfo::$namedNativeQueries
1318
     *
1319
     * @param string $queryName The query name.
1320
     *
1321
     * @return array
1322
     *
1323
     * @throws MappingException
1324
     */
1325 17
    public function getNamedNativeQuery($queryName)
1326
    {
1327 17
        if ( ! isset($this->namedNativeQueries[$queryName])) {
1328
            throw MappingException::queryNotFound($this->name, $queryName);
1329
        }
1330
1331 17
        return $this->namedNativeQueries[$queryName];
1332
    }
1333
1334
    /**
1335
     * Gets all named native queries of the class.
1336
     *
1337
     * @return array
1338
     */
1339 2
    public function getNamedNativeQueries()
1340
    {
1341 2
        return $this->namedNativeQueries;
1342
    }
1343
1344
    /**
1345
     * Gets the result set mapping.
1346
     *
1347
     * @see ClassMetadataInfo::$sqlResultSetMappings
1348
     *
1349
     * @param string $name The result set mapping name.
1350
     *
1351
     * @return array
1352
     *
1353
     * @throws MappingException
1354
     */
1355 21
    public function getSqlResultSetMapping($name)
1356
    {
1357 21
        if ( ! isset($this->sqlResultSetMappings[$name])) {
1358
            throw MappingException::resultMappingNotFound($this->name, $name);
1359
        }
1360
1361 21
        return $this->sqlResultSetMappings[$name];
1362
    }
1363
1364
    /**
1365
     * Gets all sql result set mappings of the class.
1366
     *
1367
     * @return array
1368
     */
1369 8
    public function getSqlResultSetMappings()
1370
    {
1371 8
        return $this->sqlResultSetMappings;
1372
    }
1373
1374
    /**
1375
     * Validates & completes the given field mapping.
1376
     *
1377
     * @param array $mapping The field mapping to validate & complete.
1378
     *
1379
     * @return array The validated and completed field mapping.
1380
     *
1381
     * @throws MappingException
1382
     */
1383 566
    protected function _validateAndCompleteFieldMapping(array &$mapping)
1384
    {
1385
        // Check mandatory fields
1386 566
        if ( ! isset($mapping['fieldName']) || !$mapping['fieldName']) {
1387 1
            throw MappingException::missingFieldName($this->name);
1388
        }
1389
1390 565
        if ( ! isset($mapping['type'])) {
1391
            // Default to string
1392 68
            $mapping['type'] = 'string';
1393
        }
1394
1395
        // Complete fieldName and columnName mapping
1396 565
        if ( ! isset($mapping['columnName'])) {
1397 469
            $mapping['columnName'] = $this->namingStrategy->propertyToColumnName($mapping['fieldName'], $this->name);
1398
        }
1399
1400 565
        if ('`' === $mapping['columnName'][0]) {
1401 11
            $mapping['columnName']  = trim($mapping['columnName'], '`');
1402 11
            $mapping['quoted']      = true;
1403
        }
1404
1405 565
        $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...
1406
1407 565
        if (isset($this->fieldNames[$mapping['columnName']]) || ($this->discriminatorColumn && $this->discriminatorColumn['name'] === $mapping['columnName'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->discriminatorColumn of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1408 2
            throw MappingException::duplicateColumnName($this->name, $mapping['columnName']);
1409
        }
1410
1411 564
        $this->fieldNames[$mapping['columnName']] = $mapping['fieldName'];
1412
1413
        // Complete id mapping
1414 564
        if (isset($mapping['id']) && true === $mapping['id']) {
1415 524
            if ($this->versionField == $mapping['fieldName']) {
1416
                throw MappingException::cannotVersionIdField($this->name, $mapping['fieldName']);
1417
            }
1418
1419 524
            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1420 524
                $this->identifier[] = $mapping['fieldName'];
1421
            }
1422
1423
            // Check for composite key
1424 524
            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1425 22
                $this->isIdentifierComposite = true;
1426
            }
1427
        }
1428
1429 564
        if (Type::hasType($mapping['type']) && Type::getType($mapping['type'])->canRequireSQLConversion()) {
1430 5
            if (isset($mapping['id']) && true === $mapping['id']) {
1431
                 throw MappingException::sqlConversionNotAllowedForIdentifiers($this->name, $mapping['fieldName'], $mapping['type']);
1432
            }
1433
1434 5
            $mapping['requireSQLConversion'] = true;
1435
        }
1436 564
    }
1437
1438
    /**
1439
     * Validates & completes the basic mapping information that is common to all
1440
     * association mappings (one-to-one, many-ot-one, one-to-many, many-to-many).
1441
     *
1442
     * @param array $mapping The mapping.
1443
     *
1444
     * @return array The updated mapping.
1445
     *
1446
     * @throws MappingException If something is wrong with the mapping.
1447
     */
1448 356
    protected function _validateAndCompleteAssociationMapping(array $mapping)
1449
    {
1450 356
        if ( ! isset($mapping['mappedBy'])) {
1451 342
            $mapping['mappedBy'] = null;
1452
        }
1453
1454 356
        if ( ! isset($mapping['inversedBy'])) {
1455 329
            $mapping['inversedBy'] = null;
1456
        }
1457
1458 356
        $mapping['isOwningSide'] = true; // assume owning side until we hit mappedBy
1459
1460 356
        if (empty($mapping['indexBy'])) {
1461 353
            unset($mapping['indexBy']);
1462
        }
1463
1464
        // If targetEntity is unqualified, assume it is in the same namespace as
1465
        // the sourceEntity.
1466 356
        $mapping['sourceEntity'] = $this->name;
1467
1468 356
        if (isset($mapping['targetEntity'])) {
1469 356
            $mapping['targetEntity'] = $this->fullyQualifiedClassName($mapping['targetEntity']);
1470 356
            $mapping['targetEntity'] = ltrim($mapping['targetEntity'], '\\');
1471
        }
1472
1473 356
        if (($mapping['type'] & self::MANY_TO_ONE) > 0 && isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) {
1474 1
            throw MappingException::illegalOrphanRemoval($this->name, $mapping['fieldName']);
1475
        }
1476
1477
        // Complete id mapping
1478 355
        if (isset($mapping['id']) && true === $mapping['id']) {
1479 54
            if (isset($mapping['orphanRemoval']) && $mapping['orphanRemoval']) {
1480 1
                throw MappingException::illegalOrphanRemovalOnIdentifierAssociation($this->name, $mapping['fieldName']);
1481
            }
1482
1483 53
            if ( ! in_array($mapping['fieldName'], $this->identifier)) {
1484 53
                if (isset($mapping['joinColumns']) && count($mapping['joinColumns']) >= 2) {
1485
                    throw MappingException::cannotMapCompositePrimaryKeyEntitiesAsForeignId(
1486
                        $mapping['targetEntity'], $this->name, $mapping['fieldName']
1487
                    );
1488
                }
1489
1490 53
                $this->identifier[] = $mapping['fieldName'];
1491 53
                $this->containsForeignIdentifier = true;
1492
            }
1493
1494
            // Check for composite key
1495 53
            if ( ! $this->isIdentifierComposite && count($this->identifier) > 1) {
1496 25
                $this->isIdentifierComposite = true;
1497
            }
1498
1499 53
            if ($this->cache && !isset($mapping['cache'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->cache of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

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

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

Loading history...
1500 3
                throw CacheException::nonCacheableEntityAssociation($this->name, $mapping['fieldName']);
1501
            }
1502
        }
1503
1504
        // Mandatory attributes for both sides
1505
        // Mandatory: fieldName, targetEntity
1506 351
        if ( ! isset($mapping['fieldName']) || !$mapping['fieldName']) {
1507
            throw MappingException::missingFieldName($this->name);
1508
        }
1509
1510 351
        if ( ! isset($mapping['targetEntity'])) {
1511
            throw MappingException::missingTargetEntity($mapping['fieldName']);
1512
        }
1513
1514
        // Mandatory and optional attributes for either side
1515 351
        if ( ! $mapping['mappedBy']) {
1516 337
            if (isset($mapping['joinTable']) && $mapping['joinTable']) {
1517 124
                if (isset($mapping['joinTable']['name']) && $mapping['joinTable']['name'][0] === '`') {
1518 4
                    $mapping['joinTable']['name']   = trim($mapping['joinTable']['name'], '`');
1519 337
                    $mapping['joinTable']['quoted'] = true;
1520
                }
1521
            }
1522
        } else {
1523 187
            $mapping['isOwningSide'] = false;
1524
        }
1525
1526 351
        if (isset($mapping['id']) && true === $mapping['id'] && $mapping['type'] & self::TO_MANY) {
1527 3
            throw MappingException::illegalToManyIdentifierAssociation($this->name, $mapping['fieldName']);
1528
        }
1529
1530
        // Fetch mode. Default fetch mode to LAZY, if not set.
1531 348
        if ( ! isset($mapping['fetch'])) {
1532 95
            $mapping['fetch'] = self::FETCH_LAZY;
1533
        }
1534
1535
        // Cascades
1536 348
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', $mapping['cascade']) : array();
1537
1538 348
        $allCascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1539 348
        if (in_array('all', $cascades)) {
1540 37
            $cascades = $allCascades;
1541 341
        } elseif (count($cascades) !== count(array_intersect($cascades, $allCascades))) {
1542 1
            throw MappingException::invalidCascadeOption(
1543
                array_diff($cascades, $allCascades),
1544 1
                $this->name,
1545 1
                $mapping['fieldName']
1546
            );
1547
        }
1548
1549 347
        $mapping['cascade'] = $cascades;
1550 347
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1551 347
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1552 347
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1553 347
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1554 347
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1555
1556 347
        return $mapping;
1557
    }
1558
1559
    /**
1560
     * Validates & completes a one-to-one association mapping.
1561
     *
1562
     * @param array $mapping The mapping to validate & complete.
1563
     *
1564
     * @return array The validated & completed mapping.
1565
     *
1566
     * @throws RuntimeException
1567
     * @throws MappingException
1568
     */
1569 303
    protected function _validateAndCompleteOneToOneMapping(array $mapping)
1570
    {
1571 303
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1572
1573 297
        if (isset($mapping['joinColumns']) && $mapping['joinColumns']) {
1574 216
            $mapping['isOwningSide'] = true;
1575
        }
1576
1577 297
        if ($mapping['isOwningSide']) {
1578 284
            if (empty($mapping['joinColumns'])) {
1579
                // Apply default join column
1580 89
                $mapping['joinColumns'] = array(
1581
                    array(
1582 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...
1583 89
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName()
1584
                    )
1585
                );
1586
            }
1587
1588 284
            $uniqueConstraintColumns = array();
1589
1590 284
            foreach ($mapping['joinColumns'] as &$joinColumn) {
1591 284
                if ($mapping['type'] === self::ONE_TO_ONE && ! $this->isInheritanceTypeSingleTable()) {
1592 155
                    if (count($mapping['joinColumns']) === 1) {
1593 153
                        if (empty($mapping['id'])) {
1594 153
                            $joinColumn['unique'] = true;
1595
                        }
1596
                    } else {
1597 2
                        $uniqueConstraintColumns[] = $joinColumn['name'];
1598
                    }
1599
                }
1600
1601 284
                if (empty($joinColumn['name'])) {
1602 30
                    $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...
1603
                }
1604
1605 284
                if (empty($joinColumn['referencedColumnName'])) {
1606 5
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1607
                }
1608
1609 284
                if ($joinColumn['name'][0] === '`') {
1610 7
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1611 7
                    $joinColumn['quoted'] = true;
1612
                }
1613
1614 284
                if ($joinColumn['referencedColumnName'][0] === '`') {
1615 4
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1616 4
                    $joinColumn['quoted']               = true;
1617
                }
1618
1619 284
                $mapping['sourceToTargetKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1620 284
                $mapping['joinColumnFieldNames'][$joinColumn['name']] = isset($joinColumn['fieldName'])
1621
                    ? $joinColumn['fieldName']
1622 284
                    : $joinColumn['name'];
1623
            }
1624
1625 284
            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...
1626 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...
1627
                    throw new RuntimeException("ClassMetadataInfo::setTable() has to be called before defining a one to one relationship.");
1628
                }
1629
1630 2
                $this->table['uniqueConstraints'][$mapping['fieldName'] . "_uniq"] = array(
1631 2
                    'columns' => $uniqueConstraintColumns
1632
                );
1633
            }
1634
1635 284
            $mapping['targetToSourceKeyColumns'] = array_flip($mapping['sourceToTargetKeyColumns']);
1636
        }
1637
1638 297
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'];
1639 297
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove'];
1640
1641 297
        if ($mapping['orphanRemoval']) {
1642 22
            unset($mapping['unique']);
1643
        }
1644
1645 297
        if (isset($mapping['id']) && $mapping['id'] === true && !$mapping['isOwningSide']) {
1646 2
            throw MappingException::illegalInverseIdentifierAssociation($this->name, $mapping['fieldName']);
1647
        }
1648
1649 295
        return $mapping;
1650
    }
1651
1652
    /**
1653
     * Validates & completes a one-to-many association mapping.
1654
     *
1655
     * @param array $mapping The mapping to validate and complete.
1656
     *
1657
     * @return array The validated and completed mapping.
1658
     *
1659
     * @throws MappingException
1660
     * @throws InvalidArgumentException
1661
     */
1662 133
    protected function _validateAndCompleteOneToManyMapping(array $mapping)
1663
    {
1664 133
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1665
1666
        // OneToMany-side MUST be inverse (must have mappedBy)
1667 132
        if ( ! isset($mapping['mappedBy'])) {
1668
            throw MappingException::oneToManyRequiresMappedBy($mapping['fieldName']);
1669
        }
1670
1671 132
        $mapping['orphanRemoval']   = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'];
1672 132
        $mapping['isCascadeRemove'] = $mapping['orphanRemoval'] || $mapping['isCascadeRemove'];
1673
1674 132
        $this->assertMappingOrderBy($mapping);
1675
1676 132
        return $mapping;
1677
    }
1678
1679
    /**
1680
     * Validates & completes a many-to-many association mapping.
1681
     *
1682
     * @param array $mapping The mapping to validate & complete.
1683
     *
1684
     * @return array The validated & completed mapping.
1685
     *
1686
     * @throws \InvalidArgumentException
1687
     */
1688 154
    protected function _validateAndCompleteManyToManyMapping(array $mapping)
1689
    {
1690 154
        $mapping = $this->_validateAndCompleteAssociationMapping($mapping);
1691
1692 152
        if ($mapping['isOwningSide']) {
1693
            // owning side MUST have a join table
1694 134
            if ( ! isset($mapping['joinTable']['name'])) {
1695 22
                $mapping['joinTable']['name'] = $this->namingStrategy->joinTableName($mapping['sourceEntity'], $mapping['targetEntity'], $mapping['fieldName']);
1696
            }
1697
1698 134
            $selfReferencingEntityWithoutJoinColumns = $mapping['sourceEntity'] == $mapping['targetEntity']
1699 134
                && (! (isset($mapping['joinTable']['joinColumns']) || isset($mapping['joinTable']['inverseJoinColumns'])));
1700
1701 134
            if ( ! isset($mapping['joinTable']['joinColumns'])) {
1702 21
                $mapping['joinTable']['joinColumns'] = array(
1703
                    array(
1704 21
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['sourceEntity'], $selfReferencingEntityWithoutJoinColumns ? 'source' : null),
1705 21
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1706 21
                        'onDelete' => 'CASCADE'
1707
                    )
1708
                );
1709
            }
1710
1711 134
            if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) {
1712 22
                $mapping['joinTable']['inverseJoinColumns'] = array(
1713
                    array(
1714 22
                        'name' => $this->namingStrategy->joinKeyColumnName($mapping['targetEntity'], $selfReferencingEntityWithoutJoinColumns ? 'target' : null),
1715 22
                        'referencedColumnName' => $this->namingStrategy->referenceColumnName(),
1716 22
                        'onDelete' => 'CASCADE'
1717
                    )
1718
                );
1719
            }
1720
1721 134
            $mapping['joinTableColumns'] = array();
1722
1723 134
            foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) {
1724 134
                if (empty($joinColumn['name'])) {
1725 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...
1726
                }
1727
1728 134
                if (empty($joinColumn['referencedColumnName'])) {
1729 6
                    $joinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1730
                }
1731
1732 134
                if ($joinColumn['name'][0] === '`') {
1733 3
                    $joinColumn['name']   = trim($joinColumn['name'], '`');
1734 3
                    $joinColumn['quoted'] = true;
1735
                }
1736
1737 134
                if ($joinColumn['referencedColumnName'][0] === '`') {
1738 3
                    $joinColumn['referencedColumnName'] = trim($joinColumn['referencedColumnName'], '`');
1739 3
                    $joinColumn['quoted']               = true;
1740
                }
1741
1742 134
                if (isset($joinColumn['onDelete']) && strtolower($joinColumn['onDelete']) == 'cascade') {
1743 31
                    $mapping['isOnDeleteCascade'] = true;
1744
                }
1745
1746 134
                $mapping['relationToSourceKeyColumns'][$joinColumn['name']] = $joinColumn['referencedColumnName'];
1747 134
                $mapping['joinTableColumns'][] = $joinColumn['name'];
1748
            }
1749
1750 134
            foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) {
1751 134
                if (empty($inverseJoinColumn['name'])) {
1752 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...
1753
                }
1754
1755 134
                if (empty($inverseJoinColumn['referencedColumnName'])) {
1756 6
                    $inverseJoinColumn['referencedColumnName'] = $this->namingStrategy->referenceColumnName();
1757
                }
1758
1759 134
                if ($inverseJoinColumn['name'][0] === '`') {
1760 3
                    $inverseJoinColumn['name']   = trim($inverseJoinColumn['name'], '`');
1761 3
                    $inverseJoinColumn['quoted'] = true;
1762
                }
1763
1764 134
                if ($inverseJoinColumn['referencedColumnName'][0] === '`') {
1765 3
                    $inverseJoinColumn['referencedColumnName']  = trim($inverseJoinColumn['referencedColumnName'], '`');
1766 3
                    $inverseJoinColumn['quoted']                = true;
1767
                }
1768
1769 134
                if (isset($inverseJoinColumn['onDelete']) && strtolower($inverseJoinColumn['onDelete']) == 'cascade') {
1770 27
                    $mapping['isOnDeleteCascade'] = true;
1771
                }
1772
1773 134
                $mapping['relationToTargetKeyColumns'][$inverseJoinColumn['name']] = $inverseJoinColumn['referencedColumnName'];
1774 134
                $mapping['joinTableColumns'][] = $inverseJoinColumn['name'];
1775
            }
1776
        }
1777
1778 152
        $mapping['orphanRemoval'] = isset($mapping['orphanRemoval']) && $mapping['orphanRemoval'];
1779
1780 152
        $this->assertMappingOrderBy($mapping);
1781
1782 152
        return $mapping;
1783
    }
1784
1785
    /**
1786
     * {@inheritDoc}
1787
     */
1788 608
    public function getIdentifierFieldNames()
1789
    {
1790 608
        return $this->identifier;
1791
    }
1792
1793
    /**
1794
     * Gets the name of the single id field. Note that this only works on
1795
     * entity classes that have a single-field pk.
1796
     *
1797
     * @return string
1798
     *
1799
     * @throws MappingException If the class has a composite primary key.
1800
     */
1801 404
    public function getSingleIdentifierFieldName()
1802
    {
1803 404
        if ($this->isIdentifierComposite) {
1804 1
            throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
1805
        }
1806
1807 403
        return $this->identifier[0];
1808
    }
1809
1810
    /**
1811
     * Gets the column name of the single id column. Note that this only works on
1812
     * entity classes that have a single-field pk.
1813
     *
1814
     * @return string
1815
     *
1816
     * @throws MappingException If the class has a composite primary key.
1817
     */
1818 3
    public function getSingleIdentifierColumnName()
1819
    {
1820 3
        return $this->getColumnName($this->getSingleIdentifierFieldName());
1821
    }
1822
1823
    /**
1824
     * INTERNAL:
1825
     * Sets the mapped identifier/primary key fields of this class.
1826
     * Mainly used by the ClassMetadataFactory to assign inherited identifiers.
1827
     *
1828
     * @param array $identifier
1829
     *
1830
     * @return void
1831
     */
1832 128
    public function setIdentifier(array $identifier)
1833
    {
1834 128
        $this->identifier = $identifier;
1835 128
        $this->isIdentifierComposite = (count($this->identifier) > 1);
1836 128
    }
1837
1838
    /**
1839
     * {@inheritDoc}
1840
     */
1841 63
    public function getIdentifier()
1842
    {
1843 63
        return $this->identifier;
1844
    }
1845
1846
    /**
1847
     * {@inheritDoc}
1848
     */
1849 295
    public function hasField($fieldName)
1850
    {
1851 295
        return isset($this->fieldMappings[$fieldName]);
1852
    }
1853
1854
    /**
1855
     * Gets an array containing all the column names.
1856
     *
1857
     * @param array|null $fieldNames
1858
     *
1859
     * @return array
1860
     */
1861 43
    public function getColumnNames(array $fieldNames = null)
1862
    {
1863 43
        if (null === $fieldNames) {
1864 42
            return array_keys($this->fieldNames);
1865
        }
1866
1867 1
        return array_values(array_map([$this, 'getColumnName'], $fieldNames));
1868
    }
1869
1870
    /**
1871
     * Returns an array with all the identifier column names.
1872
     *
1873
     * @return array
1874
     */
1875 325
    public function getIdentifierColumnNames()
1876
    {
1877 325
        $columnNames = array();
1878
1879 325
        foreach ($this->identifier as $idProperty) {
1880 325
            if (isset($this->fieldMappings[$idProperty])) {
1881 321
                $columnNames[] = $this->fieldMappings[$idProperty]['columnName'];
1882
1883 321
                continue;
1884
            }
1885
1886
            // Association defined as Id field
1887 22
            $joinColumns      = $this->associationMappings[$idProperty]['joinColumns'];
1888
            $assocColumnNames = array_map(function ($joinColumn) { return $joinColumn['name']; }, $joinColumns);
1889
1890 22
            $columnNames = array_merge($columnNames, $assocColumnNames);
1891
        }
1892
1893 325
        return $columnNames;
1894
    }
1895
1896
    /**
1897
     * Sets the type of Id generator to use for the mapped class.
1898
     *
1899
     * @param int $generatorType
1900
     *
1901
     * @return void
1902
     */
1903 466
    public function setIdGeneratorType($generatorType)
1904
    {
1905 466
        $this->generatorType = $generatorType;
1906 466
    }
1907
1908
    /**
1909
     * Checks whether the mapped class uses an Id generator.
1910
     *
1911
     * @return boolean TRUE if the mapped class uses an Id generator, FALSE otherwise.
1912
     */
1913 412
    public function usesIdGenerator()
1914
    {
1915 412
        return $this->generatorType != self::GENERATOR_TYPE_NONE;
1916
    }
1917
1918
    /**
1919
     * @return boolean
1920
     */
1921 1350
    public function isInheritanceTypeNone()
1922
    {
1923 1350
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1924
    }
1925
1926
    /**
1927
     * Checks whether the mapped class uses the JOINED inheritance mapping strategy.
1928
     *
1929
     * @return boolean TRUE if the class participates in a JOINED inheritance mapping,
1930
     *                 FALSE otherwise.
1931
     */
1932 1058
    public function isInheritanceTypeJoined()
1933
    {
1934 1058
        return $this->inheritanceType == self::INHERITANCE_TYPE_JOINED;
1935
    }
1936
1937
    /**
1938
     * Checks whether the mapped class uses the SINGLE_TABLE inheritance mapping strategy.
1939
     *
1940
     * @return boolean TRUE if the class participates in a SINGLE_TABLE inheritance mapping,
1941
     *                 FALSE otherwise.
1942
     */
1943 1237
    public function isInheritanceTypeSingleTable()
1944
    {
1945 1237
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_TABLE;
1946
    }
1947
1948
    /**
1949
     * Checks whether the mapped class uses the TABLE_PER_CLASS inheritance mapping strategy.
1950
     *
1951
     * @return boolean TRUE if the class participates in a TABLE_PER_CLASS inheritance mapping,
1952
     *                 FALSE otherwise.
1953
     */
1954 261
    public function isInheritanceTypeTablePerClass()
1955
    {
1956 261
        return $this->inheritanceType == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
1957
    }
1958
1959
    /**
1960
     * Checks whether the class uses an identity column for the Id generation.
1961
     *
1962
     * @return boolean TRUE if the class uses the IDENTITY generator, FALSE otherwise.
1963
     */
1964 1072
    public function isIdGeneratorIdentity()
1965
    {
1966 1072
        return $this->generatorType == self::GENERATOR_TYPE_IDENTITY;
1967
    }
1968
1969
    /**
1970
     * Checks whether the class uses a sequence for id generation.
1971
     *
1972
     * @return boolean TRUE if the class uses the SEQUENCE generator, FALSE otherwise.
1973
     */
1974 319
    public function isIdGeneratorSequence()
1975
    {
1976 319
        return $this->generatorType == self::GENERATOR_TYPE_SEQUENCE;
1977
    }
1978
1979
    /**
1980
     * Checks whether the class uses a table for id generation.
1981
     *
1982
     * @return boolean TRUE if the class uses the TABLE generator, FALSE otherwise.
1983
     */
1984 84
    public function isIdGeneratorTable()
1985
    {
1986 84
        return $this->generatorType == self::GENERATOR_TYPE_TABLE;
1987
    }
1988
1989
    /**
1990
     * Checks whether the class has a natural identifier/pk (which means it does
1991
     * not use any Id generator.
1992
     *
1993
     * @return boolean
1994
     */
1995 73
    public function isIdentifierNatural()
1996
    {
1997 73
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1998
    }
1999
2000
    /**
2001
     * Checks whether the class use a UUID for id generation.
2002
     *
2003
     * @return boolean
2004
     */
2005
    public function isIdentifierUuid()
2006
    {
2007
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
2008
    }
2009
2010
    /**
2011
     * Gets the type of a field.
2012
     *
2013
     * @param string $fieldName
2014
     *
2015
     * @return \Doctrine\DBAL\Types\Type|string|null
2016
     *
2017
     * @todo 3.0 Remove this. PersisterHelper should fix it somehow
2018
     */
2019 39
    public function getTypeOfField($fieldName)
2020
    {
2021 39
        return isset($this->fieldMappings[$fieldName])
2022 39
            ? $this->fieldMappings[$fieldName]['type']
2023 39
            : null;
2024
    }
2025
2026
    /**
2027
     * Gets the type of a column.
2028
     *
2029
     * @param string $columnName
2030
     *
2031
     * @return \Doctrine\DBAL\Types\Type|string|null
2032
     *
2033
     * @deprecated 3.0 remove this. this method is bogous and unreliable, since it cannot resolve the type of a column
2034
     *             that is derived by a referenced field on a different entity.
2035
     */
2036
    public function getTypeOfColumn($columnName)
2037
    {
2038
        return $this->getTypeOfField($this->getFieldName($columnName));
2039
    }
2040
2041
    /**
2042
     * Gets the name of the primary table.
2043
     *
2044
     * @return string
2045
     */
2046 1360
    public function getTableName()
2047
    {
2048 1360
        return $this->table['name'];
2049
    }
2050
2051
    /**
2052
     * Gets primary table's schema name.
2053
     *
2054
     * @return string|null
2055
     */
2056 13
    public function getSchemaName()
2057
    {
2058 13
        return isset($this->table['schema']) ? $this->table['schema'] : null;
2059
    }
2060
2061
    /**
2062
     * Gets the table name to use for temporary identifier tables of this class.
2063
     *
2064
     * @return string
2065
     */
2066 7
    public function getTemporaryIdTableName()
2067
    {
2068
        // replace dots with underscores because PostgreSQL creates temporary tables in a special schema
2069 7
        return str_replace('.', '_', $this->getTableName() . '_id_tmp');
2070
    }
2071
2072
    /**
2073
     * Sets the mapped subclasses of this class.
2074
     *
2075
     * @param array $subclasses The names of all mapped subclasses.
2076
     *
2077
     * @return void
2078
     */
2079 2
    public function setSubclasses(array $subclasses)
2080
    {
2081 2
        foreach ($subclasses as $subclass) {
2082 2
            $this->subClasses[] = $this->fullyQualifiedClassName($subclass);
2083
        }
2084 2
    }
2085
2086
    /**
2087
     * Sets the parent class names.
2088
     * Assumes that the class names in the passed array are in the order:
2089
     * directParent -> directParentParent -> directParentParentParent ... -> root.
2090
     *
2091
     * @param array $classNames
2092
     *
2093
     * @return void
2094
     */
2095 429
    public function setParentClasses(array $classNames)
2096
    {
2097 429
        $this->parentClasses = $classNames;
2098
2099 429
        if (count($classNames) > 0) {
2100 83
            $this->rootEntityName = array_pop($classNames);
2101
        }
2102 429
    }
2103
2104
    /**
2105
     * Sets the inheritance type used by the class and its subclasses.
2106
     *
2107
     * @param integer $type
2108
     *
2109
     * @return void
2110
     *
2111
     * @throws MappingException
2112
     */
2113 174
    public function setInheritanceType($type)
2114
    {
2115 174
        if ( ! $this->_isInheritanceType($type)) {
2116
            throw MappingException::invalidInheritanceType($this->name, $type);
2117
        }
2118
2119 174
        $this->inheritanceType = $type;
2120 174
    }
2121
2122
    /**
2123
     * Sets the association to override association mapping of property for an entity relationship.
2124
     *
2125
     * @param string $fieldName
2126
     * @param array  $overrideMapping
2127
     *
2128
     * @return void
2129
     *
2130
     * @throws MappingException
2131
     */
2132 20
    public function setAssociationOverride($fieldName, array $overrideMapping)
2133
    {
2134 20
        if ( ! isset($this->associationMappings[$fieldName])) {
2135 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2136
        }
2137
2138 19
        $mapping = $this->associationMappings[$fieldName];
2139
2140 19
        if (isset($overrideMapping['joinColumns'])) {
2141 13
            $mapping['joinColumns'] = $overrideMapping['joinColumns'];
2142
        }
2143
2144 19
        if (isset($overrideMapping['inversedBy'])) {
2145 6
            $mapping['inversedBy'] = $overrideMapping['inversedBy'];
2146
        }
2147
2148 19
        if (isset($overrideMapping['joinTable'])) {
2149 12
            $mapping['joinTable'] = $overrideMapping['joinTable'];
2150
        }
2151
2152 19
        $mapping['joinColumnFieldNames']        = null;
2153 19
        $mapping['joinTableColumns']            = null;
2154 19
        $mapping['sourceToTargetKeyColumns']    = null;
2155 19
        $mapping['relationToSourceKeyColumns']  = null;
2156 19
        $mapping['relationToTargetKeyColumns']  = null;
2157
2158 19
        switch ($mapping['type']) {
2159 19
            case self::ONE_TO_ONE:
2160 1
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2161 1
                break;
2162 18
            case self::ONE_TO_MANY:
2163
                $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2164
                break;
2165 18
            case self::MANY_TO_ONE:
2166 12
                $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2167 12
                break;
2168 18
            case self::MANY_TO_MANY:
2169 18
                $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2170 18
                break;
2171
        }
2172
2173 19
        $this->associationMappings[$fieldName] = $mapping;
2174 19
    }
2175
2176
    /**
2177
     * Sets the override for a mapped field.
2178
     *
2179
     * @param string $fieldName
2180
     * @param array  $overrideMapping
2181
     *
2182
     * @return void
2183
     *
2184
     * @throws MappingException
2185
     */
2186 16
    public function setAttributeOverride($fieldName, array $overrideMapping)
2187
    {
2188 16
        if ( ! isset($this->fieldMappings[$fieldName])) {
2189 1
            throw MappingException::invalidOverrideFieldName($this->name, $fieldName);
2190
        }
2191
2192 15
        $mapping = $this->fieldMappings[$fieldName];
2193
2194 15
        if (isset($mapping['id'])) {
2195 12
            $overrideMapping['id'] = $mapping['id'];
2196
        }
2197
2198 15
        if (isset($mapping['originalClass'])) {
2199 1
            $overrideMapping['originalClass'] = $mapping['originalClass'];
2200
        }
2201
2202 15
        if (isset($mapping['declaredField'])) {
2203 1
            $overrideMapping['declaredField'] = $mapping['declaredField'];
2204
        }
2205
2206 15
        if (isset($mapping['originalField'])) {
2207 1
            $overrideMapping['originalField'] = $mapping['originalField'];
2208
        }
2209
2210 15
        if ( ! isset($overrideMapping['type'])) {
2211 6
            $overrideMapping['type'] = $mapping['type'];
2212
        }
2213
2214 15
        if ( ! isset($overrideMapping['fieldName'])) {
2215 5
            $overrideMapping['fieldName'] = $mapping['fieldName'];
2216
        }
2217
2218 15
        if ($overrideMapping['type'] !== $mapping['type']) {
2219 1
            throw MappingException::invalidOverrideFieldType($this->name, $fieldName);
2220
        }
2221
2222 14
        $this->removeFieldMapping($fieldName);
2223 14
        $this->_validateAndCompleteFieldMapping($overrideMapping);
2224
2225 14
        $this->fieldMappings[$fieldName] = $overrideMapping;
2226 14
    }
2227
2228
    /**
2229
     * Remove a mapped field.
2230
     *
2231
     * @param string $fieldName
2232
     *
2233
     * @return void
2234
     *
2235
     * @throws MappingException
2236
     */
2237 16
    public function removeFieldMapping($fieldName)
2238
    {
2239 16
        if ( ! isset($this->fieldMappings[$fieldName])) {
2240 1
            throw MappingException::mappingNotFound($this->name, $fieldName);
2241
        }
2242
2243 15
        $mapping = $this->fieldMappings[$fieldName];
2244
2245 15
        unset($this->fieldMappings[$mapping['fieldName']]);
2246 15
        unset($this->fieldNames[$mapping['columnName']]);
2247 15
        unset($this->columnNames[$mapping['fieldName']]);
2248 15
    }
2249
2250
    /**
2251
     * Checks whether a mapped field is inherited from an entity superclass.
2252
     *
2253
     * @param string $fieldName
2254
     *
2255
     * @return bool TRUE if the field is inherited, FALSE otherwise.
2256
     */
2257 393
    public function isInheritedField($fieldName)
2258
    {
2259 393
        return isset($this->fieldMappings[$fieldName]['inherited']);
2260
    }
2261
2262
    /**
2263
     * Checks if this entity is the root in any entity-inheritance-hierarchy.
2264
     *
2265
     * @return bool
2266
     */
2267 428
    public function isRootEntity()
2268
    {
2269 428
        return $this->name == $this->rootEntityName;
2270
    }
2271
2272
    /**
2273
     * Checks whether a mapped association field is inherited from a superclass.
2274
     *
2275
     * @param string $fieldName
2276
     *
2277
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
2278
     */
2279 372
    public function isInheritedAssociation($fieldName)
2280
    {
2281 372
        return isset($this->associationMappings[$fieldName]['inherited']);
2282
    }
2283
2284 372
    public function isInheritedEmbeddedClass($fieldName)
2285
    {
2286 372
        return isset($this->embeddedClasses[$fieldName]['inherited']);
2287
    }
2288
2289
    /**
2290
     * Sets the name of the primary table the class is mapped to.
2291
     *
2292
     * @param string $tableName The table name.
2293
     *
2294
     * @return void
2295
     *
2296
     * @deprecated Use {@link setPrimaryTable}.
2297
     */
2298 5
    public function setTableName($tableName)
2299
    {
2300 5
        $this->table['name'] = $tableName;
2301 5
    }
2302
2303
    /**
2304
     * Sets the primary table definition. The provided array supports the
2305
     * following structure:
2306
     *
2307
     * name => <tableName> (optional, defaults to class name)
2308
     * indexes => array of indexes (optional)
2309
     * uniqueConstraints => array of constraints (optional)
2310
     *
2311
     * If a key is omitted, the current value is kept.
2312
     *
2313
     * @param array $table The table description.
2314
     *
2315
     * @return void
2316
     */
2317 344
    public function setPrimaryTable(array $table)
2318
    {
2319 344
        if (isset($table['name'])) {
2320
            // Split schema and table name from a table name like "myschema.mytable"
2321 273
            if (strpos($table['name'], '.') !== false) {
2322 9
                list($this->table['schema'], $table['name']) = explode('.', $table['name'], 2);
2323
            }
2324
2325 273
            if ($table['name'][0] === '`') {
2326 17
                $table['name']          = trim($table['name'], '`');
2327 17
                $this->table['quoted']  = true;
2328
            }
2329
2330 273
            $this->table['name'] = $table['name'];
2331
        }
2332
2333 344
        if (isset($table['schema'])) {
2334 6
            $this->table['schema'] = $table['schema'];
2335
        }
2336
2337 344
        if (isset($table['indexes'])) {
2338 17
            $this->table['indexes'] = $table['indexes'];
2339
        }
2340
2341 344
        if (isset($table['uniqueConstraints'])) {
2342 8
            $this->table['uniqueConstraints'] = $table['uniqueConstraints'];
2343
        }
2344
2345 344
        if (isset($table['options'])) {
2346 9
            $this->table['options'] = $table['options'];
2347
        }
2348 344
    }
2349
2350
    /**
2351
     * Checks whether the given type identifies an inheritance type.
2352
     *
2353
     * @param integer $type
2354
     *
2355
     * @return boolean TRUE if the given type identifies an inheritance type, FALSe otherwise.
2356
     */
2357 174
    private function _isInheritanceType($type)
2358
    {
2359 174
        return $type == self::INHERITANCE_TYPE_NONE ||
2360 109
                $type == self::INHERITANCE_TYPE_SINGLE_TABLE ||
2361 54
                $type == self::INHERITANCE_TYPE_JOINED ||
2362 174
                $type == self::INHERITANCE_TYPE_TABLE_PER_CLASS;
2363
    }
2364
2365
    /**
2366
     * Adds a mapped field to the class.
2367
     *
2368
     * @param array $mapping The field mapping.
2369
     *
2370
     * @return void
2371
     *
2372
     * @throws MappingException
2373
     */
2374 566
    public function mapField(array $mapping)
2375
    {
2376 566
        $this->_validateAndCompleteFieldMapping($mapping);
2377 564
        $this->assertFieldNotMapped($mapping['fieldName']);
2378
2379 563
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
2380 563
    }
2381
2382
    /**
2383
     * INTERNAL:
2384
     * Adds an association mapping without completing/validating it.
2385
     * This is mainly used to add inherited association mappings to derived classes.
2386
     *
2387
     * @param array $mapping
2388
     *
2389
     * @return void
2390
     *
2391
     * @throws MappingException
2392
     */
2393 49
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
2394
    {
2395 49
        if (isset($this->associationMappings[$mapping['fieldName']])) {
2396 1
            throw MappingException::duplicateAssociationMapping($this->name, $mapping['fieldName']);
2397
        }
2398 49
        $this->associationMappings[$mapping['fieldName']] = $mapping;
2399 49
    }
2400
2401
    /**
2402
     * INTERNAL:
2403
     * Adds a field mapping without completing/validating it.
2404
     * This is mainly used to add inherited field mappings to derived classes.
2405
     *
2406
     * @param array $fieldMapping
2407
     *
2408
     * @return void
2409
     */
2410 112
    public function addInheritedFieldMapping(array $fieldMapping)
2411
    {
2412 112
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
2413 112
        $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...
2414 112
        $this->fieldNames[$fieldMapping['columnName']] = $fieldMapping['fieldName'];
2415 112
    }
2416
2417
    /**
2418
     * INTERNAL:
2419
     * Adds a named query to this class.
2420
     *
2421
     * @param array $queryMapping
2422
     *
2423
     * @return void
2424
     *
2425
     * @throws MappingException
2426
     */
2427 30
    public function addNamedQuery(array $queryMapping)
2428
    {
2429 30
        if (!isset($queryMapping['name'])) {
2430 2
            throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2431
        }
2432
2433 28
        if (isset($this->namedQueries[$queryMapping['name']])) {
2434 1
            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2435
        }
2436
2437 28
        if (!isset($queryMapping['query'])) {
2438
            throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2439
        }
2440
2441 28
        $name   = $queryMapping['name'];
2442 28
        $query  = $queryMapping['query'];
2443 28
        $dql    = str_replace('__CLASS__', $this->name, $query);
2444
2445 28
        $this->namedQueries[$name] = array(
2446 28
            'name'  => $name,
2447 28
            'query' => $query,
2448 28
            'dql'   => $dql,
2449
        );
2450 28
    }
2451
2452
    /**
2453
     * INTERNAL:
2454
     * Adds a named native query to this class.
2455
     *
2456
     * @param array $queryMapping
2457
     *
2458
     * @return void
2459
     *
2460
     * @throws MappingException
2461
     */
2462 41
    public function addNamedNativeQuery(array $queryMapping)
2463
    {
2464 41
        if (!isset($queryMapping['name'])) {
2465
            throw MappingException::nameIsMandatoryForQueryMapping($this->name);
2466
        }
2467
2468 41
        if (isset($this->namedNativeQueries[$queryMapping['name']])) {
2469 1
            throw MappingException::duplicateQueryMapping($this->name, $queryMapping['name']);
2470
        }
2471
2472 41
        if (!isset($queryMapping['query'])) {
2473
            throw MappingException::emptyQueryMapping($this->name, $queryMapping['name']);
2474
        }
2475
2476 41
        if (!isset($queryMapping['resultClass']) && !isset($queryMapping['resultSetMapping'])) {
2477
            throw MappingException::missingQueryMapping($this->name, $queryMapping['name']);
2478
        }
2479
2480 41
        $queryMapping['isSelfClass'] = false;
2481
2482 41
        if (isset($queryMapping['resultClass'])) {
2483 39
            if ($queryMapping['resultClass'] === '__CLASS__') {
2484
2485 12
                $queryMapping['isSelfClass'] = true;
2486 12
                $queryMapping['resultClass'] = $this->name;
2487
            }
2488
2489 39
            $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...
2490 39
            $queryMapping['resultClass'] = ltrim($queryMapping['resultClass'], '\\');
2491
        }
2492
2493 41
        $this->namedNativeQueries[$queryMapping['name']] = $queryMapping;
2494 41
    }
2495
2496
    /**
2497
     * INTERNAL:
2498
     * Adds a sql result set mapping to this class.
2499
     *
2500
     * @param array $resultMapping
2501
     *
2502
     * @return void
2503
     *
2504
     * @throws MappingException
2505
     */
2506 41
    public function addSqlResultSetMapping(array $resultMapping)
2507
    {
2508 41
        if (!isset($resultMapping['name'])) {
2509
            throw MappingException::nameIsMandatoryForSqlResultSetMapping($this->name);
2510
        }
2511
2512 41
        if (isset($this->sqlResultSetMappings[$resultMapping['name']])) {
2513 1
            throw MappingException::duplicateResultSetMapping($this->name, $resultMapping['name']);
2514
        }
2515
2516 41
        if (isset($resultMapping['entities'])) {
2517 41
            foreach ($resultMapping['entities'] as $key => $entityResult) {
2518 41
                if (!isset($entityResult['entityClass'])) {
2519 1
                    throw MappingException::missingResultSetMappingEntity($this->name, $resultMapping['name']);
2520
                }
2521
2522 40
                $entityResult['isSelfClass'] = false;
2523 40
                if ($entityResult['entityClass'] === '__CLASS__') {
2524
2525 22
                    $entityResult['isSelfClass'] = true;
2526 22
                    $entityResult['entityClass'] = $this->name;
2527
2528
                }
2529
2530 40
                $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...
2531
2532 40
                $resultMapping['entities'][$key]['entityClass'] = ltrim($entityResult['entityClass'], '\\');
2533 40
                $resultMapping['entities'][$key]['isSelfClass'] = $entityResult['isSelfClass'];
2534
2535 40
                if (isset($entityResult['fields'])) {
2536 36
                    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...
2537 36
                        if (!isset($field['name'])) {
2538
                            throw MappingException::missingResultSetMappingFieldName($this->name, $resultMapping['name']);
2539
                        }
2540
2541 36
                        if (!isset($field['column'])) {
2542 16
                            $fieldName = $field['name'];
2543 16
                            if (strpos($fieldName, '.')) {
2544 10
                                list(, $fieldName) = explode('.', $fieldName);
2545
                            }
2546
2547 40
                            $resultMapping['entities'][$key]['fields'][$k]['column'] = $fieldName;
2548
                        }
2549
                    }
2550
                }
2551
            }
2552
        }
2553
2554 40
        $this->sqlResultSetMappings[$resultMapping['name']] = $resultMapping;
2555 40
    }
2556
2557
    /**
2558
     * Adds a one-to-one mapping.
2559
     *
2560
     * @param array $mapping The mapping.
2561
     *
2562
     * @return void
2563
     */
2564 172
    public function mapOneToOne(array $mapping)
2565
    {
2566 172
        $mapping['type'] = self::ONE_TO_ONE;
2567
2568 172
        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2569
2570 169
        $this->_storeAssociationMapping($mapping);
2571 168
    }
2572
2573
    /**
2574
     * Adds a one-to-many mapping.
2575
     *
2576
     * @param array $mapping The mapping.
2577
     *
2578
     * @return void
2579
     */
2580 133
    public function mapOneToMany(array $mapping)
2581
    {
2582 133
        $mapping['type'] = self::ONE_TO_MANY;
2583
2584 133
        $mapping = $this->_validateAndCompleteOneToManyMapping($mapping);
2585
2586 132
        $this->_storeAssociationMapping($mapping);
2587 132
    }
2588
2589
    /**
2590
     * Adds a many-to-one mapping.
2591
     *
2592
     * @param array $mapping The mapping.
2593
     *
2594
     * @return void
2595
     */
2596 164
    public function mapManyToOne(array $mapping)
2597
    {
2598 164
        $mapping['type'] = self::MANY_TO_ONE;
2599
2600
        // A many-to-one mapping is essentially a one-one backreference
2601 164
        $mapping = $this->_validateAndCompleteOneToOneMapping($mapping);
2602
2603 159
        $this->_storeAssociationMapping($mapping);
2604 159
    }
2605
2606
    /**
2607
     * Adds a many-to-many mapping.
2608
     *
2609
     * @param array $mapping The mapping.
2610
     *
2611
     * @return void
2612
     */
2613 154
    public function mapManyToMany(array $mapping)
2614
    {
2615 154
        $mapping['type'] = self::MANY_TO_MANY;
2616
2617 154
        $mapping = $this->_validateAndCompleteManyToManyMapping($mapping);
2618
2619 152
        $this->_storeAssociationMapping($mapping);
2620 152
    }
2621
2622
    /**
2623
     * Stores the association mapping.
2624
     *
2625
     * @param array $assocMapping
2626
     *
2627
     * @return void
2628
     *
2629
     * @throws MappingException
2630
     */
2631 345
    protected function _storeAssociationMapping(array $assocMapping)
2632
    {
2633 345
        $sourceFieldName = $assocMapping['fieldName'];
2634
2635 345
        $this->assertFieldNotMapped($sourceFieldName);
2636
2637 344
        $this->associationMappings[$sourceFieldName] = $assocMapping;
2638 344
    }
2639
2640
    /**
2641
     * Registers a custom repository class for the entity class.
2642
     *
2643
     * @param string $repositoryClassName The class name of the custom mapper.
2644
     *
2645
     * @return void
2646
     */
2647 63
    public function setCustomRepositoryClass($repositoryClassName)
2648
    {
2649 63
        $this->customRepositoryClassName = $this->fullyQualifiedClassName($repositoryClassName);
2650 63
    }
2651
2652
    /**
2653
     * Dispatches the lifecycle event of the given entity to the registered
2654
     * lifecycle callbacks and lifecycle listeners.
2655
     *
2656
     * @deprecated Deprecated since version 2.4 in favor of \Doctrine\ORM\Event\ListenersInvoker
2657
     *
2658
     * @param string $lifecycleEvent The lifecycle event.
2659
     * @param object $entity         The Entity on which the event occurred.
2660
     *
2661
     * @return void
2662
     */
2663 1
    public function invokeLifecycleCallbacks($lifecycleEvent, $entity)
2664
    {
2665 1
        foreach ($this->lifecycleCallbacks[$lifecycleEvent] as $callback) {
2666 1
            $entity->$callback();
2667
        }
2668 1
    }
2669
2670
    /**
2671
     * Whether the class has any attached lifecycle listeners or callbacks for a lifecycle event.
2672
     *
2673
     * @param string $lifecycleEvent
2674
     *
2675
     * @return boolean
2676
     */
2677
    public function hasLifecycleCallbacks($lifecycleEvent)
2678
    {
2679
        return isset($this->lifecycleCallbacks[$lifecycleEvent]);
2680
    }
2681
2682
    /**
2683
     * Gets the registered lifecycle callbacks for an event.
2684
     *
2685
     * @param string $event
2686
     *
2687
     * @return array
2688
     */
2689
    public function getLifecycleCallbacks($event)
2690
    {
2691
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
2692
    }
2693
2694
    /**
2695
     * Adds a lifecycle callback for entities of this class.
2696
     *
2697
     * @param string $callback
2698
     * @param string $event
2699
     *
2700
     * @return void
2701
     */
2702 41
    public function addLifecycleCallback($callback, $event)
2703
    {
2704 41
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
2705 3
            return;
2706
        }
2707
2708 41
        $this->lifecycleCallbacks[$event][] = $callback;
2709 41
    }
2710
2711
    /**
2712
     * Sets the lifecycle callbacks for entities of this class.
2713
     * Any previously registered callbacks are overwritten.
2714
     *
2715
     * @param array $callbacks
2716
     *
2717
     * @return void
2718
     */
2719 127
    public function setLifecycleCallbacks(array $callbacks)
2720
    {
2721 127
        $this->lifecycleCallbacks = $callbacks;
2722 127
    }
2723
2724
    /**
2725
     * Adds a entity listener for entities of this class.
2726
     *
2727
     * @param string $eventName The entity lifecycle event.
2728
     * @param string $class     The listener class.
2729
     * @param string $method    The listener callback method.
2730
     *
2731
     * @throws \Doctrine\ORM\Mapping\MappingException
2732
     */
2733 35
    public function addEntityListener($eventName, $class, $method)
2734
    {
2735 35
        $class    = $this->fullyQualifiedClassName($class);
2736
2737
        $listener = array(
2738 35
            'class'  => $class,
2739 35
            'method' => $method,
2740
        );
2741
2742 35
        if ( ! class_exists($class)) {
2743 1
            throw MappingException::entityListenerClassNotFound($class, $this->name);
2744
        }
2745
2746 34
        if ( ! method_exists($class, $method)) {
2747 1
            throw MappingException::entityListenerMethodNotFound($class, $method, $this->name);
2748
        }
2749
2750 33
        if (isset($this->entityListeners[$eventName]) && in_array($listener, $this->entityListeners[$eventName])) {
2751 1
            throw MappingException::duplicateEntityListener($class, $method, $this->name);
2752
        }
2753
2754 33
        $this->entityListeners[$eventName][] = $listener;
2755 33
    }
2756
2757
    /**
2758
     * Sets the discriminator column definition.
2759
     *
2760
     * @param array $columnDef
2761
     *
2762
     * @return void
2763
     *
2764
     * @throws MappingException
2765
     *
2766
     * @see getDiscriminatorColumn()
2767
     */
2768 168
    public function setDiscriminatorColumn($columnDef)
2769
    {
2770 168
        if ($columnDef !== null) {
2771 113
            if ( ! isset($columnDef['name'])) {
2772 1
                throw MappingException::nameIsMandatoryForDiscriminatorColumns($this->name);
2773
            }
2774
2775 112
            if (isset($this->fieldNames[$columnDef['name']])) {
2776 1
                throw MappingException::duplicateColumnName($this->name, $columnDef['name']);
2777
            }
2778
2779 111
            if ( ! isset($columnDef['fieldName'])) {
2780 106
                $columnDef['fieldName'] = $columnDef['name'];
2781
            }
2782
2783 111
            if ( ! isset($columnDef['type'])) {
2784 2
                $columnDef['type'] = "string";
2785
            }
2786
2787 111
            if (in_array($columnDef['type'], array("boolean", "array", "object", "datetime", "time", "date"))) {
2788
                throw MappingException::invalidDiscriminatorColumnType($this->name, $columnDef['type']);
2789
            }
2790
2791 111
            $this->discriminatorColumn = $columnDef;
2792
        }
2793 166
    }
2794
2795
    /**
2796
     * Sets the discriminator values used by this class.
2797
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
2798
     *
2799
     * @param array $map
2800
     *
2801
     * @return void
2802
     */
2803 161
    public function setDiscriminatorMap(array $map)
2804
    {
2805 161
        foreach ($map as $value => $className) {
2806 107
            $this->addDiscriminatorMapClass($value, $className);
2807
        }
2808 161
    }
2809
2810
    /**
2811
     * Adds one entry of the discriminator map with a new class and corresponding name.
2812
     *
2813
     * @param string $name
2814
     * @param string $className
2815
     *
2816
     * @return void
2817
     *
2818
     * @throws MappingException
2819
     */
2820 108
    public function addDiscriminatorMapClass($name, $className)
2821
    {
2822 108
        $className = $this->fullyQualifiedClassName($className);
2823 108
        $className = ltrim($className, '\\');
2824
2825 108
        $this->discriminatorMap[$name] = $className;
2826
2827 108
        if ($this->name === $className) {
2828 79
            $this->discriminatorValue = $name;
2829
2830 79
            return;
2831
        }
2832
2833 107
        if ( ! (class_exists($className) || interface_exists($className))) {
2834
            throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
2835
        }
2836
2837 107
        if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $this->name can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
2838 98
            $this->subClasses[] = $className;
2839
        }
2840 107
    }
2841
2842
    /**
2843
     * Checks whether the class has a named query with the given query name.
2844
     *
2845
     * @param string $queryName
2846
     *
2847
     * @return boolean
2848
     */
2849 1
    public function hasNamedQuery($queryName)
2850
    {
2851 1
        return isset($this->namedQueries[$queryName]);
2852
    }
2853
2854
    /**
2855
     * Checks whether the class has a named native query with the given query name.
2856
     *
2857
     * @param string $queryName
2858
     *
2859
     * @return boolean
2860
     */
2861 1
    public function hasNamedNativeQuery($queryName)
2862
    {
2863 1
        return isset($this->namedNativeQueries[$queryName]);
2864
    }
2865
2866
    /**
2867
     * Checks whether the class has a named native query with the given query name.
2868
     *
2869
     * @param string $name
2870
     *
2871
     * @return boolean
2872
     */
2873 1
    public function hasSqlResultSetMapping($name)
2874
    {
2875 1
        return isset($this->sqlResultSetMappings[$name]);
2876
    }
2877
2878
    /**
2879
     * {@inheritDoc}
2880
     */
2881 344
    public function hasAssociation($fieldName)
2882
    {
2883 344
        return isset($this->associationMappings[$fieldName]);
2884
    }
2885
2886
    /**
2887
     * {@inheritDoc}
2888
     */
2889 1
    public function isSingleValuedAssociation($fieldName)
2890
    {
2891 1
        return isset($this->associationMappings[$fieldName])
2892 1
            && ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2893
    }
2894
2895
    /**
2896
     * {@inheritDoc}
2897
     */
2898 1034
    public function isCollectionValuedAssociation($fieldName)
2899
    {
2900 1034
        return isset($this->associationMappings[$fieldName])
2901 1034
            && ! ($this->associationMappings[$fieldName]['type'] & self::TO_ONE);
2902
    }
2903
2904
    /**
2905
     * Is this an association that only has a single join column?
2906
     *
2907
     * @param string $fieldName
2908
     *
2909
     * @return bool
2910
     */
2911 35
    public function isAssociationWithSingleJoinColumn($fieldName)
2912
    {
2913 35
        return isset($this->associationMappings[$fieldName])
2914 35
            && isset($this->associationMappings[$fieldName]['joinColumns'][0])
2915 35
            && ! isset($this->associationMappings[$fieldName]['joinColumns'][1]);
2916
    }
2917
2918
    /**
2919
     * Returns the single association join column (if any).
2920
     *
2921
     * @param string $fieldName
2922
     *
2923
     * @return string
2924
     *
2925
     * @throws MappingException
2926
     */
2927 9
    public function getSingleAssociationJoinColumnName($fieldName)
2928
    {
2929 9
        if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2930
            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2931
        }
2932
2933 9
        return $this->associationMappings[$fieldName]['joinColumns'][0]['name'];
2934
    }
2935
2936
    /**
2937
     * Returns the single association referenced join column name (if any).
2938
     *
2939
     * @param string $fieldName
2940
     *
2941
     * @return string
2942
     *
2943
     * @throws MappingException
2944
     */
2945 9
    public function getSingleAssociationReferencedJoinColumnName($fieldName)
2946
    {
2947 9
        if ( ! $this->isAssociationWithSingleJoinColumn($fieldName)) {
2948
            throw MappingException::noSingleAssociationJoinColumnFound($this->name, $fieldName);
2949
        }
2950
2951 9
        return $this->associationMappings[$fieldName]['joinColumns'][0]['referencedColumnName'];
2952
    }
2953
2954
    /**
2955
     * Used to retrieve a fieldname for either field or association from a given column.
2956
     *
2957
     * This method is used in foreign-key as primary-key contexts.
2958
     *
2959
     * @param string $columnName
2960
     *
2961
     * @return string
2962
     *
2963
     * @throws MappingException
2964
     */
2965 637
    public function getFieldForColumn($columnName)
2966
    {
2967 637
        if (isset($this->fieldNames[$columnName])) {
2968 637
            return $this->fieldNames[$columnName];
2969
        }
2970
2971 33
        foreach ($this->associationMappings as $assocName => $mapping) {
2972 33
            if ($this->isAssociationWithSingleJoinColumn($assocName) &&
2973 33
                $this->associationMappings[$assocName]['joinColumns'][0]['name'] == $columnName) {
2974
2975 33
                return $assocName;
2976
            }
2977
        }
2978
2979
        throw MappingException::noFieldNameFoundForColumn($this->name, $columnName);
2980
    }
2981
2982
    /**
2983
     * Sets the ID generator used to generate IDs for instances of this class.
2984
     *
2985
     * @param \Doctrine\ORM\Id\AbstractIdGenerator $generator
2986
     *
2987
     * @return void
2988
     */
2989 431
    public function setIdGenerator($generator)
2990
    {
2991 431
        $this->idGenerator = $generator;
2992 431
    }
2993
2994
    /**
2995
     * Sets definition.
2996
     *
2997
     * @param array $definition
2998
     *
2999
     * @return void
3000
     */
3001 12
    public function setCustomGeneratorDefinition(array $definition)
3002
    {
3003 12
        $this->customGeneratorDefinition = $definition;
3004 12
    }
3005
3006
    /**
3007
     * Sets the definition of the sequence ID generator for this class.
3008
     *
3009
     * The definition must have the following structure:
3010
     * <code>
3011
     * array(
3012
     *     'sequenceName'   => 'name',
3013
     *     'allocationSize' => 20,
3014
     *     'initialValue'   => 1
3015
     *     'quoted'         => 1
3016
     * )
3017
     * </code>
3018
     *
3019
     * @param array $definition
3020
     *
3021
     * @return void
3022
     *
3023
     * @throws MappingException
3024
     */
3025 23
    public function setSequenceGeneratorDefinition(array $definition)
3026
    {
3027 23
        if ( ! isset($definition['sequenceName'])) {
3028 1
            throw MappingException::missingSequenceName($this->name);
3029
        }
3030
3031 22
        if ($definition['sequenceName'][0] == '`') {
3032 1
            $definition['sequenceName']   = trim($definition['sequenceName'], '`');
3033 1
            $definition['quoted'] = true;
3034
        }
3035
3036 22
        $this->sequenceGeneratorDefinition = $definition;
3037 22
    }
3038
3039
    /**
3040
     * Sets the version field mapping used for versioning. Sets the default
3041
     * value to use depending on the column type.
3042
     *
3043
     * @param array $mapping The version field mapping array.
3044
     *
3045
     * @return void
3046
     *
3047
     * @throws MappingException
3048
     */
3049 26
    public function setVersionMapping(array &$mapping)
3050
    {
3051 26
        $this->isVersioned = true;
3052 26
        $this->versionField = $mapping['fieldName'];
3053
3054 26
        if ( ! isset($mapping['default'])) {
3055 26
            if (in_array($mapping['type'], array('integer', 'bigint', 'smallint'))) {
3056 25
                $mapping['default'] = 1;
3057 2
            } else if ($mapping['type'] == 'datetime') {
3058 1
                $mapping['default'] = 'CURRENT_TIMESTAMP';
3059
            } else {
3060 1
                throw MappingException::unsupportedOptimisticLockingType($this->name, $mapping['fieldName'], $mapping['type']);
3061
            }
3062
        }
3063 25
    }
3064
3065
    /**
3066
     * Sets whether this class is to be versioned for optimistic locking.
3067
     *
3068
     * @param boolean $bool
3069
     *
3070
     * @return void
3071
     */
3072 127
    public function setVersioned($bool)
3073
    {
3074 127
        $this->isVersioned = $bool;
3075 127
    }
3076
3077
    /**
3078
     * Sets the name of the field that is to be used for versioning if this class is
3079
     * versioned for optimistic locking.
3080
     *
3081
     * @param string $versionField
3082
     *
3083
     * @return void
3084
     */
3085 127
    public function setVersionField($versionField)
3086
    {
3087 127
        $this->versionField = $versionField;
3088 127
    }
3089
3090
    /**
3091
     * Marks this class as read only, no change tracking is applied to it.
3092
     *
3093
     * @return void
3094
     */
3095 3
    public function markReadOnly()
3096
    {
3097 3
        $this->isReadOnly = true;
3098 3
    }
3099
3100
    /**
3101
     * {@inheritDoc}
3102
     */
3103
    public function getFieldNames()
3104
    {
3105
        return array_keys($this->fieldMappings);
3106
    }
3107
3108
    /**
3109
     * {@inheritDoc}
3110
     */
3111
    public function getAssociationNames()
3112
    {
3113
        return array_keys($this->associationMappings);
3114
    }
3115
3116
    /**
3117
     * {@inheritDoc}
3118
     *
3119
     * @throws InvalidArgumentException
3120
     */
3121 1
    public function getAssociationTargetClass($assocName)
3122
    {
3123 1
        if ( ! isset($this->associationMappings[$assocName])) {
3124
            throw new InvalidArgumentException("Association name expected, '" . $assocName ."' is not an association.");
3125
        }
3126
3127 1
        return $this->associationMappings[$assocName]['targetEntity'];
3128
    }
3129
3130
    /**
3131
     * {@inheritDoc}
3132
     */
3133 731
    public function getName()
3134
    {
3135 731
        return $this->name;
3136
    }
3137
3138
    /**
3139
     * Gets the (possibly quoted) identifier column names for safe use in an SQL statement.
3140
     *
3141
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3142
     *
3143
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3144
     *
3145
     * @return array
3146
     */
3147
    public function getQuotedIdentifierColumnNames($platform)
3148
    {
3149
        $quotedColumnNames = array();
3150
3151
        foreach ($this->identifier as $idProperty) {
3152
            if (isset($this->fieldMappings[$idProperty])) {
3153
                $quotedColumnNames[] = isset($this->fieldMappings[$idProperty]['quoted'])
3154
                    ? $platform->quoteIdentifier($this->fieldMappings[$idProperty]['columnName'])
3155
                    : $this->fieldMappings[$idProperty]['columnName'];
3156
3157
                continue;
3158
            }
3159
3160
            // Association defined as Id field
3161
            $joinColumns            = $this->associationMappings[$idProperty]['joinColumns'];
3162
            $assocQuotedColumnNames = array_map(
3163
                function ($joinColumn) use ($platform) {
3164
                    return isset($joinColumn['quoted'])
3165
                        ? $platform->quoteIdentifier($joinColumn['name'])
3166
                        : $joinColumn['name'];
3167
                },
3168
                $joinColumns
3169
            );
3170
3171
            $quotedColumnNames = array_merge($quotedColumnNames, $assocQuotedColumnNames);
3172
        }
3173
3174
        return $quotedColumnNames;
3175
    }
3176
3177
    /**
3178
     * Gets the (possibly quoted) column name of a mapped field for safe use  in an SQL statement.
3179
     *
3180
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3181
     *
3182
     * @param string                                    $field
3183
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3184
     *
3185
     * @return string
3186
     */
3187
    public function getQuotedColumnName($field, $platform)
3188
    {
3189
        return isset($this->fieldMappings[$field]['quoted'])
3190
            ? $platform->quoteIdentifier($this->fieldMappings[$field]['columnName'])
3191
            : $this->fieldMappings[$field]['columnName'];
3192
    }
3193
3194
    /**
3195
     * Gets the (possibly quoted) primary table name of this class for safe use in an SQL statement.
3196
     *
3197
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3198
     *
3199
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3200
     *
3201
     * @return string
3202
     */
3203
    public function getQuotedTableName($platform)
3204
    {
3205
        return isset($this->table['quoted'])
3206
            ? $platform->quoteIdentifier($this->table['name'])
3207
            : $this->table['name'];
3208
    }
3209
3210
    /**
3211
     * Gets the (possibly quoted) name of the join table.
3212
     *
3213
     * @deprecated Deprecated since version 2.3 in favor of \Doctrine\ORM\Mapping\QuoteStrategy
3214
     *
3215
     * @param array                                     $assoc
3216
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
3217
     *
3218
     * @return string
3219
     */
3220
    public function getQuotedJoinTableName(array $assoc, $platform)
3221
    {
3222
        return isset($assoc['joinTable']['quoted'])
3223
            ? $platform->quoteIdentifier($assoc['joinTable']['name'])
3224
            : $assoc['joinTable']['name'];
3225
    }
3226
3227
    /**
3228
     * {@inheritDoc}
3229
     */
3230 12
    public function isAssociationInverseSide($fieldName)
3231
    {
3232 12
        return isset($this->associationMappings[$fieldName])
3233 12
            && ! $this->associationMappings[$fieldName]['isOwningSide'];
3234
    }
3235
3236
    /**
3237
     * {@inheritDoc}
3238
     */
3239
    public function getAssociationMappedByTargetField($fieldName)
3240
    {
3241
        return $this->associationMappings[$fieldName]['mappedBy'];
3242
    }
3243
3244
    /**
3245
     * @param string $targetClass
3246
     *
3247
     * @return array
3248
     */
3249 2
    public function getAssociationsByTargetClass($targetClass)
3250
    {
3251 2
        $relations = array();
3252
3253 2
        foreach ($this->associationMappings as $mapping) {
3254 2
            if ($mapping['targetEntity'] == $targetClass) {
3255 2
                $relations[$mapping['fieldName']] = $mapping;
3256
            }
3257
        }
3258
3259 2
        return $relations;
3260
    }
3261
3262
    /**
3263
     * @param  string|null $className
3264
     *
3265
     * @return string|null null if the input value is null
3266
     */
3267 504
    public function fullyQualifiedClassName($className)
3268
    {
3269 504
        if (empty($className)) {
3270 47
            return $className;
3271
        }
3272
3273 488
        if ($className !== null && strpos($className, '\\') === false && $this->namespace) {
3274 359
            return $this->namespace . '\\' . $className;
3275
        }
3276
3277 252
        return $className;
3278
    }
3279
3280
    /**
3281
     * @param string $name
3282
     *
3283
     * @return mixed
3284
     */
3285 2
    public function getMetadataValue($name)
3286
    {
3287
3288 2
        if (isset($this->$name)) {
3289 2
            return $this->$name;
3290
        }
3291
3292
        return null;
3293
    }
3294
3295
    /**
3296
     * Map Embedded Class
3297
     *
3298
     * @param array $mapping
3299
     *
3300
     * @throws MappingException
3301
     * @return void
3302
     */
3303 45
    public function mapEmbedded(array $mapping)
3304
    {
3305 45
        $this->assertFieldNotMapped($mapping['fieldName']);
3306
3307 45
        $this->embeddedClasses[$mapping['fieldName']] = array(
3308 45
            'class' => $this->fullyQualifiedClassName($mapping['class']),
3309 45
            'columnPrefix' => $mapping['columnPrefix'],
3310 45
            'declaredField' => isset($mapping['declaredField']) ? $mapping['declaredField'] : null,
3311 45
            'originalField' => isset($mapping['originalField']) ? $mapping['originalField'] : null,
3312 45
            'attributeOverrides' => isset($mapping['attributeOverrides']) ? $mapping['attributeOverrides'] : null,
3313
        );
3314 45
    }
3315
3316
    /**
3317
     * Inline the embeddable class
3318
     *
3319
     * @param string            $property
3320
     * @param ClassMetadataInfo $embeddable
3321
     */
3322 11
    public function inlineEmbeddable($property, ClassMetadataInfo $embeddable)
3323
    {
3324 11
        foreach ($embeddable->fieldMappings as $fieldMapping) {
3325 11
            $fieldMapping['originalClass'] = isset($fieldMapping['originalClass'])
3326 4
                ? $fieldMapping['originalClass']
3327 11
                : $embeddable->name;
3328 11
            $fieldMapping['declaredField'] = isset($fieldMapping['declaredField'])
3329 4
                ? $property . '.' . $fieldMapping['declaredField']
3330 11
                : $property;
3331 11
            $fieldMapping['originalField'] = isset($fieldMapping['originalField'])
3332 4
                ? $fieldMapping['originalField']
3333 11
                : $fieldMapping['fieldName'];
3334 11
            $fieldMapping['fieldName'] = $property . "." . $fieldMapping['fieldName'];
3335
3336 11
            if (! empty($this->embeddedClasses[$property]['columnPrefix'])) {
3337 2
                $fieldMapping['columnName'] = $this->embeddedClasses[$property]['columnPrefix'] . $fieldMapping['columnName'];
3338 10
            } elseif ($this->embeddedClasses[$property]['columnPrefix'] !== false) {
3339 7
                $fieldMapping['columnName'] = $this->namingStrategy
3340 7
                    ->embeddedFieldToColumnName(
3341
                        $property,
3342 7
                        $fieldMapping['columnName'],
3343 7
                        $this->reflClass->name,
3344 7
                        $embeddable->reflClass->name
3345
                    );
3346
            }
3347
3348 11
            $this->mapField($fieldMapping);
3349
        }
3350 11
    }
3351
3352
    /**
3353
     * @param string $fieldName
3354
     * @throws MappingException
3355
     */
3356 601
    private function assertFieldNotMapped($fieldName)
3357
    {
3358 601
        if (isset($this->fieldMappings[$fieldName]) ||
3359 601
            isset($this->associationMappings[$fieldName]) ||
3360 601
            isset($this->embeddedClasses[$fieldName])) {
3361
3362 2
            throw MappingException::duplicateFieldMapping($this->name, $fieldName);
3363
        }
3364 601
    }
3365
3366
    /**
3367
     * Gets the sequence name based on class metadata.
3368
     *
3369
     * @param AbstractPlatform $platform
3370
     * @return string
3371
     *
3372
     * @todo Sequence names should be computed in DBAL depending on the platform
3373
     */
3374 3
    public function getSequenceName(AbstractPlatform $platform)
3375
    {
3376 3
        $sequencePrefix = $this->getSequencePrefix($platform);
3377 3
        $columnName     = $this->getSingleIdentifierColumnName();
3378 3
        $sequenceName   = $sequencePrefix . '_' . $columnName . '_seq';
3379
3380 3
        return $sequenceName;
3381
    }
3382
3383
    /**
3384
     * Gets the sequence name prefix based on class metadata.
3385
     *
3386
     * @param AbstractPlatform $platform
3387
     * @return string
3388
     *
3389
     * @todo Sequence names should be computed in DBAL depending on the platform
3390
     */
3391 3
    public function getSequencePrefix(AbstractPlatform $platform)
3392
    {
3393 3
        $tableName      = $this->getTableName();
3394 3
        $sequencePrefix = $tableName;
3395
3396
        // Prepend the schema name to the table name if there is one
3397 3
        if ($schemaName = $this->getSchemaName()) {
3398 3
            $sequencePrefix = $schemaName . '.' . $tableName;
3399
3400 3
            if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) {
3401 3
                $sequencePrefix = $schemaName . '__' . $tableName;
3402
            }
3403
        }
3404
3405 3
        return $sequencePrefix;
3406
    }
3407
3408
    /**
3409
     * @param array $mapping
3410
     */
3411 214
    private function assertMappingOrderBy(array $mapping)
3412
    {
3413 214
        if (isset($mapping['orderBy']) && !is_array($mapping['orderBy'])) {
3414
            throw new InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy']));
3415
        }
3416 214
    }
3417
}
3418