Completed
Pull Request — master (#1620)
by Maciej
09:01
created

getAssociationMappedByTargetField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
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\ODM\MongoDB\Mapping;
21
22
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
23
use Doctrine\ODM\MongoDB\LockException;
24
use Doctrine\ODM\MongoDB\Proxy\Proxy;
25
use Doctrine\ODM\MongoDB\Types\Type;
26
use InvalidArgumentException;
27
28
/**
29
 * A <tt>ClassMetadata</tt> instance holds all the object-document mapping metadata
30
 * of a document and it's references.
31
 *
32
 * Once populated, ClassMetadata instances are usually cached in a serialized form.
33
 *
34
 * <b>IMPORTANT NOTE:</b>
35
 *
36
 * The fields of this class are only public for 2 reasons:
37
 * 1) To allow fast READ access.
38
 * 2) To drastically reduce the size of a serialized instance (private/protected members
39
 *    get the whole class name, namespace inclusive, prepended to every property in
40
 *    the serialized representation).
41
 *
42
 * @since       1.0
43
 */
44
class ClassMetadataInfo implements \Doctrine\Common\Persistence\Mapping\ClassMetadata
45
{
46
    /* The Id generator types. */
47
    /**
48
     * AUTO means Doctrine will automatically create a new \MongoId instance for us.
49
     */
50
    const GENERATOR_TYPE_AUTO = 1;
51
52
    /**
53
     * INCREMENT means a separate collection is used for maintaining and incrementing id generation.
54
     * Offers full portability.
55
     */
56
    const GENERATOR_TYPE_INCREMENT = 2;
57
58
    /**
59
     * UUID means Doctrine will generate a uuid for us.
60
     */
61
    const GENERATOR_TYPE_UUID = 3;
62
63
    /**
64
     * ALNUM means Doctrine will generate Alpha-numeric string identifiers, using the INCREMENT
65
     * generator to ensure identifier uniqueness
66
     */
67
    const GENERATOR_TYPE_ALNUM = 4;
68
69
    /**
70
     * CUSTOM means Doctrine expect a class parameter. It will then try to initiate that class
71
     * and pass other options to the generator. It will throw an Exception if the class
72
     * does not exist or if an option was passed for that there is not setter in the new
73
     * generator class.
74
     *
75
     * The class  will have to be a subtype of AbstractIdGenerator.
76
     */
77
    const GENERATOR_TYPE_CUSTOM = 5;
78
79
    /**
80
     * NONE means Doctrine will not generate any id for us and you are responsible for manually
81
     * assigning an id.
82
     */
83
    const GENERATOR_TYPE_NONE = 6;
84
85
    /**
86
     * Default discriminator field name.
87
     *
88
     * This is used for associations value for associations where a that do not define a "targetDocument" or
89
     * "discriminatorField" option in their mapping.
90
     */
91
    const DEFAULT_DISCRIMINATOR_FIELD = '_doctrine_class_name';
92
93
    const REFERENCE_ONE = 1;
94
    const REFERENCE_MANY = 2;
95
    const EMBED_ONE = 3;
96
    const EMBED_MANY = 4;
97
    const MANY = 'many';
98
    const ONE = 'one';
99
100
    /**
101
     * The types of storeAs references
102
     */
103
    const REFERENCE_STORE_AS_ID = 'id';
104
    const REFERENCE_STORE_AS_DB_REF = 'dbRef';
105
    const REFERENCE_STORE_AS_DB_REF_WITH_DB = 'dbRefWithDb';
106
107
    /* The inheritance mapping types */
108
    /**
109
     * NONE means the class does not participate in an inheritance hierarchy
110
     * and therefore does not need an inheritance mapping type.
111
     */
112
    const INHERITANCE_TYPE_NONE = 1;
113
114
    /**
115
     * SINGLE_COLLECTION means the class will be persisted according to the rules of
116
     * <tt>Single Collection Inheritance</tt>.
117
     */
118
    const INHERITANCE_TYPE_SINGLE_COLLECTION = 2;
119
120
    /**
121
     * COLLECTION_PER_CLASS means the class will be persisted according to the rules
122
     * of <tt>Concrete Collection Inheritance</tt>.
123
     */
124
    const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3;
125
126
    /**
127
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
128
     * by doing a property-by-property comparison with the original data. This will
129
     * be done for all entities that are in MANAGED state at commit-time.
130
     *
131
     * This is the default change tracking policy.
132
     */
133
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
134
135
    /**
136
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
137
     * by doing a property-by-property comparison with the original data. This will
138
     * be done only for entities that were explicitly saved (through persist() or a cascade).
139
     */
140
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
141
142
    /**
143
     * NOTIFY means that Doctrine relies on the entities sending out notifications
144
     * when their properties change. Such entity classes must implement
145
     * the <tt>NotifyPropertyChanged</tt> interface.
146
     */
147
    const CHANGETRACKING_NOTIFY = 3;
148
149
    /**
150
     * SET means that fields will be written to the database using a $set operator
151
     */
152
    const STORAGE_STRATEGY_SET = 'set';
153
154
    /**
155
     * INCREMENT means that fields will be written to the database by calculating
156
     * the difference and using the $inc operator
157
     */
158
    const STORAGE_STRATEGY_INCREMENT = 'increment';
159
160
    const STORAGE_STRATEGY_PUSH_ALL = 'pushAll';
161
    const STORAGE_STRATEGY_ADD_TO_SET = 'addToSet';
162
    const STORAGE_STRATEGY_ATOMIC_SET = 'atomicSet';
163
    const STORAGE_STRATEGY_ATOMIC_SET_ARRAY = 'atomicSetArray';
164
    const STORAGE_STRATEGY_SET_ARRAY = 'setArray';
165
166
    /**
167
     * READ-ONLY: The name of the mongo database the document is mapped to.
168
     */
169
    public $db;
170
171
    /**
172
     * READ-ONLY: The name of the mongo collection the document is mapped to.
173
     */
174
    public $collection;
175
176
    /**
177
     * READ-ONLY: If the collection should be a fixed size.
178
     */
179
    public $collectionCapped;
180
181
    /**
182
     * READ-ONLY: If the collection is fixed size, its size in bytes.
183
     */
184
    public $collectionSize;
185
186
    /**
187
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
188
     */
189
    public $collectionMax;
190
191
    /**
192
     * READ-ONLY Describes how MongoDB clients route read operations to the members of a replica set.
193
     */
194
    public $readPreference;
195
196
    /**
197
     * READ-ONLY Associated with readPreference Allows to specify criteria so that your application can target read
198
     * operations to specific members, based on custom parameters.
199
     */
200
    public $readPreferenceTags;
201
202
    /**
203
     * READ-ONLY: Describes the level of acknowledgement requested from MongoDB for write operations.
204
     */
205
    public $writeConcern;
206
207
    /**
208
     * READ-ONLY: The field name of the document identifier.
209
     */
210
    public $identifier;
211
212
    /**
213
     * READ-ONLY: The field that stores a file reference and indicates the
214
     * document is a file and should be stored on the MongoGridFS.
215
     */
216
    public $file;
217
218
    /**
219
     * READ-ONLY: The field that stores the calculated distance when performing geo spatial
220
     * queries.
221
     */
222
    public $distance;
223
224
    /**
225
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
226
     *
227
     * @deprecated in version 1.2 and will be removed in 2.0.
228
     */
229
    public $slaveOkay;
230
231
    /**
232
     * READ-ONLY: The array of indexes for the document collection.
233
     */
234
    public $indexes = array();
235
236
    /**
237
     * READ-ONLY: Keys and options describing shard key. Only for sharded collections.
238
     */
239
    public $shardKey;
240
241
    /**
242
     * READ-ONLY: Whether or not queries on this document should require indexes.
243
     *
244
     * @deprecated property was deprecated in 1.2 and will be removed in 2.0
245
     */
246
    public $requireIndexes = false;
247
248
    /**
249
     * READ-ONLY: The name of the document class.
250
     */
251
    public $name;
252
253
    /**
254
     * READ-ONLY: The namespace the document class is contained in.
255
     *
256
     * @var string
257
     * @todo Not really needed. Usage could be localized.
258
     */
259
    public $namespace;
260
261
    /**
262
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
263
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
264
     * as {@link $documentName}.
265
     *
266
     * @var string
267
     */
268
    public $rootDocumentName;
269
270
    /**
271
     * The name of the custom repository class used for the document class.
272
     * (Optional).
273
     *
274
     * @var string
275
     */
276
    public $customRepositoryClassName;
277
278
    /**
279
     * READ-ONLY: The names of the parent classes (ancestors).
280
     *
281
     * @var array
282
     */
283
    public $parentClasses = array();
284
285
    /**
286
     * READ-ONLY: The names of all subclasses (descendants).
287
     *
288
     * @var array
289
     */
290
    public $subClasses = array();
291
292
    /**
293
     * The ReflectionProperty instances of the mapped class.
294
     *
295
     * @var \ReflectionProperty[]
296
     */
297
    public $reflFields = array();
298
299
    /**
300
     * READ-ONLY: The inheritance mapping type used by the class.
301
     *
302
     * @var integer
303
     */
304
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
305
306
    /**
307
     * READ-ONLY: The Id generator type used by the class.
308
     *
309
     * @var string
310
     */
311
    public $generatorType = self::GENERATOR_TYPE_AUTO;
312
313
    /**
314
     * READ-ONLY: The Id generator options.
315
     *
316
     * @var array
317
     */
318
    public $generatorOptions = array();
319
320
    /**
321
     * READ-ONLY: The ID generator used for generating IDs for this class.
322
     *
323
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
324
     */
325
    public $idGenerator;
326
327
    /**
328
     * READ-ONLY: The field mappings of the class.
329
     * Keys are field names and values are mapping definitions.
330
     *
331
     * The mapping definition array has the following values:
332
     *
333
     * - <b>fieldName</b> (string)
334
     * The name of the field in the Document.
335
     *
336
     * - <b>id</b> (boolean, optional)
337
     * Marks the field as the primary key of the document. Multiple fields of an
338
     * document can have the id attribute, forming a composite key.
339
     *
340
     * @var array
341
     */
342
    public $fieldMappings = array();
343
344
    /**
345
     * READ-ONLY: The association mappings of the class.
346
     * Keys are field names and values are mapping definitions.
347
     *
348
     * @var array
349
     */
350
    public $associationMappings = array();
351
352
    /**
353
     * READ-ONLY: Array of fields to also load with a given method.
354
     *
355
     * @var array
356
     */
357
    public $alsoLoadMethods = array();
358
359
    /**
360
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
361
     *
362
     * @var array
363
     */
364
    public $lifecycleCallbacks = array();
365
366
    /**
367
     * READ-ONLY: The discriminator value of this class.
368
     *
369
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
370
     * where a discriminator field is used.</b>
371
     *
372
     * @var mixed
373
     * @see discriminatorField
374
     */
375
    public $discriminatorValue;
376
377
    /**
378
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
379
     *
380
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
381
     * where a discriminator field is used.</b>
382
     *
383
     * @var mixed
384
     * @see discriminatorField
385
     */
386
    public $discriminatorMap = array();
387
388
    /**
389
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
390
     * inheritance mapping.
391
     *
392
     * @var string
393
     */
394
    public $discriminatorField;
395
396
    /**
397
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
398
     *
399
     * @var string
400
     * @see discriminatorField
401
     */
402
    public $defaultDiscriminatorValue;
403
404
    /**
405
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
406
     *
407
     * @var boolean
408
     */
409
    public $isMappedSuperclass = false;
410
411
    /**
412
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
413
     *
414
     * @var boolean
415
     */
416
    public $isEmbeddedDocument = false;
417
418
    /**
419
     * READ-ONLY: Whether this class describes the mapping of an aggregation result document.
420
     *
421
     * @var boolean
422
     */
423
    public $isQueryResultDocument = false;
424
425
    /**
426
     * READ-ONLY: The policy used for change-tracking on entities of this class.
427
     *
428
     * @var integer
429
     */
430
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
431
432
    /**
433
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
434
     * with optimistic locking.
435
     *
436
     * @var boolean $isVersioned
437
     */
438
    public $isVersioned;
439
440
    /**
441
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
442
     *
443
     * @var mixed $versionField
444
     */
445
    public $versionField;
446
447
    /**
448
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
449
     * locking.
450
     *
451
     * @var boolean $isLockable
452
     */
453
    public $isLockable;
454
455
    /**
456
     * READ-ONLY: The name of the field which is used for locking a document.
457
     *
458
     * @var mixed $lockField
459
     */
460
    public $lockField;
461
462
    /**
463
     * The ReflectionClass instance of the mapped class.
464
     *
465
     * @var \ReflectionClass
466
     */
467
    public $reflClass;
468
469
    /**
470
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
471
     * metadata of the class with the given name.
472
     *
473
     * @param string $documentName The name of the document class the new instance is used for.
474
     */
475 975
    public function __construct($documentName)
476
    {
477 975
        $this->name = $documentName;
478 975
        $this->rootDocumentName = $documentName;
479 975
    }
480
481
    /**
482
     * {@inheritDoc}
483
     */
484 903
    public function getReflectionClass()
485
    {
486 903
        if ( ! $this->reflClass) {
487 2
            $this->reflClass = new \ReflectionClass($this->name);
488
        }
489
490 903
        return $this->reflClass;
491
    }
492
493
    /**
494
     * {@inheritDoc}
495
     */
496 330
    public function isIdentifier($fieldName)
497
    {
498 330
        return $this->identifier === $fieldName;
499
    }
500
501
    /**
502
     * INTERNAL:
503
     * Sets the mapped identifier field of this class.
504
     *
505
     * @param string $identifier
506
     */
507 370
    public function setIdentifier($identifier)
508
    {
509 370
        $this->identifier = $identifier;
510 370
    }
511
512
    /**
513
     * {@inheritDoc}
514
     *
515
     * Since MongoDB only allows exactly one identifier field
516
     * this will always return an array with only one value
517
     */
518 40
    public function getIdentifier()
519
    {
520 40
        return array($this->identifier);
521
    }
522
523
    /**
524
     * {@inheritDoc}
525
     *
526
     * Since MongoDB only allows exactly one identifier field
527
     * this will always return an array with only one value
528
     */
529 98
    public function getIdentifierFieldNames()
530
    {
531 98
        return array($this->identifier);
532
    }
533
534
    /**
535
     * {@inheritDoc}
536
     */
537 561
    public function hasField($fieldName)
538
    {
539 561
        return isset($this->fieldMappings[$fieldName]);
540
    }
541
542
    /**
543
     * Sets the inheritance type used by the class and it's subclasses.
544
     *
545
     * @param integer $type
546
     */
547 386
    public function setInheritanceType($type)
548
    {
549 386
        $this->inheritanceType = $type;
550 386
    }
551
552
    /**
553
     * Checks whether a mapped field is inherited from an entity superclass.
554
     *
555
     * @param  string $fieldName
556
     *
557
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
558
     */
559 903
    public function isInheritedField($fieldName)
560
    {
561 903
        return isset($this->fieldMappings[$fieldName]['inherited']);
562
    }
563
564
    /**
565
     * Registers a custom repository class for the document class.
566
     *
567
     * @param string $repositoryClassName The class name of the custom repository.
568
     */
569 318
    public function setCustomRepositoryClass($repositoryClassName)
570
    {
571 318
        if ($this->isEmbeddedDocument || $this->isQueryResultDocument) {
572
            return;
573
        }
574
575 318 View Code Duplication
        if ($repositoryClassName && strpos($repositoryClassName, '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
576 4
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
577
        }
578
579 318
        $this->customRepositoryClassName = $repositoryClassName;
580 318
    }
581
582
    /**
583
     * Dispatches the lifecycle event of the given document by invoking all
584
     * registered callbacks.
585
     *
586
     * @param string $event     Lifecycle event
587
     * @param object $document  Document on which the event occurred
588
     * @param array  $arguments Arguments to pass to all callbacks
589
     * @throws \InvalidArgumentException if document class is not this class or
590
     *                                   a Proxy of this class
591
     */
592 660
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
593
    {
594 660
        if ( ! $document instanceof $this->name) {
595 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
596
        }
597
598 659
        if (empty($this->lifecycleCallbacks[$event])) {
599 645
            return;
600
        }
601
602 196
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
603 196
            if ($arguments !== null) {
604 195
                call_user_func_array(array($document, $callback), $arguments);
605
            } else {
606 196
                $document->$callback();
607
            }
608
        }
609 196
    }
610
611
    /**
612
     * Checks whether the class has callbacks registered for a lifecycle event.
613
     *
614
     * @param string $event Lifecycle event
615
     *
616
     * @return boolean
617
     */
618
    public function hasLifecycleCallbacks($event)
619
    {
620
        return ! empty($this->lifecycleCallbacks[$event]);
621
    }
622
623
    /**
624
     * Gets the registered lifecycle callbacks for an event.
625
     *
626
     * @param string $event
627
     * @return array
628
     */
629
    public function getLifecycleCallbacks($event)
630
    {
631
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
632
    }
633
634
    /**
635
     * Adds a lifecycle callback for documents of this class.
636
     *
637
     * If the callback is already registered, this is a NOOP.
638
     *
639
     * @param string $callback
640
     * @param string $event
641
     */
642 297
    public function addLifecycleCallback($callback, $event)
643
    {
644 297
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
645 1
            return;
646
        }
647
648 297
        $this->lifecycleCallbacks[$event][] = $callback;
649 297
    }
650
651
    /**
652
     * Sets the lifecycle callbacks for documents of this class.
653
     *
654
     * Any previously registered callbacks are overwritten.
655
     *
656
     * @param array $callbacks
657
     */
658 369
    public function setLifecycleCallbacks(array $callbacks)
659
    {
660 369
        $this->lifecycleCallbacks = $callbacks;
661 369
    }
662
663
    /**
664
     * Registers a method for loading document data before field hydration.
665
     *
666
     * Note: A method may be registered multiple times for different fields.
667
     * it will be invoked only once for the first field found.
668
     *
669
     * @param string       $method Method name
670
     * @param array|string $fields Database field name(s)
671
     */
672 15
    public function registerAlsoLoadMethod($method, $fields)
673
    {
674 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
675 15
    }
676
677
    /**
678
     * Sets the AlsoLoad methods for documents of this class.
679
     *
680
     * Any previously registered methods are overwritten.
681
     *
682
     * @param array $methods
683
     */
684 369
    public function setAlsoLoadMethods(array $methods)
685
    {
686 369
        $this->alsoLoadMethods = $methods;
687 369
    }
688
689
    /**
690
     * Sets the discriminator field.
691
     *
692
     * The field name is the the unmapped database field. Discriminator values
693
     * are only used to discern the hydration class and are not mapped to class
694
     * properties.
695
     *
696
     * @param string $discriminatorField
697
     *
698
     * @throws MappingException If the discriminator field conflicts with the
699
     *                          "name" attribute of a mapped field.
700
     */
701 399
    public function setDiscriminatorField($discriminatorField)
702
    {
703 399
        if ($discriminatorField === null) {
704 326
            $this->discriminatorField = null;
705
706 326
            return;
707
        }
708
709
        // Handle array argument with name/fieldName keys for BC
710 130
        if (is_array($discriminatorField)) {
711
            if (isset($discriminatorField['name'])) {
712
                $discriminatorField = $discriminatorField['name'];
713
            } elseif (isset($discriminatorField['fieldName'])) {
714
                $discriminatorField = $discriminatorField['fieldName'];
715
            }
716
        }
717
718 130
        foreach ($this->fieldMappings as $fieldMapping) {
719 4
            if ($discriminatorField == $fieldMapping['name']) {
720 4
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
721
            }
722
        }
723
724 129
        $this->discriminatorField = $discriminatorField;
725 129
    }
726
727
    /**
728
     * Sets the discriminator values used by this class.
729
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
730
     *
731
     * @param array $map
732
     *
733
     * @throws MappingException
734
     */
735 392
    public function setDiscriminatorMap(array $map)
736
    {
737 392
        foreach ($map as $value => $className) {
738 125
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
739 91
                $className = $this->namespace . '\\' . $className;
740
            }
741 125
            $this->discriminatorMap[$value] = $className;
742 125
            if ($this->name == $className) {
743 117
                $this->discriminatorValue = $value;
744
            } else {
745 120
                if ( ! class_exists($className)) {
746
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
747
                }
748 120
                if (is_subclass_of($className, $this->name)) {
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...
749 125
                    $this->subClasses[] = $className;
750
                }
751
            }
752
        }
753 392
    }
754
755
    /**
756
     * Sets the default discriminator value to be used for this class
757
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
758
     *
759
     * @param string $defaultDiscriminatorValue
760
     *
761
     * @throws MappingException
762
     */
763 376
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
764
    {
765 376
        if ($defaultDiscriminatorValue === null) {
766 369
            $this->defaultDiscriminatorValue = null;
767
768 369
            return;
769
        }
770
771 60
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
772
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
773
        }
774
775 60
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
776 60
    }
777
778
    /**
779
     * Sets the discriminator value for this class.
780
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
781
     * collection.
782
     *
783
     * @param string $value
784
     */
785 3
    public function setDiscriminatorValue($value)
786
    {
787 3
        $this->discriminatorMap[$value] = $this->name;
788 3
        $this->discriminatorValue = $value;
789 3
    }
790
791
    /**
792
     * Sets the slaveOkay option applied to collections for this class.
793
     *
794
     * @param boolean|null $slaveOkay
795
     *
796
     * @deprecated in version 1.2 and will be removed in 2.0.
797
     */
798 3
    public function setSlaveOkay($slaveOkay)
799
    {
800 3
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
801 3
            sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.'),
802 3
            E_USER_DEPRECATED
803
        );
804 3
        $this->slaveOkay = $slaveOkay === null ? null : (boolean) $slaveOkay;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...etadataInfo::$slaveOkay has been deprecated with message: in version 1.2 and will be removed in 2.0.

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...
805 3
    }
806
807
    /**
808
     * Add a index for this Document.
809
     *
810
     * @param array $keys Array of keys for the index.
811
     * @param array $options Array of options for the index.
812
     */
813 232
    public function addIndex($keys, array $options = array())
814
    {
815 232
        $this->indexes[] = array(
816 View Code Duplication
            'keys' => array_map(function($value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
817 232
                if ($value == 1 || $value == -1) {
818 63
                    return (int) $value;
819
                }
820 224
                if (is_string($value)) {
821 224
                    $lower = strtolower($value);
822 224
                    if ($lower === 'asc') {
823 217
                        return 1;
824 11
                    } elseif ($lower === 'desc') {
825 4
                        return -1;
826
                    }
827
                }
828 7
                return $value;
829 232
            }, $keys),
830 232
            'options' => $options
831
        );
832 232
    }
833
834
    /**
835
     * Set whether or not queries on this document should require indexes.
836
     *
837
     * @param bool $requireIndexes
838
     *
839
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
840
     */
841 894
    public function setRequireIndexes($requireIndexes)
842
    {
843 894
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
844 894
            'requireIndexes was deprecated in version 1.2 and will be removed altogether in 2.0.',
845 894
            E_USER_DEPRECATED
846
        );
847 894
        $this->requireIndexes = $requireIndexes;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...taInfo::$requireIndexes has been deprecated with message: property was deprecated in 1.2 and will be removed in 2.0

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...
848 894
    }
849
850
    /**
851
     * Returns the array of indexes for this Document.
852
     *
853
     * @return array $indexes The array of indexes.
854
     */
855 54
    public function getIndexes()
856
    {
857 54
        return $this->indexes;
858
    }
859
860
    /**
861
     * Checks whether this document has indexes or not.
862
     *
863
     * @return boolean
864
     */
865
    public function hasIndexes()
866
    {
867
        return $this->indexes ? true : false;
868
    }
869
870
    /**
871
     * Set shard key for this Document.
872
     *
873
     * @param array $keys Array of document keys.
874
     * @param array $options Array of sharding options.
875
     *
876
     * @throws MappingException
877
     */
878 77
    public function setShardKey(array $keys, array $options = array())
879
    {
880 77
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
881 2
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
882
        }
883
884 77
        if ($this->isEmbeddedDocument) {
885 2
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
886
        }
887
888 75
        foreach (array_keys($keys) as $field) {
889 75
            if (! isset($this->fieldMappings[$field])) {
890 68
                continue;
891
            }
892
893 7
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
894 3
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
895
            }
896
897 4
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
898 4
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
899
            }
900
        }
901
902 71
        $this->shardKey = array(
903 View Code Duplication
            'keys' => array_map(function($value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
904 71
                if ($value == 1 || $value == -1) {
905 6
                    return (int) $value;
906
                }
907 70
                if (is_string($value)) {
908 70
                    $lower = strtolower($value);
909 70
                    if ($lower === 'asc') {
910 69
                        return 1;
911 53
                    } elseif ($lower === 'desc') {
912
                        return -1;
913
                    }
914
                }
915 53
                return $value;
916 71
            }, $keys),
917 71
            'options' => $options
918
        );
919 71
    }
920
921
    /**
922
     * @return array
923
     */
924 18
    public function getShardKey()
925
    {
926 18
        return $this->shardKey;
927
    }
928
929
    /**
930
     * Checks whether this document has shard key or not.
931
     *
932
     * @return bool
933
     */
934 598
    public function isSharded()
935
    {
936 598
        return $this->shardKey ? true : false;
937
    }
938
939
    /**
940
     * Sets the read preference used by this class.
941
     *
942
     * @param string $readPreference
943
     * @param array|null $tags
944
     */
945 373
    public function setReadPreference($readPreference, $tags)
946
    {
947 373
        $this->readPreference = $readPreference;
948 373
        $this->readPreferenceTags = $tags;
949 373
    }
950
951
    /**
952
     * Sets the write concern used by this class.
953
     *
954
     * @param string $writeConcern
955
     */
956 383
    public function setWriteConcern($writeConcern)
957
    {
958 383
        $this->writeConcern = $writeConcern;
959 383
    }
960
961
    /**
962
     * @return string
963
     */
964 12
    public function getWriteConcern()
965
    {
966 12
        return $this->writeConcern;
967
    }
968
969
    /**
970
     * Whether there is a write concern configured for this class.
971
     *
972
     * @return bool
973
     */
974 604
    public function hasWriteConcern()
975
    {
976 604
        return $this->writeConcern !== null;
977
    }
978
979
    /**
980
     * Sets the change tracking policy used by this class.
981
     *
982
     * @param integer $policy
983
     */
984 374
    public function setChangeTrackingPolicy($policy)
985
    {
986 374
        $this->changeTrackingPolicy = $policy;
987 374
    }
988
989
    /**
990
     * Whether the change tracking policy of this class is "deferred explicit".
991
     *
992
     * @return boolean
993
     */
994 73
    public function isChangeTrackingDeferredExplicit()
995
    {
996 73
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
997
    }
998
999
    /**
1000
     * Whether the change tracking policy of this class is "deferred implicit".
1001
     *
1002
     * @return boolean
1003
     */
1004 624
    public function isChangeTrackingDeferredImplicit()
1005
    {
1006 624
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1007
    }
1008
1009
    /**
1010
     * Whether the change tracking policy of this class is "notify".
1011
     *
1012
     * @return boolean
1013
     */
1014 347
    public function isChangeTrackingNotify()
1015
    {
1016 347
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1017
    }
1018
1019
    /**
1020
     * Gets the ReflectionProperties of the mapped class.
1021
     *
1022
     * @return array An array of ReflectionProperty instances.
1023
     */
1024 98
    public function getReflectionProperties()
1025
    {
1026 98
        return $this->reflFields;
1027
    }
1028
1029
    /**
1030
     * Gets a ReflectionProperty for a specific field of the mapped class.
1031
     *
1032
     * @param string $name
1033
     *
1034
     * @return \ReflectionProperty
1035
     */
1036
    public function getReflectionProperty($name)
1037
    {
1038
        return $this->reflFields[$name];
1039
    }
1040
1041
    /**
1042
     * {@inheritDoc}
1043
     */
1044 909
    public function getName()
1045
    {
1046 909
        return $this->name;
1047
    }
1048
1049
    /**
1050
     * The namespace this Document class belongs to.
1051
     *
1052
     * @return string $namespace The namespace name.
1053
     */
1054
    public function getNamespace()
1055
    {
1056
        return $this->namespace;
1057
    }
1058
1059
    /**
1060
     * Returns the database this Document is mapped to.
1061
     *
1062
     * @return string $db The database name.
1063
     */
1064 829
    public function getDatabase()
1065
    {
1066 829
        return $this->db;
1067
    }
1068
1069
    /**
1070
     * Set the database this Document is mapped to.
1071
     *
1072
     * @param string $db The database name
1073
     */
1074 104
    public function setDatabase($db)
1075
    {
1076 104
        $this->db = $db;
1077 104
    }
1078
1079
    /**
1080
     * Get the collection this Document is mapped to.
1081
     *
1082
     * @return string $collection The collection name.
1083
     */
1084 834
    public function getCollection()
1085
    {
1086 834
        return $this->collection;
1087
    }
1088
1089
    /**
1090
     * Sets the collection this Document is mapped to.
1091
     *
1092
     * @param array|string $name
1093
     *
1094
     * @throws \InvalidArgumentException
1095
     */
1096 939
    public function setCollection($name)
1097
    {
1098 939
        if (is_array($name)) {
1099
            if ( ! isset($name['name'])) {
1100
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
1101
            }
1102
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
1103
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
1104
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
1105
            $this->collection = $name['name'];
1106
        } else {
1107 939
            $this->collection = $name;
1108
        }
1109 939
    }
1110
1111
    /**
1112
     * Get whether or not the documents collection is capped.
1113
     *
1114
     * @return boolean
1115
     */
1116 4
    public function getCollectionCapped()
1117
    {
1118 4
        return $this->collectionCapped;
1119
    }
1120
1121
    /**
1122
     * Set whether or not the documents collection is capped.
1123
     *
1124
     * @param boolean $bool
1125
     */
1126 1
    public function setCollectionCapped($bool)
1127
    {
1128 1
        $this->collectionCapped = $bool;
1129 1
    }
1130
1131
    /**
1132
     * Get the collection size
1133
     *
1134
     * @return integer
1135
     */
1136 4
    public function getCollectionSize()
1137
    {
1138 4
        return $this->collectionSize;
1139
    }
1140
1141
    /**
1142
     * Set the collection size.
1143
     *
1144
     * @param integer $size
1145
     */
1146 1
    public function setCollectionSize($size)
1147
    {
1148 1
        $this->collectionSize = $size;
1149 1
    }
1150
1151
    /**
1152
     * Get the collection max.
1153
     *
1154
     * @return integer
1155
     */
1156 4
    public function getCollectionMax()
1157
    {
1158 4
        return $this->collectionMax;
1159
    }
1160
1161
    /**
1162
     * Set the collection max.
1163
     *
1164
     * @param integer $max
1165
     */
1166 1
    public function setCollectionMax($max)
1167
    {
1168 1
        $this->collectionMax = $max;
1169 1
    }
1170
1171
    /**
1172
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1173
     *
1174
     * @return boolean
1175
     */
1176
    public function isMappedToCollection()
1177
    {
1178
        return $this->collection ? true : false;
1179
    }
1180
1181
    /**
1182
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1183
     *
1184
     * @return boolean
1185
     */
1186 775
    public function isFile()
1187
    {
1188 775
        return $this->file ? true : false;
1189
    }
1190
1191
    /**
1192
     * Returns the file field name.
1193
     *
1194
     * @return string $file The file field name.
1195
     */
1196 369
    public function getFile()
1197
    {
1198 369
        return $this->file;
1199
    }
1200
1201
    /**
1202
     * Set the field name that stores the grid file.
1203
     *
1204
     * @param string $file
1205
     */
1206 370
    public function setFile($file)
1207
    {
1208 370
        $this->file = $file;
1209 370
    }
1210
1211
    /**
1212
     * Returns the distance field name.
1213
     *
1214
     * @return string $distance The distance field name.
1215
     */
1216
    public function getDistance()
1217
    {
1218
        return $this->distance;
1219
    }
1220
1221
    /**
1222
     * Set the field name that stores the distance.
1223
     *
1224
     * @param string $distance
1225
     */
1226 1
    public function setDistance($distance)
1227
    {
1228 1
        $this->distance = $distance;
1229 1
    }
1230
1231
    /**
1232
     * Map a field.
1233
     *
1234
     * @param array $mapping The mapping information.
1235
     *
1236
     * @return array
1237
     *
1238
     * @throws MappingException
1239
     */
1240 953
    public function mapField(array $mapping)
1241
    {
1242 953
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1243 10
            $mapping['fieldName'] = $mapping['name'];
1244
        }
1245 953
        if ( ! isset($mapping['fieldName'])) {
1246
            throw MappingException::missingFieldName($this->name);
1247
        }
1248 953
        if ( ! isset($mapping['name'])) {
1249 943
            $mapping['name'] = $mapping['fieldName'];
1250
        }
1251 953
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1252 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1253
        }
1254 952
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1255
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1256
        }
1257 952
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1258 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1259
        }
1260 951 View Code Duplication
        if (isset($mapping['targetDocument']) && strpos($mapping['targetDocument'], '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1261 616
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1262
        }
1263 951
        if (isset($mapping['collectionClass'])) {
1264 65 View Code Duplication
            if (strpos($mapping['collectionClass'], '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1265 63
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1266
            }
1267 65
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1268
        }
1269 951
        if ( ! empty($mapping['collectionClass'])) {
1270 65
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1271 65
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1272 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1273
            }
1274
        }
1275
1276 950
        if (isset($mapping['discriminatorMap'])) {
1277 123
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1278 123 View Code Duplication
                if (strpos($class, '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1279 123
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1280
                }
1281
            }
1282
        }
1283
1284 950
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1285 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1286
        }
1287
1288 949
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1289
1290 949
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1291 644
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1292
        }
1293
1294 949
        if (isset($mapping['embedded'])) {
1295 601
            unset($mapping['cascade']);
1296 944
        } elseif (isset($mapping['cascade'])) {
1297 408
            $mapping['cascade'] = $cascades;
1298
        }
1299
1300 949
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1301 949
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1302 949
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1303 949
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1304 949
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1305
1306 949
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1307 63
            $mapping['file'] = true;
1308
        }
1309 949
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1310 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1311
        }
1312 949 View Code Duplication
        if (isset($mapping['file']) && $mapping['file'] === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1313 63
            $this->file = $mapping['fieldName'];
1314 63
            $mapping['name'] = 'file';
1315
        }
1316 949 View Code Duplication
        if (isset($mapping['distance']) && $mapping['distance'] === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1317 7
            $this->distance = $mapping['fieldName'];
1318
        }
1319 949
        if (isset($mapping['id']) && $mapping['id'] === true) {
1320 921
            $mapping['name'] = '_id';
1321 921
            $this->identifier = $mapping['fieldName'];
1322 921 View Code Duplication
            if (isset($mapping['strategy'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1323 902
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1324
            }
1325 921
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1326 921
            switch ($this->generatorType) {
1327 921
                case self::GENERATOR_TYPE_AUTO:
1328 847
                    $mapping['type'] = 'id';
1329 847
                    break;
1330
                default:
1331 153
                    if ( ! empty($this->generatorOptions['type'])) {
1332 52
                        $mapping['type'] = $this->generatorOptions['type'];
1333 101
                    } elseif (empty($mapping['type'])) {
1334 86
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1335
                    }
1336
            }
1337 921
            unset($this->generatorOptions['type']);
1338
        }
1339
1340 949
        if ( ! isset($mapping['nullable'])) {
1341 53
            $mapping['nullable'] = false;
1342
        }
1343
1344
        // Synchronize the "simple" and "storeAs" mapping information for backwards compatibility
1345 949
        if (isset($mapping['simple']) && ($mapping['simple'] === true || $mapping['simple'] === 'true')) {
1346 293
            $mapping['storeAs'] = ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1347 293
            @trigger_error('"simple" attribute of a reference is deprecated - use storeAs="id" instead.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1348
        }
1349
        // Provide the correct value for the "simple" field for backwards compatibility
1350 949
        if (isset($mapping['storeAs'])) {
1351 583
            $mapping['simple'] = $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1352
        }
1353
1354 949
        if (isset($mapping['reference'])
1355 949
            && isset($mapping['storeAs'])
1356 949
            && $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID
1357 949
            && ! isset($mapping['targetDocument'])
1358
        ) {
1359 3
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1360
        }
1361
1362 946
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1363 946
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1364 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1365
        }
1366
1367 942
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1368 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1369
        }
1370
1371 941 View Code Duplication
        if (isset($mapping['reference']) && $mapping['type'] === 'one') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1372 516
            $mapping['association'] = self::REFERENCE_ONE;
1373
        }
1374 941 View Code Duplication
        if (isset($mapping['reference']) && $mapping['type'] === 'many') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1375 460
            $mapping['association'] = self::REFERENCE_MANY;
1376
        }
1377 941 View Code Duplication
        if (isset($mapping['embedded']) && $mapping['type'] === 'one') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1378 462
            $mapping['association'] = self::EMBED_ONE;
1379
        }
1380 941 View Code Duplication
        if (isset($mapping['embedded']) && $mapping['type'] === 'many') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1381 505
            $mapping['association'] = self::EMBED_MANY;
1382
        }
1383
1384 941
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1385 133
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1386
        }
1387
1388
        /*
1389
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1390
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1391
        }
1392
        */
1393 941
        if (isset($mapping['version'])) {
1394 100
            $mapping['notSaved'] = true;
1395 100
            $this->setVersionMapping($mapping);
1396
        }
1397 941
        if (isset($mapping['lock'])) {
1398 27
            $mapping['notSaved'] = true;
1399 27
            $this->setLockMapping($mapping);
1400
        }
1401 941
        $mapping['isOwningSide'] = true;
1402 941
        $mapping['isInverseSide'] = false;
1403 941
        if (isset($mapping['reference'])) {
1404 588 View Code Duplication
            if (isset($mapping['inversedBy']) && $mapping['inversedBy']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1405 92
                $mapping['isOwningSide'] = true;
1406 92
                $mapping['isInverseSide'] = false;
1407
            }
1408 588 View Code Duplication
            if (isset($mapping['mappedBy']) && $mapping['mappedBy']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1409 295
                $mapping['isInverseSide'] = true;
1410 295
                $mapping['isOwningSide'] = false;
1411
            }
1412 588 View Code Duplication
            if (isset($mapping['repositoryMethod'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1413 67
                $mapping['isInverseSide'] = true;
1414 67
                $mapping['isOwningSide'] = false;
1415
            }
1416 588
            if (!isset($mapping['orphanRemoval'])) {
1417 563
                $mapping['orphanRemoval'] = false;
1418
            }
1419
        }
1420
1421 941
        if (!empty($mapping['prime']) && ($mapping['association'] !== self::REFERENCE_MANY || !$mapping['isInverseSide'])) {
1422
            throw MappingException::referencePrimersOnlySupportedForInverseReferenceMany($this->name, $mapping['fieldName']);
1423
        }
1424
1425 941
        $this->applyStorageStrategy($mapping);
1426
1427 940
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1428 940
        if (isset($mapping['association'])) {
1429 741
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1430
        }
1431
1432 940
        return $mapping;
1433
    }
1434
1435
    /**
1436
     * Validates the storage strategy of a mapping for consistency
1437
     * @param array $mapping
1438
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1439
     */
1440 941
    private function applyStorageStrategy(array &$mapping)
1441
    {
1442 941
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1443 923
            return;
1444
        }
1445
1446
        switch (true) {
1447 903
            case $mapping['type'] == 'int':
1448 902
            case $mapping['type'] == 'float':
1449 902
            case $mapping['type'] == 'increment':
1450 334
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1451 334
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1452 334
                break;
1453
1454 901
            case $mapping['type'] == 'many':
1455 618
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1456
                $allowedStrategies = [
1457 618
                    self::STORAGE_STRATEGY_PUSH_ALL,
1458 618
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1459 618
                    self::STORAGE_STRATEGY_SET,
1460 618
                    self::STORAGE_STRATEGY_SET_ARRAY,
1461 618
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1462 618
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1463
                ];
1464 618
                break;
1465
1466
            default:
1467 889
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1468 889
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1469
        }
1470
1471 903
        if (! isset($mapping['strategy'])) {
1472 892
            $mapping['strategy'] = $defaultStrategy;
1473
        }
1474
1475 903
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1476
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1477
        }
1478
1479 903
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1480 903
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1481 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1482
        }
1483 902
    }
1484
1485
    /**
1486
     * Map a MongoGridFSFile.
1487
     *
1488
     * @param array $mapping The mapping information.
1489
     */
1490
    public function mapFile(array $mapping)
1491
    {
1492
        $mapping['file'] = true;
1493
        $mapping['type'] = 'file';
1494
        $this->mapField($mapping);
1495
    }
1496
1497
    /**
1498
     * Map a single embedded document.
1499
     *
1500
     * @param array $mapping The mapping information.
1501
     */
1502 6
    public function mapOneEmbedded(array $mapping)
1503
    {
1504 6
        $mapping['embedded'] = true;
1505 6
        $mapping['type'] = 'one';
1506 6
        $this->mapField($mapping);
1507 5
    }
1508
1509
    /**
1510
     * Map a collection of embedded documents.
1511
     *
1512
     * @param array $mapping The mapping information.
1513
     */
1514 5
    public function mapManyEmbedded(array $mapping)
1515
    {
1516 5
        $mapping['embedded'] = true;
1517 5
        $mapping['type'] = 'many';
1518 5
        $this->mapField($mapping);
1519 5
    }
1520
1521
    /**
1522
     * Map a single document reference.
1523
     *
1524
     * @param array $mapping The mapping information.
1525
     */
1526 8
    public function mapOneReference(array $mapping)
1527
    {
1528 8
        $mapping['reference'] = true;
1529 8
        $mapping['type'] = 'one';
1530 8
        $this->mapField($mapping);
1531 8
    }
1532
1533
    /**
1534
     * Map a collection of document references.
1535
     *
1536
     * @param array $mapping The mapping information.
1537
     */
1538 8
    public function mapManyReference(array $mapping)
1539
    {
1540 8
        $mapping['reference'] = true;
1541 8
        $mapping['type'] = 'many';
1542 8
        $this->mapField($mapping);
1543 8
    }
1544
1545
    /**
1546
     * INTERNAL:
1547
     * Adds a field mapping without completing/validating it.
1548
     * This is mainly used to add inherited field mappings to derived classes.
1549
     *
1550
     * @param array $fieldMapping
1551
     */
1552 129
    public function addInheritedFieldMapping(array $fieldMapping)
1553
    {
1554 129
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1555
1556 129
        if (isset($fieldMapping['association'])) {
1557 77
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1558
        }
1559 129
    }
1560
1561
    /**
1562
     * INTERNAL:
1563
     * Adds an association mapping without completing/validating it.
1564
     * This is mainly used to add inherited association mappings to derived classes.
1565
     *
1566
     * @param array $mapping
1567
     *
1568
     * @return void
1569
     *
1570
     * @throws MappingException
1571
     */
1572 78
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1573
    {
1574 78
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1575 78
    }
1576
1577
    /**
1578
     * Checks whether the class has a mapped association with the given field name.
1579
     *
1580
     * @param string $fieldName
1581
     * @return boolean
1582
     */
1583 14
    public function hasReference($fieldName)
1584
    {
1585 14
        return isset($this->fieldMappings[$fieldName]['reference']);
1586
    }
1587
1588
    /**
1589
     * Checks whether the class has a mapped embed with the given field name.
1590
     *
1591
     * @param string $fieldName
1592
     * @return boolean
1593
     */
1594 5
    public function hasEmbed($fieldName)
1595
    {
1596 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1597
    }
1598
1599
    /**
1600
     * {@inheritDoc}
1601
     *
1602
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1603
     */
1604 7
    public function hasAssociation($fieldName)
1605
    {
1606 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1607
    }
1608
1609
    /**
1610
     * {@inheritDoc}
1611
     *
1612
     * Checks whether the class has a mapped reference or embed for the specified field and
1613
     * is a single valued association.
1614
     */
1615
    public function isSingleValuedAssociation($fieldName)
1616
    {
1617
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1618
    }
1619
1620
    /**
1621
     * {@inheritDoc}
1622
     *
1623
     * Checks whether the class has a mapped reference or embed for the specified field and
1624
     * is a collection valued association.
1625
     */
1626
    public function isCollectionValuedAssociation($fieldName)
1627
    {
1628
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1629
    }
1630
1631
    /**
1632
     * Checks whether the class has a mapped association for the specified field
1633
     * and if yes, checks whether it is a single-valued association (to-one).
1634
     *
1635
     * @param string $fieldName
1636
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1637
     */
1638
    public function isSingleValuedReference($fieldName)
1639
    {
1640
        return isset($this->fieldMappings[$fieldName]['association']) &&
1641
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1642
    }
1643
1644
    /**
1645
     * Checks whether the class has a mapped association for the specified field
1646
     * and if yes, checks whether it is a collection-valued association (to-many).
1647
     *
1648
     * @param string $fieldName
1649
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1650
     */
1651
    public function isCollectionValuedReference($fieldName)
1652
    {
1653
        return isset($this->fieldMappings[$fieldName]['association']) &&
1654
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1655
    }
1656
1657
    /**
1658
     * Checks whether the class has a mapped embedded document for the specified field
1659
     * and if yes, checks whether it is a single-valued association (to-one).
1660
     *
1661
     * @param string $fieldName
1662
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1663
     */
1664
    public function isSingleValuedEmbed($fieldName)
1665
    {
1666
        return isset($this->fieldMappings[$fieldName]['association']) &&
1667
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1668
    }
1669
1670
    /**
1671
     * Checks whether the class has a mapped embedded document for the specified field
1672
     * and if yes, checks whether it is a collection-valued association (to-many).
1673
     *
1674
     * @param string $fieldName
1675
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1676
     */
1677
    public function isCollectionValuedEmbed($fieldName)
1678
    {
1679
        return isset($this->fieldMappings[$fieldName]['association']) &&
1680
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1681
    }
1682
1683
    /**
1684
     * Sets the ID generator used to generate IDs for instances of this class.
1685
     *
1686
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1687
     */
1688 842
    public function setIdGenerator($generator)
1689
    {
1690 842
        $this->idGenerator = $generator;
1691 842
    }
1692
1693
    /**
1694
     * Casts the identifier to its portable PHP type.
1695
     *
1696
     * @param mixed $id
1697
     * @return mixed $id
1698
     */
1699 647
    public function getPHPIdentifierValue($id)
1700
    {
1701 647
        $idType = $this->fieldMappings[$this->identifier]['type'];
1702 647
        return Type::getType($idType)->convertToPHPValue($id);
1703
    }
1704
1705
    /**
1706
     * Casts the identifier to its database type.
1707
     *
1708
     * @param mixed $id
1709
     * @return mixed $id
1710
     */
1711 714
    public function getDatabaseIdentifierValue($id)
1712
    {
1713 714
        $idType = $this->fieldMappings[$this->identifier]['type'];
1714 714
        return Type::getType($idType)->convertToDatabaseValue($id);
1715
    }
1716
1717
    /**
1718
     * Sets the document identifier of a document.
1719
     *
1720
     * The value will be converted to a PHP type before being set.
1721
     *
1722
     * @param object $document
1723
     * @param mixed $id
1724
     */
1725 576
    public function setIdentifierValue($document, $id)
1726
    {
1727 576
        $id = $this->getPHPIdentifierValue($id);
1728 576
        $this->reflFields[$this->identifier]->setValue($document, $id);
1729 576
    }
1730
1731
    /**
1732
     * Gets the document identifier as a PHP type.
1733
     *
1734
     * @param object $document
1735
     * @return mixed $id
1736
     */
1737 665
    public function getIdentifierValue($document)
1738
    {
1739 665
        return $this->reflFields[$this->identifier]->getValue($document);
1740
    }
1741
1742
    /**
1743
     * {@inheritDoc}
1744
     *
1745
     * Since MongoDB only allows exactly one identifier field this is a proxy
1746
     * to {@see getIdentifierValue()} and returns an array with the identifier
1747
     * field as a key.
1748
     */
1749
    public function getIdentifierValues($object)
1750
    {
1751
        return array($this->identifier => $this->getIdentifierValue($object));
1752
    }
1753
1754
    /**
1755
     * Get the document identifier object as a database type.
1756
     *
1757
     * @param object $document
1758
     *
1759
     * @return \MongoId $id The MongoID object.
1760
     */
1761 36
    public function getIdentifierObject($document)
1762
    {
1763 36
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1764
    }
1765
1766
    /**
1767
     * Sets the specified field to the specified value on the given document.
1768
     *
1769
     * @param object $document
1770
     * @param string $field
1771
     * @param mixed $value
1772
     */
1773 11
    public function setFieldValue($document, $field, $value)
1774
    {
1775 11
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1776
            //property changes to an uninitialized proxy will not be tracked or persisted,
1777
            //so the proxy needs to be loaded first.
1778 1
            $document->__load();
1779
        }
1780
1781 11
        $this->reflFields[$field]->setValue($document, $value);
1782 11
    }
1783
1784
    /**
1785
     * Gets the specified field's value off the given document.
1786
     *
1787
     * @param object $document
1788
     * @param string $field
1789
     *
1790
     * @return mixed
1791
     */
1792 31
    public function getFieldValue($document, $field)
1793
    {
1794 31
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1795 1
            $document->__load();
1796
        }
1797
1798 31
        return $this->reflFields[$field]->getValue($document);
1799
    }
1800
1801
    /**
1802
     * Gets the mapping of a field.
1803
     *
1804
     * @param string $fieldName  The field name.
1805
     *
1806
     * @return array  The field mapping.
1807
     *
1808
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1809
     */
1810 102
    public function getFieldMapping($fieldName)
1811
    {
1812 102
        if ( ! isset($this->fieldMappings[$fieldName])) {
1813 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1814
        }
1815 100
        return $this->fieldMappings[$fieldName];
1816
    }
1817
1818
    /**
1819
     * Gets mappings of fields holding embedded document(s).
1820
     *
1821
     * @return array of field mappings
1822
     */
1823 616
    public function getEmbeddedFieldsMappings()
1824
    {
1825 616
        return array_filter(
1826 616
            $this->associationMappings,
1827
            function($assoc) { return ! empty($assoc['embedded']); }
1828
        );
1829
    }
1830
1831
    /**
1832
     * Gets the field mapping by its DB name.
1833
     * E.g. it returns identifier's mapping when called with _id.
1834
     *
1835
     * @param string $dbFieldName
1836
     *
1837
     * @return array
1838
     * @throws MappingException
1839
     */
1840 3
    public function getFieldMappingByDbFieldName($dbFieldName)
1841
    {
1842 3
        foreach ($this->fieldMappings as $mapping) {
1843 3
            if ($mapping['name'] == $dbFieldName) {
1844 3
                return $mapping;
1845
            }
1846
        }
1847
1848
        throw MappingException::mappingNotFoundByDbName($this->name, $dbFieldName);
1849
    }
1850
1851
    /**
1852
     * Check if the field is not null.
1853
     *
1854
     * @param string $fieldName  The field name
1855
     *
1856
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1857
     */
1858 1
    public function isNullable($fieldName)
1859
    {
1860 1
        $mapping = $this->getFieldMapping($fieldName);
1861 1
        if ($mapping !== false) {
1862 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1863
        }
1864
        return false;
1865
    }
1866
1867
    /**
1868
     * Checks whether the document has a discriminator field and value configured.
1869
     *
1870
     * @return boolean
1871
     */
1872 531
    public function hasDiscriminator()
1873
    {
1874 531
        return isset($this->discriminatorField, $this->discriminatorValue);
1875
    }
1876
1877
    /**
1878
     * Sets the type of Id generator to use for the mapped class.
1879
     *
1880
     * @param string $generatorType Generator type.
1881
     */
1882 375
    public function setIdGeneratorType($generatorType)
1883
    {
1884 375
        $this->generatorType = $generatorType;
1885 375
    }
1886
1887
    /**
1888
     * Sets the Id generator options.
1889
     *
1890
     * @param array $generatorOptions Generator options.
1891
     */
1892
    public function setIdGeneratorOptions($generatorOptions)
1893
    {
1894
        $this->generatorOptions = $generatorOptions;
1895
    }
1896
1897
    /**
1898
     * @return boolean
1899
     */
1900 622
    public function isInheritanceTypeNone()
1901
    {
1902 622
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1903
    }
1904
1905
    /**
1906
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1907
     *
1908
     * @return boolean
1909
     */
1910 368
    public function isInheritanceTypeSingleCollection()
1911
    {
1912 368
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1913
    }
1914
1915
    /**
1916
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1917
     *
1918
     * @return boolean
1919
     */
1920
    public function isInheritanceTypeCollectionPerClass()
1921
    {
1922
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1923
    }
1924
1925
    /**
1926
     * Sets the mapped subclasses of this class.
1927
     *
1928
     * @param string[] $subclasses The names of all mapped subclasses.
1929
     */
1930 2
    public function setSubclasses(array $subclasses)
1931
    {
1932 2
        foreach ($subclasses as $subclass) {
1933 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1934 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1935
            } else {
1936 2
                $this->subClasses[] = $subclass;
1937
            }
1938
        }
1939 2
    }
1940
1941
    /**
1942
     * Sets the parent class names.
1943
     * Assumes that the class names in the passed array are in the order:
1944
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1945
     *
1946
     * @param string[] $classNames
1947
     */
1948 900
    public function setParentClasses(array $classNames)
1949
    {
1950 900
        $this->parentClasses = $classNames;
1951
1952 900
        if (count($classNames) > 0) {
1953 113
            $this->rootDocumentName = array_pop($classNames);
1954
        }
1955 900
    }
1956
1957
    /**
1958
     * Checks whether the class will generate a new \MongoId instance for us.
1959
     *
1960
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1961
     */
1962
    public function isIdGeneratorAuto()
1963
    {
1964
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1965
    }
1966
1967
    /**
1968
     * Checks whether the class will use a collection to generate incremented identifiers.
1969
     *
1970
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1971
     */
1972
    public function isIdGeneratorIncrement()
1973
    {
1974
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1975
    }
1976
1977
    /**
1978
     * Checks whether the class will generate a uuid id.
1979
     *
1980
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1981
     */
1982
    public function isIdGeneratorUuid()
1983
    {
1984
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1985
    }
1986
1987
    /**
1988
     * Checks whether the class uses no id generator.
1989
     *
1990
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1991
     */
1992
    public function isIdGeneratorNone()
1993
    {
1994
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1995
    }
1996
1997
    /**
1998
     * Sets the version field mapping used for versioning. Sets the default
1999
     * value to use depending on the column type.
2000
     *
2001
     * @param array $mapping   The version field mapping array
2002
     *
2003
     * @throws LockException
2004
     */
2005 100
    public function setVersionMapping(array &$mapping)
2006
    {
2007 100
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
2008 1
            throw LockException::invalidVersionFieldType($mapping['type']);
2009
        }
2010
2011 99
        $this->isVersioned  = true;
2012 99
        $this->versionField = $mapping['fieldName'];
2013 99
    }
2014
2015
    /**
2016
     * Sets whether this class is to be versioned for optimistic locking.
2017
     *
2018
     * @param boolean $bool
2019
     */
2020 369
    public function setVersioned($bool)
2021
    {
2022 369
        $this->isVersioned = $bool;
2023 369
    }
2024
2025
    /**
2026
     * Sets the name of the field that is to be used for versioning if this class is
2027
     * versioned for optimistic locking.
2028
     *
2029
     * @param string $versionField
2030
     */
2031 369
    public function setVersionField($versionField)
2032
    {
2033 369
        $this->versionField = $versionField;
2034 369
    }
2035
2036
    /**
2037
     * Sets the version field mapping used for versioning. Sets the default
2038
     * value to use depending on the column type.
2039
     *
2040
     * @param array $mapping   The version field mapping array
2041
     *
2042
     * @throws \Doctrine\ODM\MongoDB\LockException
2043
     */
2044 27
    public function setLockMapping(array &$mapping)
2045
    {
2046 27
        if ($mapping['type'] !== 'int') {
2047 1
            throw LockException::invalidLockFieldType($mapping['type']);
2048
        }
2049
2050 26
        $this->isLockable = true;
2051 26
        $this->lockField = $mapping['fieldName'];
2052 26
    }
2053
2054
    /**
2055
     * Sets whether this class is to allow pessimistic locking.
2056
     *
2057
     * @param boolean $bool
2058
     */
2059
    public function setLockable($bool)
2060
    {
2061
        $this->isLockable = $bool;
2062
    }
2063
2064
    /**
2065
     * Sets the name of the field that is to be used for storing whether a document
2066
     * is currently locked or not.
2067
     *
2068
     * @param string $lockField
2069
     */
2070
    public function setLockField($lockField)
2071
    {
2072
        $this->lockField = $lockField;
2073
    }
2074
2075
    /**
2076
     * {@inheritDoc}
2077
     */
2078
    public function getFieldNames()
2079
    {
2080
        return array_keys($this->fieldMappings);
2081
    }
2082
2083
    /**
2084
     * {@inheritDoc}
2085
     */
2086
    public function getAssociationNames()
2087
    {
2088
        return array_keys($this->associationMappings);
2089
    }
2090
2091
    /**
2092
     * {@inheritDoc}
2093
     */
2094 22
    public function getTypeOfField($fieldName)
2095
    {
2096 22
        return isset($this->fieldMappings[$fieldName]) ?
2097 22
            $this->fieldMappings[$fieldName]['type'] : null;
2098
    }
2099
2100
    /**
2101
     * {@inheritDoc}
2102
     */
2103 6
    public function getAssociationTargetClass($assocName)
2104
    {
2105 6
        if ( ! isset($this->associationMappings[$assocName])) {
2106 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2107
        }
2108
2109 3
        return $this->associationMappings[$assocName]['targetDocument'];
2110
    }
2111
2112
    /**
2113
     * Retrieve the collectionClass associated with an association
2114
     *
2115
     * @param string $assocName
2116
     */
2117 2
    public function getAssociationCollectionClass($assocName)
2118
    {
2119 2
        if ( ! isset($this->associationMappings[$assocName])) {
2120
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2121
        }
2122
2123 2
        if ( ! array_key_exists('collectionClass', $this->associationMappings[$assocName])) {
2124
            throw new InvalidArgumentException("collectionClass can only be applied to 'embedMany' and 'referenceMany' associations.");
2125
        }
2126
2127 2
        return $this->associationMappings[$assocName]['collectionClass'];
2128
    }
2129
2130
    /**
2131
     * {@inheritDoc}
2132
     */
2133
    public function isAssociationInverseSide($fieldName)
2134
    {
2135
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2136
    }
2137
2138
    /**
2139
     * {@inheritDoc}
2140
     */
2141
    public function getAssociationMappedByTargetField($fieldName)
2142
    {
2143
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2144
    }
2145
}
2146