Completed
Push — master ( 61f8e0...b1cbaf )
by Maciej
13:57
created

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