Completed
Pull Request — master (#1379)
by Maciej
16:13
created

ClassMetadataInfo::getIdentifierValues()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

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