Completed
Pull Request — master (#1566)
by Andreas
08:25 queued 03:38
created

ClassMetadataInfo::getIdentifierValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\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
    const REFERENCE_STORE_AS_REF = 'ref';
107
    const REFERENCE_STORE_AS_REF_WITH_DB = 'refWithDb';
108
109
    /* The inheritance mapping types */
110
    /**
111
     * NONE means the class does not participate in an inheritance hierarchy
112
     * and therefore does not need an inheritance mapping type.
113
     */
114
    const INHERITANCE_TYPE_NONE = 1;
115
116
    /**
117
     * SINGLE_COLLECTION means the class will be persisted according to the rules of
118
     * <tt>Single Collection Inheritance</tt>.
119
     */
120
    const INHERITANCE_TYPE_SINGLE_COLLECTION = 2;
121
122
    /**
123
     * COLLECTION_PER_CLASS means the class will be persisted according to the rules
124
     * of <tt>Concrete Collection Inheritance</tt>.
125
     */
126
    const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3;
127
128
    /**
129
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
130
     * by doing a property-by-property comparison with the original data. This will
131
     * be done for all entities that are in MANAGED state at commit-time.
132
     *
133
     * This is the default change tracking policy.
134
     */
135
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
136
137
    /**
138
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
139
     * by doing a property-by-property comparison with the original data. This will
140
     * be done only for entities that were explicitly saved (through persist() or a cascade).
141
     */
142
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
143
144
    /**
145
     * NOTIFY means that Doctrine relies on the entities sending out notifications
146
     * when their properties change. Such entity classes must implement
147
     * the <tt>NotifyPropertyChanged</tt> interface.
148
     */
149
    const CHANGETRACKING_NOTIFY = 3;
150
151
    /**
152
     * SET means that fields will be written to the database using a $set operator
153
     */
154
    const STORAGE_STRATEGY_SET = 'set';
155
156
    /**
157
     * INCREMENT means that fields will be written to the database by calculating
158
     * the difference and using the $inc operator
159
     */
160
    const STORAGE_STRATEGY_INCREMENT = 'increment';
161
162
    const STORAGE_STRATEGY_PUSH_ALL = 'pushAll';
163
    const STORAGE_STRATEGY_ADD_TO_SET = 'addToSet';
164
    const STORAGE_STRATEGY_ATOMIC_SET = 'atomicSet';
165
    const STORAGE_STRATEGY_ATOMIC_SET_ARRAY = 'atomicSetArray';
166
    const STORAGE_STRATEGY_SET_ARRAY = 'setArray';
167
168
    /**
169
     * READ-ONLY: The name of the mongo database the document is mapped to.
170
     */
171
    public $db;
172
173
    /**
174
     * READ-ONLY: The name of the mongo collection the document is mapped to.
175
     */
176
    public $collection;
177
178
    /**
179
     * READ-ONLY: If the collection should be a fixed size.
180
     */
181
    public $collectionCapped;
182
183
    /**
184
     * READ-ONLY: If the collection is fixed size, its size in bytes.
185
     */
186
    public $collectionSize;
187
188
    /**
189
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
190
     */
191
    public $collectionMax;
192
193
    /**
194
     * READ-ONLY: Describes the level of acknowledgement requested from MongoDB for write operations.
195
     */
196
    public $writeConcern;
197
198
    /**
199
     * READ-ONLY: The field name of the document identifier.
200
     */
201
    public $identifier;
202
203
    /**
204
     * READ-ONLY: The field that stores a file reference and indicates the
205
     * document is a file and should be stored on the MongoGridFS.
206
     */
207
    public $file;
208
209
    /**
210
     * READ-ONLY: The field that stores the calculated distance when performing geo spatial
211
     * queries.
212
     */
213
    public $distance;
214
215
    /**
216
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
217
     *
218
     * @deprecated in version 1.2 and will be removed in 2.0.
219
     */
220
    public $slaveOkay;
221
222
    /**
223
     * READ-ONLY: The array of indexes for the document collection.
224
     */
225
    public $indexes = array();
226
227
    /**
228
     * READ-ONLY: Keys and options describing shard key. Only for sharded collections.
229
     */
230
    public $shardKey;
231
232
    /**
233
     * READ-ONLY: Whether or not queries on this document should require indexes.
234
     *
235
     * @deprecated property was deprecated in 1.2 and will be removed in 2.0
236
     */
237
    public $requireIndexes = false;
238
239
    /**
240
     * READ-ONLY: The name of the document class.
241
     */
242
    public $name;
243
244
    /**
245
     * READ-ONLY: The namespace the document class is contained in.
246
     *
247
     * @var string
248
     * @todo Not really needed. Usage could be localized.
249
     */
250
    public $namespace;
251
252
    /**
253
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
254
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
255
     * as {@link $documentName}.
256
     *
257
     * @var string
258
     */
259
    public $rootDocumentName;
260
261
    /**
262
     * The name of the custom repository class used for the document class.
263
     * (Optional).
264
     *
265
     * @var string
266
     */
267
    public $customRepositoryClassName;
268
269
    /**
270
     * READ-ONLY: The names of the parent classes (ancestors).
271
     *
272
     * @var array
273
     */
274
    public $parentClasses = array();
275
276
    /**
277
     * READ-ONLY: The names of all subclasses (descendants).
278
     *
279
     * @var array
280
     */
281
    public $subClasses = array();
282
283
    /**
284
     * The ReflectionProperty instances of the mapped class.
285
     *
286
     * @var \ReflectionProperty[]
287
     */
288
    public $reflFields = array();
289
290
    /**
291
     * READ-ONLY: The inheritance mapping type used by the class.
292
     *
293
     * @var integer
294
     */
295
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
296
297
    /**
298
     * READ-ONLY: The Id generator type used by the class.
299
     *
300
     * @var string
301
     */
302
    public $generatorType = self::GENERATOR_TYPE_AUTO;
303
304
    /**
305
     * READ-ONLY: The Id generator options.
306
     *
307
     * @var array
308
     */
309
    public $generatorOptions = array();
310
311
    /**
312
     * READ-ONLY: The ID generator used for generating IDs for this class.
313
     *
314
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
315
     */
316
    public $idGenerator;
317
318
    /**
319
     * READ-ONLY: The field mappings of the class.
320
     * Keys are field names and values are mapping definitions.
321
     *
322
     * The mapping definition array has the following values:
323
     *
324
     * - <b>fieldName</b> (string)
325
     * The name of the field in the Document.
326
     *
327
     * - <b>id</b> (boolean, optional)
328
     * Marks the field as the primary key of the document. Multiple fields of an
329
     * document can have the id attribute, forming a composite key.
330
     *
331
     * @var array
332
     */
333
    public $fieldMappings = array();
334
335
    /**
336
     * READ-ONLY: The association mappings of the class.
337
     * Keys are field names and values are mapping definitions.
338
     *
339
     * @var array
340
     */
341
    public $associationMappings = array();
342
343
    /**
344
     * READ-ONLY: Array of fields to also load with a given method.
345
     *
346
     * @var array
347
     */
348
    public $alsoLoadMethods = array();
349
350
    /**
351
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
352
     *
353
     * @var array
354
     */
355
    public $lifecycleCallbacks = array();
356
357
    /**
358
     * READ-ONLY: The discriminator value of this class.
359
     *
360
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
361
     * where a discriminator field is used.</b>
362
     *
363
     * @var mixed
364
     * @see discriminatorField
365
     */
366
    public $discriminatorValue;
367
368
    /**
369
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
370
     *
371
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
372
     * where a discriminator field is used.</b>
373
     *
374
     * @var mixed
375
     * @see discriminatorField
376
     */
377
    public $discriminatorMap = array();
378
379
    /**
380
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
381
     * inheritance mapping.
382
     *
383
     * @var string
384
     */
385
    public $discriminatorField;
386
387
    /**
388
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
389
     *
390
     * @var string
391
     * @see discriminatorField
392
     */
393
    public $defaultDiscriminatorValue;
394
395
    /**
396
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
397
     *
398
     * @var boolean
399
     */
400
    public $isMappedSuperclass = false;
401
402
    /**
403
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
404
     *
405
     * @var boolean
406
     */
407
    public $isEmbeddedDocument = false;
408
409
    /**
410
     * READ-ONLY: Whether this class describes the mapping of an aggregation result document.
411
     *
412
     * @var boolean
413
     */
414
    public $isQueryResultDocument = false;
415
416
    /**
417
     * READ-ONLY: The policy used for change-tracking on entities of this class.
418
     *
419
     * @var integer
420
     */
421
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
422
423
    /**
424
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
425
     * with optimistic locking.
426
     *
427
     * @var boolean $isVersioned
428
     */
429
    public $isVersioned;
430
431
    /**
432
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
433
     *
434
     * @var mixed $versionField
435
     */
436
    public $versionField;
437
438
    /**
439
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
440
     * locking.
441
     *
442
     * @var boolean $isLockable
443
     */
444
    public $isLockable;
445
446
    /**
447
     * READ-ONLY: The name of the field which is used for locking a document.
448
     *
449
     * @var mixed $lockField
450
     */
451
    public $lockField;
452
453
    /**
454
     * The ReflectionClass instance of the mapped class.
455
     *
456
     * @var \ReflectionClass
457
     */
458
    public $reflClass;
459
460
    /**
461
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
462
     * metadata of the class with the given name.
463
     *
464
     * @param string $documentName The name of the document class the new instance is used for.
465
     */
466 975
    public function __construct($documentName)
467
    {
468 975
        $this->name = $documentName;
469 975
        $this->rootDocumentName = $documentName;
470 975
    }
471
472
    /**
473
     * Helper method to get reference id of ref* type references
474
     * @param array  $reference
475
     * @param string $storeAs
476
     * @return mixed
477
     * @internal
478
     */
479 46
    public static function getReferenceId($reference, $storeAs)
480
    {
481 46
        return $reference[ClassMetadataInfo::getReferencePrefix($storeAs) . 'id'];
482
    }
483
484
    /**
485
     * Returns the reference prefix used for a referene
486
     * @param string $storeAs
487
     * @return string
488
     * @internal
489
     */
490 220
    public static function getReferencePrefix($storeAs)
491
    {
492 220
        if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_REF_WITH_DB || $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_REF) {
493
            return '';
494
        } else {
495 220
            return '$';
496
        }
497
    }
498
499
    /**
500
     * {@inheritDoc}
501
     */
502 903
    public function getReflectionClass()
503
    {
504 903
        if ( ! $this->reflClass) {
505 2
            $this->reflClass = new \ReflectionClass($this->name);
506
        }
507
508 903
        return $this->reflClass;
509
    }
510
511
    /**
512
     * {@inheritDoc}
513
     */
514 330
    public function isIdentifier($fieldName)
515
    {
516 330
        return $this->identifier === $fieldName;
517
    }
518
519
    /**
520
     * INTERNAL:
521
     * Sets the mapped identifier field of this class.
522
     *
523
     * @param string $identifier
524
     */
525 370
    public function setIdentifier($identifier)
526
    {
527 370
        $this->identifier = $identifier;
528 370
    }
529
530
    /**
531
     * {@inheritDoc}
532
     *
533
     * Since MongoDB only allows exactly one identifier field
534
     * this will always return an array with only one value
535
     */
536 40
    public function getIdentifier()
537
    {
538 40
        return array($this->identifier);
539
    }
540
541
    /**
542
     * {@inheritDoc}
543
     *
544
     * Since MongoDB only allows exactly one identifier field
545
     * this will always return an array with only one value
546
     */
547 98
    public function getIdentifierFieldNames()
548
    {
549 98
        return array($this->identifier);
550
    }
551
552
    /**
553
     * {@inheritDoc}
554
     */
555 561
    public function hasField($fieldName)
556
    {
557 561
        return isset($this->fieldMappings[$fieldName]);
558
    }
559
560
    /**
561
     * Sets the inheritance type used by the class and it's subclasses.
562
     *
563
     * @param integer $type
564
     */
565 386
    public function setInheritanceType($type)
566
    {
567 386
        $this->inheritanceType = $type;
568 386
    }
569
570
    /**
571
     * Checks whether a mapped field is inherited from an entity superclass.
572
     *
573
     * @param  string $fieldName
574
     *
575
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
576
     */
577 903
    public function isInheritedField($fieldName)
578
    {
579 903
        return isset($this->fieldMappings[$fieldName]['inherited']);
580
    }
581
582
    /**
583
     * Registers a custom repository class for the document class.
584
     *
585
     * @param string $repositoryClassName The class name of the custom repository.
586
     */
587 318
    public function setCustomRepositoryClass($repositoryClassName)
588
    {
589 318
        if ($this->isEmbeddedDocument || $this->isQueryResultDocument) {
590
            return;
591
        }
592
593 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...
594 4
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
595
        }
596
597 318
        $this->customRepositoryClassName = $repositoryClassName;
598 318
    }
599
600
    /**
601
     * Dispatches the lifecycle event of the given document by invoking all
602
     * registered callbacks.
603
     *
604
     * @param string $event     Lifecycle event
605
     * @param object $document  Document on which the event occurred
606
     * @param array  $arguments Arguments to pass to all callbacks
607
     * @throws \InvalidArgumentException if document class is not this class or
608
     *                                   a Proxy of this class
609
     */
610 660
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
611
    {
612 660
        if ( ! $document instanceof $this->name) {
613 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
614
        }
615
616 659
        if (empty($this->lifecycleCallbacks[$event])) {
617 645
            return;
618
        }
619
620 196
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
621 196
            if ($arguments !== null) {
622 195
                call_user_func_array(array($document, $callback), $arguments);
623
            } else {
624 196
                $document->$callback();
625
            }
626
        }
627 196
    }
628
629
    /**
630
     * Checks whether the class has callbacks registered for a lifecycle event.
631
     *
632
     * @param string $event Lifecycle event
633
     *
634
     * @return boolean
635
     */
636
    public function hasLifecycleCallbacks($event)
637
    {
638
        return ! empty($this->lifecycleCallbacks[$event]);
639
    }
640
641
    /**
642
     * Gets the registered lifecycle callbacks for an event.
643
     *
644
     * @param string $event
645
     * @return array
646
     */
647
    public function getLifecycleCallbacks($event)
648
    {
649
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
650
    }
651
652
    /**
653
     * Adds a lifecycle callback for documents of this class.
654
     *
655
     * If the callback is already registered, this is a NOOP.
656
     *
657
     * @param string $callback
658
     * @param string $event
659
     */
660 297
    public function addLifecycleCallback($callback, $event)
661
    {
662 297
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
663 1
            return;
664
        }
665
666 297
        $this->lifecycleCallbacks[$event][] = $callback;
667 297
    }
668
669
    /**
670
     * Sets the lifecycle callbacks for documents of this class.
671
     *
672
     * Any previously registered callbacks are overwritten.
673
     *
674
     * @param array $callbacks
675
     */
676 369
    public function setLifecycleCallbacks(array $callbacks)
677
    {
678 369
        $this->lifecycleCallbacks = $callbacks;
679 369
    }
680
681
    /**
682
     * Registers a method for loading document data before field hydration.
683
     *
684
     * Note: A method may be registered multiple times for different fields.
685
     * it will be invoked only once for the first field found.
686
     *
687
     * @param string       $method Method name
688
     * @param array|string $fields Database field name(s)
689
     */
690 15
    public function registerAlsoLoadMethod($method, $fields)
691
    {
692 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
693 15
    }
694
695
    /**
696
     * Sets the AlsoLoad methods for documents of this class.
697
     *
698
     * Any previously registered methods are overwritten.
699
     *
700
     * @param array $methods
701
     */
702 369
    public function setAlsoLoadMethods(array $methods)
703
    {
704 369
        $this->alsoLoadMethods = $methods;
705 369
    }
706
707
    /**
708
     * Sets the discriminator field.
709
     *
710
     * The field name is the the unmapped database field. Discriminator values
711
     * are only used to discern the hydration class and are not mapped to class
712
     * properties.
713
     *
714
     * @param string $discriminatorField
715
     *
716
     * @throws MappingException If the discriminator field conflicts with the
717
     *                          "name" attribute of a mapped field.
718
     */
719 399
    public function setDiscriminatorField($discriminatorField)
720
    {
721 399
        if ($discriminatorField === null) {
722 326
            $this->discriminatorField = null;
723
724 326
            return;
725
        }
726
727
        // Handle array argument with name/fieldName keys for BC
728 130
        if (is_array($discriminatorField)) {
729
            if (isset($discriminatorField['name'])) {
730
                $discriminatorField = $discriminatorField['name'];
731
            } elseif (isset($discriminatorField['fieldName'])) {
732
                $discriminatorField = $discriminatorField['fieldName'];
733
            }
734
        }
735
736 130
        foreach ($this->fieldMappings as $fieldMapping) {
737 4
            if ($discriminatorField == $fieldMapping['name']) {
738 4
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
739
            }
740
        }
741
742 129
        $this->discriminatorField = $discriminatorField;
743 129
    }
744
745
    /**
746
     * Sets the discriminator values used by this class.
747
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
748
     *
749
     * @param array $map
750
     *
751
     * @throws MappingException
752
     */
753 392
    public function setDiscriminatorMap(array $map)
754
    {
755 392
        foreach ($map as $value => $className) {
756 125
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
757 91
                $className = $this->namespace . '\\' . $className;
758
            }
759 125
            $this->discriminatorMap[$value] = $className;
760 125
            if ($this->name == $className) {
761 117
                $this->discriminatorValue = $value;
762
            } else {
763 120
                if ( ! class_exists($className)) {
764
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
765
                }
766 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...
767 125
                    $this->subClasses[] = $className;
768
                }
769
            }
770
        }
771 392
    }
772
773
    /**
774
     * Sets the default discriminator value to be used for this class
775
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
776
     *
777
     * @param string $defaultDiscriminatorValue
778
     *
779
     * @throws MappingException
780
     */
781 376
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
782
    {
783 376
        if ($defaultDiscriminatorValue === null) {
784 369
            $this->defaultDiscriminatorValue = null;
785
786 369
            return;
787
        }
788
789 60
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
790
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
791
        }
792
793 60
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
794 60
    }
795
796
    /**
797
     * Sets the discriminator value for this class.
798
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
799
     * collection.
800
     *
801
     * @param string $value
802
     */
803 3
    public function setDiscriminatorValue($value)
804
    {
805 3
        $this->discriminatorMap[$value] = $this->name;
806 3
        $this->discriminatorValue = $value;
807 3
    }
808
809
    /**
810
     * Sets the slaveOkay option applied to collections for this class.
811
     *
812
     * @param boolean|null $slaveOkay
813
     *
814
     * @deprecated in version 1.2 and will be removed in 2.0.
815
     */
816 3
    public function setSlaveOkay($slaveOkay)
817
    {
818 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...
819 3
            sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.'),
820 3
            E_USER_DEPRECATED
821
        );
822 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...
823 3
    }
824
825
    /**
826
     * Add a index for this Document.
827
     *
828
     * @param array $keys Array of keys for the index.
829
     * @param array $options Array of options for the index.
830
     */
831 232
    public function addIndex($keys, array $options = array())
832
    {
833 232
        $this->indexes[] = array(
834 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...
835 232
                if ($value == 1 || $value == -1) {
836 63
                    return (int) $value;
837
                }
838 224
                if (is_string($value)) {
839 224
                    $lower = strtolower($value);
840 224
                    if ($lower === 'asc') {
841 217
                        return 1;
842 11
                    } elseif ($lower === 'desc') {
843 4
                        return -1;
844
                    }
845
                }
846 7
                return $value;
847 232
            }, $keys),
848 232
            'options' => $options
849
        );
850 232
    }
851
852
    /**
853
     * Set whether or not queries on this document should require indexes.
854
     *
855
     * @param bool $requireIndexes
856
     *
857
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
858
     */
859 894
    public function setRequireIndexes($requireIndexes)
860
    {
861 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...
862 894
            'requireIndexes was deprecated in version 1.2 and will be removed altogether in 2.0.',
863 894
            E_USER_DEPRECATED
864
        );
865 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...
866 894
    }
867
868
    /**
869
     * Returns the array of indexes for this Document.
870
     *
871
     * @return array $indexes The array of indexes.
872
     */
873 54
    public function getIndexes()
874
    {
875 54
        return $this->indexes;
876
    }
877
878
    /**
879
     * Checks whether this document has indexes or not.
880
     *
881
     * @return boolean
882
     */
883
    public function hasIndexes()
884
    {
885
        return $this->indexes ? true : false;
886
    }
887
888
    /**
889
     * Set shard key for this Document.
890
     *
891
     * @param array $keys Array of document keys.
892
     * @param array $options Array of sharding options.
893
     *
894
     * @throws MappingException
895
     */
896 77
    public function setShardKey(array $keys, array $options = array())
897
    {
898 77
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
899 2
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
900
        }
901
902 77
        if ($this->isEmbeddedDocument) {
903 2
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
904
        }
905
906 75
        foreach (array_keys($keys) as $field) {
907 75
            if (! isset($this->fieldMappings[$field])) {
908 68
                continue;
909
            }
910
911 7
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
912 3
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
913
            }
914
915 4
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
916 4
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
917
            }
918
        }
919
920 71
        $this->shardKey = array(
921 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...
922 71
                if ($value == 1 || $value == -1) {
923 6
                    return (int) $value;
924
                }
925 70
                if (is_string($value)) {
926 70
                    $lower = strtolower($value);
927 70
                    if ($lower === 'asc') {
928 69
                        return 1;
929 53
                    } elseif ($lower === 'desc') {
930
                        return -1;
931
                    }
932
                }
933 53
                return $value;
934 71
            }, $keys),
935 71
            'options' => $options
936
        );
937 71
    }
938
939
    /**
940
     * @return array
941
     */
942 18
    public function getShardKey()
943
    {
944 18
        return $this->shardKey;
945
    }
946
947
    /**
948
     * Checks whether this document has shard key or not.
949
     *
950
     * @return bool
951
     */
952 598
    public function isSharded()
953
    {
954 598
        return $this->shardKey ? true : false;
955
    }
956
957
    /**
958
     * Sets the write concern used by this class.
959
     *
960
     * @param string $writeConcern
961
     */
962 383
    public function setWriteConcern($writeConcern)
963
    {
964 383
        $this->writeConcern = $writeConcern;
965 383
    }
966
967
    /**
968
     * @return string
969
     */
970 12
    public function getWriteConcern()
971
    {
972 12
        return $this->writeConcern;
973
    }
974
975
    /**
976
     * Whether there is a write concern configured for this class.
977
     *
978
     * @return bool
979
     */
980 604
    public function hasWriteConcern()
981
    {
982 604
        return $this->writeConcern !== null;
983
    }
984
985
    /**
986
     * Sets the change tracking policy used by this class.
987
     *
988
     * @param integer $policy
989
     */
990 374
    public function setChangeTrackingPolicy($policy)
991
    {
992 374
        $this->changeTrackingPolicy = $policy;
993 374
    }
994
995
    /**
996
     * Whether the change tracking policy of this class is "deferred explicit".
997
     *
998
     * @return boolean
999
     */
1000 73
    public function isChangeTrackingDeferredExplicit()
1001
    {
1002 73
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
1003
    }
1004
1005
    /**
1006
     * Whether the change tracking policy of this class is "deferred implicit".
1007
     *
1008
     * @return boolean
1009
     */
1010 624
    public function isChangeTrackingDeferredImplicit()
1011
    {
1012 624
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1013
    }
1014
1015
    /**
1016
     * Whether the change tracking policy of this class is "notify".
1017
     *
1018
     * @return boolean
1019
     */
1020 347
    public function isChangeTrackingNotify()
1021
    {
1022 347
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1023
    }
1024
1025
    /**
1026
     * Gets the ReflectionProperties of the mapped class.
1027
     *
1028
     * @return array An array of ReflectionProperty instances.
1029
     */
1030 98
    public function getReflectionProperties()
1031
    {
1032 98
        return $this->reflFields;
1033
    }
1034
1035
    /**
1036
     * Gets a ReflectionProperty for a specific field of the mapped class.
1037
     *
1038
     * @param string $name
1039
     *
1040
     * @return \ReflectionProperty
1041
     */
1042
    public function getReflectionProperty($name)
1043
    {
1044
        return $this->reflFields[$name];
1045
    }
1046
1047
    /**
1048
     * {@inheritDoc}
1049
     */
1050 909
    public function getName()
1051
    {
1052 909
        return $this->name;
1053
    }
1054
1055
    /**
1056
     * The namespace this Document class belongs to.
1057
     *
1058
     * @return string $namespace The namespace name.
1059
     */
1060
    public function getNamespace()
1061
    {
1062
        return $this->namespace;
1063
    }
1064
1065
    /**
1066
     * Returns the database this Document is mapped to.
1067
     *
1068
     * @return string $db The database name.
1069
     */
1070 829
    public function getDatabase()
1071
    {
1072 829
        return $this->db;
1073
    }
1074
1075
    /**
1076
     * Set the database this Document is mapped to.
1077
     *
1078
     * @param string $db The database name
1079
     */
1080 104
    public function setDatabase($db)
1081
    {
1082 104
        $this->db = $db;
1083 104
    }
1084
1085
    /**
1086
     * Get the collection this Document is mapped to.
1087
     *
1088
     * @return string $collection The collection name.
1089
     */
1090 834
    public function getCollection()
1091
    {
1092 834
        return $this->collection;
1093
    }
1094
1095
    /**
1096
     * Sets the collection this Document is mapped to.
1097
     *
1098
     * @param array|string $name
1099
     *
1100
     * @throws \InvalidArgumentException
1101
     */
1102 939
    public function setCollection($name)
1103
    {
1104 939
        if (is_array($name)) {
1105
            if ( ! isset($name['name'])) {
1106
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
1107
            }
1108
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
1109
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
1110
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
1111
            $this->collection = $name['name'];
1112
        } else {
1113 939
            $this->collection = $name;
1114
        }
1115 939
    }
1116
1117
    /**
1118
     * Get whether or not the documents collection is capped.
1119
     *
1120
     * @return boolean
1121
     */
1122 4
    public function getCollectionCapped()
1123
    {
1124 4
        return $this->collectionCapped;
1125
    }
1126
1127
    /**
1128
     * Set whether or not the documents collection is capped.
1129
     *
1130
     * @param boolean $bool
1131
     */
1132 1
    public function setCollectionCapped($bool)
1133
    {
1134 1
        $this->collectionCapped = $bool;
1135 1
    }
1136
1137
    /**
1138
     * Get the collection size
1139
     *
1140
     * @return integer
1141
     */
1142 4
    public function getCollectionSize()
1143
    {
1144 4
        return $this->collectionSize;
1145
    }
1146
1147
    /**
1148
     * Set the collection size.
1149
     *
1150
     * @param integer $size
1151
     */
1152 1
    public function setCollectionSize($size)
1153
    {
1154 1
        $this->collectionSize = $size;
1155 1
    }
1156
1157
    /**
1158
     * Get the collection max.
1159
     *
1160
     * @return integer
1161
     */
1162 4
    public function getCollectionMax()
1163
    {
1164 4
        return $this->collectionMax;
1165
    }
1166
1167
    /**
1168
     * Set the collection max.
1169
     *
1170
     * @param integer $max
1171
     */
1172 1
    public function setCollectionMax($max)
1173
    {
1174 1
        $this->collectionMax = $max;
1175 1
    }
1176
1177
    /**
1178
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1179
     *
1180
     * @return boolean
1181
     */
1182
    public function isMappedToCollection()
1183
    {
1184
        return $this->collection ? true : false;
1185
    }
1186
1187
    /**
1188
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1189
     *
1190
     * @return boolean
1191
     */
1192 775
    public function isFile()
1193
    {
1194 775
        return $this->file ? true : false;
1195
    }
1196
1197
    /**
1198
     * Returns the file field name.
1199
     *
1200
     * @return string $file The file field name.
1201
     */
1202 369
    public function getFile()
1203
    {
1204 369
        return $this->file;
1205
    }
1206
1207
    /**
1208
     * Set the field name that stores the grid file.
1209
     *
1210
     * @param string $file
1211
     */
1212 370
    public function setFile($file)
1213
    {
1214 370
        $this->file = $file;
1215 370
    }
1216
1217
    /**
1218
     * Returns the distance field name.
1219
     *
1220
     * @return string $distance The distance field name.
1221
     */
1222
    public function getDistance()
1223
    {
1224
        return $this->distance;
1225
    }
1226
1227
    /**
1228
     * Set the field name that stores the distance.
1229
     *
1230
     * @param string $distance
1231
     */
1232 1
    public function setDistance($distance)
1233
    {
1234 1
        $this->distance = $distance;
1235 1
    }
1236
1237
    /**
1238
     * Map a field.
1239
     *
1240
     * @param array $mapping The mapping information.
1241
     *
1242
     * @return array
1243
     *
1244
     * @throws MappingException
1245
     */
1246 953
    public function mapField(array $mapping)
1247
    {
1248 953
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1249 10
            $mapping['fieldName'] = $mapping['name'];
1250
        }
1251 953
        if ( ! isset($mapping['fieldName'])) {
1252
            throw MappingException::missingFieldName($this->name);
1253
        }
1254 953
        if ( ! isset($mapping['name'])) {
1255 943
            $mapping['name'] = $mapping['fieldName'];
1256
        }
1257 953
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1258 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1259
        }
1260 952
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1261
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1262
        }
1263 952
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1264 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1265
        }
1266 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...
1267 616
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1268
        }
1269 951
        if (isset($mapping['collectionClass'])) {
1270 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...
1271 63
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1272
            }
1273 65
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1274
        }
1275 951
        if ( ! empty($mapping['collectionClass'])) {
1276 65
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1277 65
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1278 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1279
            }
1280
        }
1281
1282 950
        if (isset($mapping['discriminatorMap'])) {
1283 123
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1284 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...
1285 123
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1286
                }
1287
            }
1288
        }
1289
1290 950
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1291 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1292
        }
1293
1294 949
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1295
1296 949
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1297 644
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1298
        }
1299
1300 949
        if (isset($mapping['embedded'])) {
1301 601
            unset($mapping['cascade']);
1302 944
        } elseif (isset($mapping['cascade'])) {
1303 408
            $mapping['cascade'] = $cascades;
1304
        }
1305
1306 949
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1307 949
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1308 949
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1309 949
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1310 949
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1311
1312 949
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1313 63
            $mapping['file'] = true;
1314
        }
1315 949
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1316 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1317
        }
1318 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...
1319 63
            $this->file = $mapping['fieldName'];
1320 63
            $mapping['name'] = 'file';
1321
        }
1322 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...
1323 7
            $this->distance = $mapping['fieldName'];
1324
        }
1325 949
        if (isset($mapping['id']) && $mapping['id'] === true) {
1326 921
            $mapping['name'] = '_id';
1327 921
            $this->identifier = $mapping['fieldName'];
1328 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...
1329 902
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1330
            }
1331 921
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1332 921
            switch ($this->generatorType) {
1333 921
                case self::GENERATOR_TYPE_AUTO:
1334 847
                    $mapping['type'] = 'id';
1335 847
                    break;
1336
                default:
1337 153
                    if ( ! empty($this->generatorOptions['type'])) {
1338 52
                        $mapping['type'] = $this->generatorOptions['type'];
1339 101
                    } elseif (empty($mapping['type'])) {
1340 86
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1341
                    }
1342
            }
1343 921
            unset($this->generatorOptions['type']);
1344
        }
1345
1346 949
        if ( ! isset($mapping['nullable'])) {
1347 53
            $mapping['nullable'] = false;
1348
        }
1349
1350
        // Synchronize the "simple" and "storeAs" mapping information for backwards compatibility
1351 949
        if (isset($mapping['simple']) && ($mapping['simple'] === true || $mapping['simple'] === 'true')) {
1352 293
            $mapping['storeAs'] = ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1353 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...
1354
        }
1355
        // Provide the correct value for the "simple" field for backwards compatibility
1356 949
        if (isset($mapping['storeAs'])) {
1357 583
            $mapping['simple'] = $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1358
        }
1359
1360 949
        if (isset($mapping['reference'])
1361 949
            && isset($mapping['storeAs'])
1362 949
            && $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID
1363 949
            && ! isset($mapping['targetDocument'])
1364
        ) {
1365 3
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1366
        }
1367
1368 946
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1369 946
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1370 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1371
        }
1372
1373 942
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1374 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1375
        }
1376
1377 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...
1378 516
            $mapping['association'] = self::REFERENCE_ONE;
1379
        }
1380 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...
1381 460
            $mapping['association'] = self::REFERENCE_MANY;
1382
        }
1383 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...
1384 462
            $mapping['association'] = self::EMBED_ONE;
1385
        }
1386 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...
1387 505
            $mapping['association'] = self::EMBED_MANY;
1388
        }
1389
1390 941
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1391 133
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1392
        }
1393
1394
        /*
1395
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1396
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1397
        }
1398
        */
1399 941
        if (isset($mapping['version'])) {
1400 100
            $mapping['notSaved'] = true;
1401 100
            $this->setVersionMapping($mapping);
1402
        }
1403 941
        if (isset($mapping['lock'])) {
1404 27
            $mapping['notSaved'] = true;
1405 27
            $this->setLockMapping($mapping);
1406
        }
1407 941
        $mapping['isOwningSide'] = true;
1408 941
        $mapping['isInverseSide'] = false;
1409 941
        if (isset($mapping['reference'])) {
1410 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...
1411 92
                $mapping['isOwningSide'] = true;
1412 92
                $mapping['isInverseSide'] = false;
1413
            }
1414 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...
1415 295
                $mapping['isInverseSide'] = true;
1416 295
                $mapping['isOwningSide'] = false;
1417
            }
1418 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...
1419 67
                $mapping['isInverseSide'] = true;
1420 67
                $mapping['isOwningSide'] = false;
1421
            }
1422 588
            if (!isset($mapping['orphanRemoval'])) {
1423 563
                $mapping['orphanRemoval'] = false;
1424
            }
1425
        }
1426
1427 941
        if (!empty($mapping['prime']) && ($mapping['association'] !== self::REFERENCE_MANY || !$mapping['isInverseSide'])) {
1428
            throw MappingException::referencePrimersOnlySupportedForInverseReferenceMany($this->name, $mapping['fieldName']);
1429
        }
1430
1431 941
        $this->applyStorageStrategy($mapping);
1432
1433 940
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1434 940
        if (isset($mapping['association'])) {
1435 741
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1436
        }
1437
1438 940
        return $mapping;
1439
    }
1440
1441
    /**
1442
     * Validates the storage strategy of a mapping for consistency
1443
     * @param array $mapping
1444
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1445
     */
1446 941
    private function applyStorageStrategy(array &$mapping)
1447
    {
1448 941
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1449 923
            return;
1450
        }
1451
1452
        switch (true) {
1453 903
            case $mapping['type'] == 'int':
1454 902
            case $mapping['type'] == 'float':
1455 902
            case $mapping['type'] == 'increment':
1456 334
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1457 334
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1458 334
                break;
1459
1460 901
            case $mapping['type'] == 'many':
1461 618
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1462
                $allowedStrategies = [
1463 618
                    self::STORAGE_STRATEGY_PUSH_ALL,
1464 618
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1465 618
                    self::STORAGE_STRATEGY_SET,
1466 618
                    self::STORAGE_STRATEGY_SET_ARRAY,
1467 618
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1468 618
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1469
                ];
1470 618
                break;
1471
1472
            default:
1473 889
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1474 889
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1475
        }
1476
1477 903
        if (! isset($mapping['strategy'])) {
1478 892
            $mapping['strategy'] = $defaultStrategy;
1479
        }
1480
1481 903
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1482
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1483
        }
1484
1485 903
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1486 903
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1487 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1488
        }
1489 902
    }
1490
1491
    /**
1492
     * Map a MongoGridFSFile.
1493
     *
1494
     * @param array $mapping The mapping information.
1495
     */
1496
    public function mapFile(array $mapping)
1497
    {
1498
        $mapping['file'] = true;
1499
        $mapping['type'] = 'file';
1500
        $this->mapField($mapping);
1501
    }
1502
1503
    /**
1504
     * Map a single embedded document.
1505
     *
1506
     * @param array $mapping The mapping information.
1507
     */
1508 6
    public function mapOneEmbedded(array $mapping)
1509
    {
1510 6
        $mapping['embedded'] = true;
1511 6
        $mapping['type'] = 'one';
1512 6
        $this->mapField($mapping);
1513 5
    }
1514
1515
    /**
1516
     * Map a collection of embedded documents.
1517
     *
1518
     * @param array $mapping The mapping information.
1519
     */
1520 5
    public function mapManyEmbedded(array $mapping)
1521
    {
1522 5
        $mapping['embedded'] = true;
1523 5
        $mapping['type'] = 'many';
1524 5
        $this->mapField($mapping);
1525 5
    }
1526
1527
    /**
1528
     * Map a single document reference.
1529
     *
1530
     * @param array $mapping The mapping information.
1531
     */
1532 8
    public function mapOneReference(array $mapping)
1533
    {
1534 8
        $mapping['reference'] = true;
1535 8
        $mapping['type'] = 'one';
1536 8
        $this->mapField($mapping);
1537 8
    }
1538
1539
    /**
1540
     * Map a collection of document references.
1541
     *
1542
     * @param array $mapping The mapping information.
1543
     */
1544 8
    public function mapManyReference(array $mapping)
1545
    {
1546 8
        $mapping['reference'] = true;
1547 8
        $mapping['type'] = 'many';
1548 8
        $this->mapField($mapping);
1549 8
    }
1550
1551
    /**
1552
     * INTERNAL:
1553
     * Adds a field mapping without completing/validating it.
1554
     * This is mainly used to add inherited field mappings to derived classes.
1555
     *
1556
     * @param array $fieldMapping
1557
     */
1558 129
    public function addInheritedFieldMapping(array $fieldMapping)
1559
    {
1560 129
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1561
1562 129
        if (isset($fieldMapping['association'])) {
1563 77
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1564
        }
1565 129
    }
1566
1567
    /**
1568
     * INTERNAL:
1569
     * Adds an association mapping without completing/validating it.
1570
     * This is mainly used to add inherited association mappings to derived classes.
1571
     *
1572
     * @param array $mapping
1573
     *
1574
     * @return void
1575
     *
1576
     * @throws MappingException
1577
     */
1578 78
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1579
    {
1580 78
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1581 78
    }
1582
1583
    /**
1584
     * Checks whether the class has a mapped association with the given field name.
1585
     *
1586
     * @param string $fieldName
1587
     * @return boolean
1588
     */
1589 14
    public function hasReference($fieldName)
1590
    {
1591 14
        return isset($this->fieldMappings[$fieldName]['reference']);
1592
    }
1593
1594
    /**
1595
     * Checks whether the class has a mapped embed with the given field name.
1596
     *
1597
     * @param string $fieldName
1598
     * @return boolean
1599
     */
1600 5
    public function hasEmbed($fieldName)
1601
    {
1602 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1603
    }
1604
1605
    /**
1606
     * {@inheritDoc}
1607
     *
1608
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1609
     */
1610 7
    public function hasAssociation($fieldName)
1611
    {
1612 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1613
    }
1614
1615
    /**
1616
     * {@inheritDoc}
1617
     *
1618
     * Checks whether the class has a mapped reference or embed for the specified field and
1619
     * is a single valued association.
1620
     */
1621
    public function isSingleValuedAssociation($fieldName)
1622
    {
1623
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1624
    }
1625
1626
    /**
1627
     * {@inheritDoc}
1628
     *
1629
     * Checks whether the class has a mapped reference or embed for the specified field and
1630
     * is a collection valued association.
1631
     */
1632
    public function isCollectionValuedAssociation($fieldName)
1633
    {
1634
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1635
    }
1636
1637
    /**
1638
     * Checks whether the class has a mapped association for the specified field
1639
     * and if yes, checks whether it is a single-valued association (to-one).
1640
     *
1641
     * @param string $fieldName
1642
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1643
     */
1644
    public function isSingleValuedReference($fieldName)
1645
    {
1646
        return isset($this->fieldMappings[$fieldName]['association']) &&
1647
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1648
    }
1649
1650
    /**
1651
     * Checks whether the class has a mapped association for the specified field
1652
     * and if yes, checks whether it is a collection-valued association (to-many).
1653
     *
1654
     * @param string $fieldName
1655
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1656
     */
1657
    public function isCollectionValuedReference($fieldName)
1658
    {
1659
        return isset($this->fieldMappings[$fieldName]['association']) &&
1660
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1661
    }
1662
1663
    /**
1664
     * Checks whether the class has a mapped embedded document for the specified field
1665
     * and if yes, checks whether it is a single-valued association (to-one).
1666
     *
1667
     * @param string $fieldName
1668
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1669
     */
1670
    public function isSingleValuedEmbed($fieldName)
1671
    {
1672
        return isset($this->fieldMappings[$fieldName]['association']) &&
1673
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1674
    }
1675
1676
    /**
1677
     * Checks whether the class has a mapped embedded document for the specified field
1678
     * and if yes, checks whether it is a collection-valued association (to-many).
1679
     *
1680
     * @param string $fieldName
1681
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1682
     */
1683
    public function isCollectionValuedEmbed($fieldName)
1684
    {
1685
        return isset($this->fieldMappings[$fieldName]['association']) &&
1686
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1687
    }
1688
1689
    /**
1690
     * Sets the ID generator used to generate IDs for instances of this class.
1691
     *
1692
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1693
     */
1694 842
    public function setIdGenerator($generator)
1695
    {
1696 842
        $this->idGenerator = $generator;
1697 842
    }
1698
1699
    /**
1700
     * Casts the identifier to its portable PHP type.
1701
     *
1702
     * @param mixed $id
1703
     * @return mixed $id
1704
     */
1705 647
    public function getPHPIdentifierValue($id)
1706
    {
1707 647
        $idType = $this->fieldMappings[$this->identifier]['type'];
1708 647
        return Type::getType($idType)->convertToPHPValue($id);
1709
    }
1710
1711
    /**
1712
     * Casts the identifier to its database type.
1713
     *
1714
     * @param mixed $id
1715
     * @return mixed $id
1716
     */
1717 714
    public function getDatabaseIdentifierValue($id)
1718
    {
1719 714
        $idType = $this->fieldMappings[$this->identifier]['type'];
1720 714
        return Type::getType($idType)->convertToDatabaseValue($id);
1721
    }
1722
1723
    /**
1724
     * Sets the document identifier of a document.
1725
     *
1726
     * The value will be converted to a PHP type before being set.
1727
     *
1728
     * @param object $document
1729
     * @param mixed $id
1730
     */
1731 576
    public function setIdentifierValue($document, $id)
1732
    {
1733 576
        $id = $this->getPHPIdentifierValue($id);
1734 576
        $this->reflFields[$this->identifier]->setValue($document, $id);
1735 576
    }
1736
1737
    /**
1738
     * Gets the document identifier as a PHP type.
1739
     *
1740
     * @param object $document
1741
     * @return mixed $id
1742
     */
1743 665
    public function getIdentifierValue($document)
1744
    {
1745 665
        return $this->reflFields[$this->identifier]->getValue($document);
1746
    }
1747
1748
    /**
1749
     * {@inheritDoc}
1750
     *
1751
     * Since MongoDB only allows exactly one identifier field this is a proxy
1752
     * to {@see getIdentifierValue()} and returns an array with the identifier
1753
     * field as a key.
1754
     */
1755
    public function getIdentifierValues($object)
1756
    {
1757
        return array($this->identifier => $this->getIdentifierValue($object));
1758
    }
1759
1760
    /**
1761
     * Get the document identifier object as a database type.
1762
     *
1763
     * @param object $document
1764
     *
1765
     * @return \MongoId $id The MongoID object.
1766
     */
1767 36
    public function getIdentifierObject($document)
1768
    {
1769 36
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1770
    }
1771
1772
    /**
1773
     * Sets the specified field to the specified value on the given document.
1774
     *
1775
     * @param object $document
1776
     * @param string $field
1777
     * @param mixed $value
1778
     */
1779 11
    public function setFieldValue($document, $field, $value)
1780
    {
1781 11
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1782
            //property changes to an uninitialized proxy will not be tracked or persisted,
1783
            //so the proxy needs to be loaded first.
1784 1
            $document->__load();
1785
        }
1786
1787 11
        $this->reflFields[$field]->setValue($document, $value);
1788 11
    }
1789
1790
    /**
1791
     * Gets the specified field's value off the given document.
1792
     *
1793
     * @param object $document
1794
     * @param string $field
1795
     *
1796
     * @return mixed
1797
     */
1798 31
    public function getFieldValue($document, $field)
1799
    {
1800 31
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1801 1
            $document->__load();
1802
        }
1803
1804 31
        return $this->reflFields[$field]->getValue($document);
1805
    }
1806
1807
    /**
1808
     * Gets the mapping of a field.
1809
     *
1810
     * @param string $fieldName  The field name.
1811
     *
1812
     * @return array  The field mapping.
1813
     *
1814
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1815
     */
1816 102
    public function getFieldMapping($fieldName)
1817
    {
1818 102
        if ( ! isset($this->fieldMappings[$fieldName])) {
1819 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1820
        }
1821 100
        return $this->fieldMappings[$fieldName];
1822
    }
1823
1824
    /**
1825
     * Gets mappings of fields holding embedded document(s).
1826
     *
1827
     * @return array of field mappings
1828
     */
1829 616
    public function getEmbeddedFieldsMappings()
1830
    {
1831 616
        return array_filter(
1832 616
            $this->associationMappings,
1833
            function($assoc) { return ! empty($assoc['embedded']); }
1834
        );
1835
    }
1836
1837
    /**
1838
     * Gets the field mapping by its DB name.
1839
     * E.g. it returns identifier's mapping when called with _id.
1840
     *
1841
     * @param string $dbFieldName
1842
     *
1843
     * @return array
1844
     * @throws MappingException
1845
     */
1846 3
    public function getFieldMappingByDbFieldName($dbFieldName)
1847
    {
1848 3
        foreach ($this->fieldMappings as $mapping) {
1849 3
            if ($mapping['name'] == $dbFieldName) {
1850 3
                return $mapping;
1851
            }
1852
        }
1853
1854
        throw MappingException::mappingNotFoundByDbName($this->name, $dbFieldName);
1855
    }
1856
1857
    /**
1858
     * Check if the field is not null.
1859
     *
1860
     * @param string $fieldName  The field name
1861
     *
1862
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1863
     */
1864 1
    public function isNullable($fieldName)
1865
    {
1866 1
        $mapping = $this->getFieldMapping($fieldName);
1867 1
        if ($mapping !== false) {
1868 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1869
        }
1870
        return false;
1871
    }
1872
1873
    /**
1874
     * Checks whether the document has a discriminator field and value configured.
1875
     *
1876
     * @return boolean
1877
     */
1878 531
    public function hasDiscriminator()
1879
    {
1880 531
        return isset($this->discriminatorField, $this->discriminatorValue);
1881
    }
1882
1883
    /**
1884
     * Sets the type of Id generator to use for the mapped class.
1885
     *
1886
     * @param string $generatorType Generator type.
1887
     */
1888 375
    public function setIdGeneratorType($generatorType)
1889
    {
1890 375
        $this->generatorType = $generatorType;
1891 375
    }
1892
1893
    /**
1894
     * Sets the Id generator options.
1895
     *
1896
     * @param array $generatorOptions Generator options.
1897
     */
1898
    public function setIdGeneratorOptions($generatorOptions)
1899
    {
1900
        $this->generatorOptions = $generatorOptions;
1901
    }
1902
1903
    /**
1904
     * @return boolean
1905
     */
1906 622
    public function isInheritanceTypeNone()
1907
    {
1908 622
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1909
    }
1910
1911
    /**
1912
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1913
     *
1914
     * @return boolean
1915
     */
1916 368
    public function isInheritanceTypeSingleCollection()
1917
    {
1918 368
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1919
    }
1920
1921
    /**
1922
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1923
     *
1924
     * @return boolean
1925
     */
1926
    public function isInheritanceTypeCollectionPerClass()
1927
    {
1928
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1929
    }
1930
1931
    /**
1932
     * Sets the mapped subclasses of this class.
1933
     *
1934
     * @param string[] $subclasses The names of all mapped subclasses.
1935
     */
1936 2
    public function setSubclasses(array $subclasses)
1937
    {
1938 2
        foreach ($subclasses as $subclass) {
1939 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1940 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1941
            } else {
1942 2
                $this->subClasses[] = $subclass;
1943
            }
1944
        }
1945 2
    }
1946
1947
    /**
1948
     * Sets the parent class names.
1949
     * Assumes that the class names in the passed array are in the order:
1950
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1951
     *
1952
     * @param string[] $classNames
1953
     */
1954 900
    public function setParentClasses(array $classNames)
1955
    {
1956 900
        $this->parentClasses = $classNames;
1957
1958 900
        if (count($classNames) > 0) {
1959 113
            $this->rootDocumentName = array_pop($classNames);
1960
        }
1961 900
    }
1962
1963
    /**
1964
     * Checks whether the class will generate a new \MongoId instance for us.
1965
     *
1966
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1967
     */
1968
    public function isIdGeneratorAuto()
1969
    {
1970
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1971
    }
1972
1973
    /**
1974
     * Checks whether the class will use a collection to generate incremented identifiers.
1975
     *
1976
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1977
     */
1978
    public function isIdGeneratorIncrement()
1979
    {
1980
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1981
    }
1982
1983
    /**
1984
     * Checks whether the class will generate a uuid id.
1985
     *
1986
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1987
     */
1988
    public function isIdGeneratorUuid()
1989
    {
1990
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1991
    }
1992
1993
    /**
1994
     * Checks whether the class uses no id generator.
1995
     *
1996
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1997
     */
1998
    public function isIdGeneratorNone()
1999
    {
2000
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
2001
    }
2002
2003
    /**
2004
     * Sets the version field mapping used for versioning. Sets the default
2005
     * value to use depending on the column type.
2006
     *
2007
     * @param array $mapping   The version field mapping array
2008
     *
2009
     * @throws LockException
2010
     */
2011 100
    public function setVersionMapping(array &$mapping)
2012
    {
2013 100
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
2014 1
            throw LockException::invalidVersionFieldType($mapping['type']);
2015
        }
2016
2017 99
        $this->isVersioned  = true;
2018 99
        $this->versionField = $mapping['fieldName'];
2019 99
    }
2020
2021
    /**
2022
     * Sets whether this class is to be versioned for optimistic locking.
2023
     *
2024
     * @param boolean $bool
2025
     */
2026 369
    public function setVersioned($bool)
2027
    {
2028 369
        $this->isVersioned = $bool;
2029 369
    }
2030
2031
    /**
2032
     * Sets the name of the field that is to be used for versioning if this class is
2033
     * versioned for optimistic locking.
2034
     *
2035
     * @param string $versionField
2036
     */
2037 369
    public function setVersionField($versionField)
2038
    {
2039 369
        $this->versionField = $versionField;
2040 369
    }
2041
2042
    /**
2043
     * Sets the version field mapping used for versioning. Sets the default
2044
     * value to use depending on the column type.
2045
     *
2046
     * @param array $mapping   The version field mapping array
2047
     *
2048
     * @throws \Doctrine\ODM\MongoDB\LockException
2049
     */
2050 27
    public function setLockMapping(array &$mapping)
2051
    {
2052 27
        if ($mapping['type'] !== 'int') {
2053 1
            throw LockException::invalidLockFieldType($mapping['type']);
2054
        }
2055
2056 26
        $this->isLockable = true;
2057 26
        $this->lockField = $mapping['fieldName'];
2058 26
    }
2059
2060
    /**
2061
     * Sets whether this class is to allow pessimistic locking.
2062
     *
2063
     * @param boolean $bool
2064
     */
2065
    public function setLockable($bool)
2066
    {
2067
        $this->isLockable = $bool;
2068
    }
2069
2070
    /**
2071
     * Sets the name of the field that is to be used for storing whether a document
2072
     * is currently locked or not.
2073
     *
2074
     * @param string $lockField
2075
     */
2076
    public function setLockField($lockField)
2077
    {
2078
        $this->lockField = $lockField;
2079
    }
2080
2081
    /**
2082
     * {@inheritDoc}
2083
     */
2084
    public function getFieldNames()
2085
    {
2086
        return array_keys($this->fieldMappings);
2087
    }
2088
2089
    /**
2090
     * {@inheritDoc}
2091
     */
2092
    public function getAssociationNames()
2093
    {
2094
        return array_keys($this->associationMappings);
2095
    }
2096
2097
    /**
2098
     * {@inheritDoc}
2099
     */
2100 22
    public function getTypeOfField($fieldName)
2101
    {
2102 22
        return isset($this->fieldMappings[$fieldName]) ?
2103 22
            $this->fieldMappings[$fieldName]['type'] : null;
2104
    }
2105
2106
    /**
2107
     * {@inheritDoc}
2108
     */
2109 6
    public function getAssociationTargetClass($assocName)
2110
    {
2111 6
        if ( ! isset($this->associationMappings[$assocName])) {
2112 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2113
        }
2114
2115 3
        return $this->associationMappings[$assocName]['targetDocument'];
2116
    }
2117
2118
    /**
2119
     * Retrieve the collectionClass associated with an association
2120
     *
2121
     * @param string $assocName
2122
     */
2123 2
    public function getAssociationCollectionClass($assocName)
2124
    {
2125 2
        if ( ! isset($this->associationMappings[$assocName])) {
2126
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2127
        }
2128
2129 2
        if ( ! array_key_exists('collectionClass', $this->associationMappings[$assocName])) {
2130
            throw new InvalidArgumentException("collectionClass can only be applied to 'embedMany' and 'referenceMany' associations.");
2131
        }
2132
2133 2
        return $this->associationMappings[$assocName]['collectionClass'];
2134
    }
2135
2136
    /**
2137
     * {@inheritDoc}
2138
     */
2139
    public function isAssociationInverseSide($fieldName)
2140
    {
2141
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2142
    }
2143
2144
    /**
2145
     * {@inheritDoc}
2146
     */
2147
    public function getAssociationMappedByTargetField($fieldName)
2148
    {
2149
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2150
    }
2151
}
2152