Completed
Push — master ( 6b4adf...8f1c34 )
by Maciej
40:03 queued 19:59
created

ClassMetadataInfo::getCollection()   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\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 860
    public function __construct($documentName)
437
    {
438 860
        $this->name = $documentName;
439 860
        $this->rootDocumentName = $documentName;
440 860
    }
441
442
    /**
443
     * {@inheritDoc}
444
     */
445 810
    public function getReflectionClass()
446
    {
447 810
        if ( ! $this->reflClass) {
448 2
            $this->reflClass = new \ReflectionClass($this->name);
449 2
        }
450
451 810
        return $this->reflClass;
452
    }
453
454
    /**
455
     * {@inheritDoc}
456
     */
457 299
    public function isIdentifier($fieldName)
458
    {
459 299
        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 90
    public function getIdentifierFieldNames()
491
    {
492 90
        return array($this->identifier);
493
    }
494
495
    /**
496
     * {@inheritDoc}
497
     */
498 518
    public function hasField($fieldName)
499
    {
500 518
        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 810
    public function isInheritedField($fieldName)
521
    {
522 810
        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 595
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
554
    {
555 595
        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 594
        if (empty($this->lifecycleCallbacks[$event])) {
560 581
            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 802
    public function setRequireIndexes($requireIndexes)
795
    {
796 802
        $this->requireIndexes = $requireIndexes;
797 802
    }
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 570
    public function isChangeTrackingDeferredImplicit()
845
    {
846 570
        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 328
    public function isChangeTrackingNotify()
855
    {
856 328
        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 90
    public function getReflectionProperties()
865
    {
866 90
        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 807
    public function getName()
885
    {
886 807
        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 748
    public function getDatabase()
905
    {
906 748
        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 752
    public function getCollection()
925
    {
926 752
        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 840
    public function setCollection($name)
937
    {
938 840
        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 840
            $this->collection = $name;
948
        }
949 840
    }
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 701
    public function isFile()
1027
    {
1028 701
        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 846
    public function mapField(array $mapping)
1081
    {
1082 846
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1083 8
            $mapping['fieldName'] = $mapping['name'];
1084 8
        }
1085 846
        if ( ! isset($mapping['fieldName'])) {
1086
            throw MappingException::missingFieldName($this->name);
1087
        }
1088 846
        if ( ! isset($mapping['name'])) {
1089 838
            $mapping['name'] = $mapping['fieldName'];
1090 838
        }
1091 846
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1092 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1093
        }
1094 845
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1095
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1096 53
        }
1097 845
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1098 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1099
        }
1100 844
        if (isset($mapping['targetDocument']) && strpos($mapping['targetDocument'], '\\') === false && strlen($this->namespace)) {
1101 540
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1102 540
        }
1103
1104 844
        if (isset($mapping['discriminatorMap'])) {
1105 103
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1106 103
                if (strpos($class, '\\') === false && strlen($this->namespace)) {
1107 68
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1108 68
                }
1109 103
            }
1110 103
        }
1111
1112 844
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1113 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1114
        }
1115
1116 843
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1117
1118 843
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1119 565
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1120 565
        }
1121
1122 843
        if (isset($mapping['embedded'])) {
1123 531
            unset($mapping['cascade']);
1124 843
        } elseif (isset($mapping['cascade'])) {
1125 371
            $mapping['cascade'] = $cascades;
1126 371
        }
1127
1128 843
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1129 843
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1130 843
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1131 843
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1132 843
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1133
1134 843
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1135 56
            $mapping['file'] = true;
1136 56
        }
1137 843
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1138 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1139 1
        }
1140 843 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...
1141 56
            $this->file = $mapping['fieldName'];
1142 56
            $mapping['name'] = 'file';
1143 56
        }
1144 843 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...
1145 7
            $this->distance = $mapping['fieldName'];
1146 7
        }
1147 843
        if (isset($mapping['id']) && $mapping['id'] === true) {
1148 823
            $mapping['name'] = '_id';
1149 823
            $this->identifier = $mapping['fieldName'];
1150 823 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...
1151 808
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1152 808
            }
1153 823
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1154 823
            switch ($this->generatorType) {
1155 823
                case self::GENERATOR_TYPE_AUTO:
1156 756
                    $mapping['type'] = 'id';
1157 756
                    break;
1158 149
                default:
1159 149
                    if ( ! empty($this->generatorOptions['type'])) {
1160 52
                        $mapping['type'] = $this->generatorOptions['type'];
1161 149
                    } elseif (empty($mapping['type'])) {
1162 74
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1163 74
                    }
1164 823
            }
1165 823
            unset($this->generatorOptions['type']);
1166 823
        }
1167
1168 843
        if ( ! isset($mapping['nullable'])) {
1169 40
            $mapping['nullable'] = false;
1170 40
        }
1171
1172 843
        if (isset($mapping['reference']) && ! empty($mapping['simple']) && ! isset($mapping['targetDocument'])) {
1173 1
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1174
        }
1175
1176 842
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1177 842
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1178 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1179
        }
1180
1181 838
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1182 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1183
        }
1184
1185 837 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...
1186 460
            $mapping['association'] = self::REFERENCE_ONE;
1187 460
        }
1188 837 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...
1189 403
            $mapping['association'] = self::REFERENCE_MANY;
1190 403
        }
1191 837 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...
1192 411
            $mapping['association'] = self::EMBED_ONE;
1193 411
        }
1194 837 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...
1195 462
            $mapping['association'] = self::EMBED_MANY;
1196 462
        }
1197
1198 837
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1199 107
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1200 107
        }
1201
1202
        /*
1203
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1204
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1205
        }
1206
        */
1207 837
        if (isset($mapping['version'])) {
1208 93
            $mapping['notSaved'] = true;
1209 93
            $this->setVersionMapping($mapping);
1210 92
        }
1211 837
        if (isset($mapping['lock'])) {
1212 26
            $mapping['notSaved'] = true;
1213 26
            $this->setLockMapping($mapping);
1214 25
        }
1215 837
        $mapping['isOwningSide'] = true;
1216 837
        $mapping['isInverseSide'] = false;
1217 837
        if (isset($mapping['reference'])) {
1218 517 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...
1219 220
                $mapping['isOwningSide'] = true;
1220 220
                $mapping['isInverseSide'] = false;
1221 220
            }
1222 517 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...
1223 259
                $mapping['isInverseSide'] = true;
1224 259
                $mapping['isOwningSide'] = false;
1225 259
            }
1226 517 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...
1227 200
                $mapping['isInverseSide'] = true;
1228 200
                $mapping['isOwningSide'] = false;
1229 200
            }
1230 517
            if (!isset($mapping['orphanRemoval'])) {
1231 497
                $mapping['orphanRemoval'] = false;
1232 497
            }
1233 517
        }
1234
1235 837
        $this->applyStorageStrategy($mapping);
1236
1237 836
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1238 836
        if (isset($mapping['association'])) {
1239 656
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1240 656
        }
1241
1242 836
        return $mapping;
1243
    }
1244
1245
    /**
1246
     * Validates the storage strategy of a mapping for consistency
1247
     * @param array $mapping
1248
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1249
     */
1250 837
    private function applyStorageStrategy(array &$mapping)
1251
    {
1252 837
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1253 825
            return;
1254
        }
1255
1256 805
        switch (true) {
1257 805
            case $mapping['type'] == 'int':
1258 805
            case $mapping['type'] == 'float':
1259 805
            case $mapping['type'] == 'increment':
1260 305
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1261 305
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1262 305
                break;
1263
1264 804
            case $mapping['type'] == 'many':
1265 550
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1266
                $allowedStrategies = [
1267 550
                    self::STORAGE_STRATEGY_PUSH_ALL,
1268 550
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1269 550
                    self::STORAGE_STRATEGY_SET,
1270 550
                    self::STORAGE_STRATEGY_SET_ARRAY,
1271 550
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1272 550
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1273 550
                ];
1274 550
                break;
1275
1276 800
            default:
1277 800
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1278 800
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1279 800
        }
1280
1281 805
        if (! isset($mapping['strategy'])) {
1282 801
            $mapping['strategy'] = $defaultStrategy;
1283 801
        }
1284
1285 805
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1286
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1287
        }
1288
1289 805
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1290 805
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1291 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1292
        }
1293 804
    }
1294
1295
    /**
1296
     * Map a MongoGridFSFile.
1297
     *
1298
     * @param array $mapping The mapping information.
1299
     */
1300
    public function mapFile(array $mapping)
1301
    {
1302
        $mapping['file'] = true;
1303
        $mapping['type'] = 'file';
1304
        $this->mapField($mapping);
1305
    }
1306
1307
    /**
1308
     * Map a single embedded document.
1309
     *
1310
     * @param array $mapping The mapping information.
1311
     */
1312 6
    public function mapOneEmbedded(array $mapping)
1313
    {
1314 6
        $mapping['embedded'] = true;
1315 6
        $mapping['type'] = 'one';
1316 6
        $this->mapField($mapping);
1317 5
    }
1318
1319
    /**
1320
     * Map a collection of embedded documents.
1321
     *
1322
     * @param array $mapping The mapping information.
1323
     */
1324 3
    public function mapManyEmbedded(array $mapping)
1325
    {
1326 3
        $mapping['embedded'] = true;
1327 3
        $mapping['type'] = 'many';
1328 3
        $this->mapField($mapping);
1329 3
    }
1330
1331
    /**
1332
     * Map a single document reference.
1333
     *
1334
     * @param array $mapping The mapping information.
1335
     */
1336 8
    public function mapOneReference(array $mapping)
1337
    {
1338 8
        $mapping['reference'] = true;
1339 8
        $mapping['type'] = 'one';
1340 8
        $this->mapField($mapping);
1341 8
    }
1342
1343
    /**
1344
     * Map a collection of document references.
1345
     *
1346
     * @param array $mapping The mapping information.
1347
     */
1348 8
    public function mapManyReference(array $mapping)
1349
    {
1350 8
        $mapping['reference'] = true;
1351 8
        $mapping['type'] = 'many';
1352 8
        $this->mapField($mapping);
1353 8
    }
1354
1355
    /**
1356
     * INTERNAL:
1357
     * Adds a field mapping without completing/validating it.
1358
     * This is mainly used to add inherited field mappings to derived classes.
1359
     *
1360
     * @param array $fieldMapping
1361
     */
1362 115
    public function addInheritedFieldMapping(array $fieldMapping)
1363
    {
1364 115
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1365
1366 115
        if (isset($fieldMapping['association'])) {
1367 76
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1368 76
        }
1369 115
    }
1370
1371
    /**
1372
     * INTERNAL:
1373
     * Adds an association mapping without completing/validating it.
1374
     * This is mainly used to add inherited association mappings to derived classes.
1375
     *
1376
     * @param array $mapping
1377
     *
1378
     * @return void
1379
     *
1380
     * @throws MappingException
1381
     */
1382 77
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1383
    {
1384 77
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1385 77
    }
1386
1387
    /**
1388
     * Checks whether the class has a mapped association with the given field name.
1389
     *
1390
     * @param string $fieldName
1391
     * @return boolean
1392
     */
1393 7
    public function hasReference($fieldName)
1394
    {
1395 7
        return isset($this->fieldMappings[$fieldName]['reference']);
1396
    }
1397
1398
    /**
1399
     * Checks whether the class has a mapped embed with the given field name.
1400
     *
1401
     * @param string $fieldName
1402
     * @return boolean
1403
     */
1404 5
    public function hasEmbed($fieldName)
1405
    {
1406 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1407
    }
1408
1409
    /**
1410
     * {@inheritDoc}
1411
     *
1412
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1413
     */
1414 7
    public function hasAssociation($fieldName)
1415
    {
1416 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1417
    }
1418
1419
    /**
1420
     * {@inheritDoc}
1421
     *
1422
     * Checks whether the class has a mapped reference or embed for the specified field and
1423
     * is a single valued association.
1424
     */
1425
    public function isSingleValuedAssociation($fieldName)
1426
    {
1427
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1428
    }
1429
1430
    /**
1431
     * {@inheritDoc}
1432
     *
1433
     * Checks whether the class has a mapped reference or embed for the specified field and
1434
     * is a collection valued association.
1435
     */
1436
    public function isCollectionValuedAssociation($fieldName)
1437
    {
1438
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1439
    }
1440
1441
    /**
1442
     * Checks whether the class has a mapped association for the specified field
1443
     * and if yes, checks whether it is a single-valued association (to-one).
1444
     *
1445
     * @param string $fieldName
1446
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1447
     */
1448
    public function isSingleValuedReference($fieldName)
1449
    {
1450
        return isset($this->fieldMappings[$fieldName]['association']) &&
1451
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1452
    }
1453
1454
    /**
1455
     * Checks whether the class has a mapped association for the specified field
1456
     * and if yes, checks whether it is a collection-valued association (to-many).
1457
     *
1458
     * @param string $fieldName
1459
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1460
     */
1461
    public function isCollectionValuedReference($fieldName)
1462
    {
1463
        return isset($this->fieldMappings[$fieldName]['association']) &&
1464
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1465
    }
1466
1467
    /**
1468
     * Checks whether the class has a mapped embedded document for the specified field
1469
     * and if yes, checks whether it is a single-valued association (to-one).
1470
     *
1471
     * @param string $fieldName
1472
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1473
     */
1474
    public function isSingleValuedEmbed($fieldName)
1475
    {
1476
        return isset($this->fieldMappings[$fieldName]['association']) &&
1477
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1478
    }
1479
1480
    /**
1481
     * Checks whether the class has a mapped embedded document for the specified field
1482
     * and if yes, checks whether it is a collection-valued association (to-many).
1483
     *
1484
     * @param string $fieldName
1485
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1486
     */
1487
    public function isCollectionValuedEmbed($fieldName)
1488
    {
1489
        return isset($this->fieldMappings[$fieldName]['association']) &&
1490
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1491
    }
1492
1493
    /**
1494
     * Sets the ID generator used to generate IDs for instances of this class.
1495
     *
1496
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1497
     */
1498 750
    public function setIdGenerator($generator)
1499
    {
1500 750
        $this->idGenerator = $generator;
1501 750
    }
1502
1503
    /**
1504
     * Casts the identifier to its portable PHP type.
1505
     *
1506
     * @param mixed $id
1507
     * @return mixed $id
1508
     */
1509 587
    public function getPHPIdentifierValue($id)
1510
    {
1511 587
        $idType = $this->fieldMappings[$this->identifier]['type'];
1512 587
        return Type::getType($idType)->convertToPHPValue($id);
1513
    }
1514
1515
    /**
1516
     * Casts the identifier to its database type.
1517
     *
1518
     * @param mixed $id
1519
     * @return mixed $id
1520
     */
1521 649
    public function getDatabaseIdentifierValue($id)
1522
    {
1523 649
        $idType = $this->fieldMappings[$this->identifier]['type'];
1524 649
        return Type::getType($idType)->convertToDatabaseValue($id);
1525
    }
1526
1527
    /**
1528
     * Sets the document identifier of a document.
1529
     *
1530
     * The value will be converted to a PHP type before being set.
1531
     *
1532
     * @param object $document
1533
     * @param mixed $id
1534
     */
1535 520
    public function setIdentifierValue($document, $id)
1536
    {
1537 520
        $id = $this->getPHPIdentifierValue($id);
1538 520
        $this->reflFields[$this->identifier]->setValue($document, $id);
1539 520
    }
1540
1541
    /**
1542
     * Gets the document identifier as a PHP type.
1543
     *
1544
     * @param object $document
1545
     * @return mixed $id
1546
     */
1547 600
    public function getIdentifierValue($document)
1548
    {
1549 600
        return $this->reflFields[$this->identifier]->getValue($document);
1550
    }
1551
1552
    /**
1553
     * {@inheritDoc}
1554
     *
1555
     * Since MongoDB only allows exactly one identifier field this is a proxy
1556
     * to {@see getIdentifierValue()} and returns an array with the identifier
1557
     * field as a key.
1558
     */
1559
    public function getIdentifierValues($object)
1560
    {
1561
        return array($this->identifier => $this->getIdentifierValue($object));
1562
    }
1563
1564
    /**
1565
     * Get the document identifier object as a database type.
1566
     *
1567
     * @param object $document
1568
     *
1569
     * @return \MongoId $id The MongoID object.
1570
     */
1571 32
    public function getIdentifierObject($document)
1572
    {
1573 32
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1574
    }
1575
1576
    /**
1577
     * Sets the specified field to the specified value on the given document.
1578
     *
1579
     * @param object $document
1580
     * @param string $field
1581
     * @param mixed $value
1582
     */
1583 7
    public function setFieldValue($document, $field, $value)
1584
    {
1585 7
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1586
            //property changes to an uninitialized proxy will not be tracked or persisted,
1587
            //so the proxy needs to be loaded first.
1588 1
            $document->__load();
1589 1
        }
1590
1591 7
        $this->reflFields[$field]->setValue($document, $value);
1592 7
    }
1593
1594
    /**
1595
     * Gets the specified field's value off the given document.
1596
     *
1597
     * @param object $document
1598
     * @param string $field
1599
     *
1600
     * @return mixed
1601
     */
1602 25
    public function getFieldValue($document, $field)
1603
    {
1604 25
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1605 1
            $document->__load();
1606 1
        }
1607
1608 25
        return $this->reflFields[$field]->getValue($document);
1609
    }
1610
1611
    /**
1612
     * Gets the mapping of a field.
1613
     *
1614
     * @param string $fieldName  The field name.
1615
     *
1616
     * @return array  The field mapping.
1617
     *
1618
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1619
     *
1620
     * @throws MappingException
1621
     */
1622 90
    public function getFieldMapping($fieldName)
1623
    {
1624 90
        if ( ! isset($this->fieldMappings[$fieldName])) {
1625 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1626
        }
1627 88
        return $this->fieldMappings[$fieldName];
1628
    }
1629
1630
    /**
1631
     * Gets mappings of fields holding embedded document(s).
1632
     *
1633
     * @return array of field mappings
1634
     */
1635 562
    public function getEmbeddedFieldsMappings()
1636
    {
1637 562
        return array_filter(
1638 562
            $this->associationMappings,
1639
            function($assoc) { return ! empty($assoc['embedded']); }
1640 562
        );
1641
    }
1642
1643
    /**
1644
     * Check if the field is not null.
1645
     *
1646
     * @param string $fieldName  The field name
1647
     *
1648
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1649
     */
1650 1
    public function isNullable($fieldName)
1651
    {
1652 1
        $mapping = $this->getFieldMapping($fieldName);
1653 1
        if ($mapping !== false) {
1654 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1655
        }
1656
        return false;
1657
    }
1658
1659
    /**
1660
     * Checks whether the document has a discriminator field and value configured.
1661
     *
1662
     * @return boolean
1663
     */
1664 481
    public function hasDiscriminator()
1665
    {
1666 481
        return isset($this->discriminatorField, $this->discriminatorValue);
1667
    }
1668
1669
    /**
1670
     * Sets the type of Id generator to use for the mapped class.
1671
     */
1672 342
    public function setIdGeneratorType($generatorType)
1673
    {
1674 342
        $this->generatorType = $generatorType;
1675 342
    }
1676
1677
    /**
1678
     * Sets the Id generator options.
1679
     */
1680
    public function setIdGeneratorOptions($generatorOptions)
1681
    {
1682
        $this->generatorOptions = $generatorOptions;
1683
    }
1684
1685
    /**
1686
     * @return boolean
1687
     */
1688 568
    public function isInheritanceTypeNone()
1689
    {
1690 568
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1691
    }
1692
1693
    /**
1694
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1695
     *
1696
     * @return boolean
1697
     */
1698 336
    public function isInheritanceTypeSingleCollection()
1699
    {
1700 336
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1701
    }
1702
1703
    /**
1704
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1705
     *
1706
     * @return boolean
1707
     */
1708
    public function isInheritanceTypeCollectionPerClass()
1709
    {
1710
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1711
    }
1712
1713
    /**
1714
     * Sets the mapped subclasses of this class.
1715
     *
1716
     * @param string[] $subclasses The names of all mapped subclasses.
1717
     */
1718 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...
1719
    {
1720 2
        foreach ($subclasses as $subclass) {
1721 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1722 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1723 1
            } else {
1724 1
                $this->subClasses[] = $subclass;
1725
            }
1726 2
        }
1727 2
    }
1728
1729
    /**
1730
     * Sets the parent class names.
1731
     * Assumes that the class names in the passed array are in the order:
1732
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1733
     *
1734
     * @param string[] $classNames
1735
     */
1736 805
    public function setParentClasses(array $classNames)
1737
    {
1738 805
        $this->parentClasses = $classNames;
1739
1740 805
        if (count($classNames) > 0) {
1741 101
            $this->rootDocumentName = array_pop($classNames);
1742 101
        }
1743 805
    }
1744
1745
    /**
1746
     * Checks whether the class will generate a new \MongoId instance for us.
1747
     *
1748
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1749
     */
1750
    public function isIdGeneratorAuto()
1751
    {
1752
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1753
    }
1754
1755
    /**
1756
     * Checks whether the class will use a collection to generate incremented identifiers.
1757
     *
1758
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1759
     */
1760
    public function isIdGeneratorIncrement()
1761
    {
1762
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1763
    }
1764
1765
    /**
1766
     * Checks whether the class will generate a uuid id.
1767
     *
1768
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1769
     */
1770
    public function isIdGeneratorUuid()
1771
    {
1772
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1773
    }
1774
1775
    /**
1776
     * Checks whether the class uses no id generator.
1777
     *
1778
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1779
     */
1780
    public function isIdGeneratorNone()
1781
    {
1782
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1783
    }
1784
1785
    /**
1786
     * Sets the version field mapping used for versioning. Sets the default
1787
     * value to use depending on the column type.
1788
     *
1789
     * @param array $mapping   The version field mapping array
1790
     *
1791
     * @throws LockException
1792
     */
1793 93
    public function setVersionMapping(array &$mapping)
1794
    {
1795 93
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
1796 1
            throw LockException::invalidVersionFieldType($mapping['type']);
1797
        }
1798
1799 92
        $this->isVersioned  = true;
1800 92
        $this->versionField = $mapping['fieldName'];
1801 92
    }
1802
1803
    /**
1804
     * Sets whether this class is to be versioned for optimistic locking.
1805
     *
1806
     * @param boolean $bool
1807
     */
1808 336
    public function setVersioned($bool)
1809
    {
1810 336
        $this->isVersioned = $bool;
1811 336
    }
1812
1813
    /**
1814
     * Sets the name of the field that is to be used for versioning if this class is
1815
     * versioned for optimistic locking.
1816
     *
1817
     * @param string $versionField
1818
     */
1819 336
    public function setVersionField($versionField)
1820
    {
1821 336
        $this->versionField = $versionField;
1822 336
    }
1823
1824
    /**
1825
     * Sets the version field mapping used for versioning. Sets the default
1826
     * value to use depending on the column type.
1827
     *
1828
     * @param array $mapping   The version field mapping array
1829
     *
1830
     * @throws \Doctrine\ODM\MongoDB\LockException
1831
     */
1832 26
    public function setLockMapping(array &$mapping)
1833
    {
1834 26
        if ($mapping['type'] !== 'int') {
1835 1
            throw LockException::invalidLockFieldType($mapping['type']);
1836
        }
1837
1838 25
        $this->isLockable = true;
1839 25
        $this->lockField = $mapping['fieldName'];
1840 25
    }
1841
1842
    /**
1843
     * Sets whether this class is to allow pessimistic locking.
1844
     *
1845
     * @param boolean $bool
1846
     */
1847
    public function setLockable($bool)
1848
    {
1849
        $this->isLockable = $bool;
1850
    }
1851
1852
    /**
1853
     * Sets the name of the field that is to be used for storing whether a document
1854
     * is currently locked or not.
1855
     *
1856
     * @param string $lockField
1857
     */
1858
    public function setLockField($lockField)
1859
    {
1860
        $this->lockField = $lockField;
1861
    }
1862
1863
    /**
1864
     * {@inheritDoc}
1865
     */
1866
    public function getFieldNames()
1867
    {
1868
        return array_keys($this->fieldMappings);
1869
    }
1870
1871
    /**
1872
     * {@inheritDoc}
1873
     */
1874
    public function getAssociationNames()
1875
    {
1876
        return array_keys($this->associationMappings);
1877
    }
1878
1879
    /**
1880
     * {@inheritDoc}
1881
     */
1882 22
    public function getTypeOfField($fieldName)
1883
    {
1884 22
        return isset($this->fieldMappings[$fieldName]) ?
1885 22
            $this->fieldMappings[$fieldName]['type'] : null;
1886
    }
1887
1888
    /**
1889
     * {@inheritDoc}
1890
     */
1891 6
    public function getAssociationTargetClass($assocName)
1892
    {
1893 6
        if ( ! isset($this->associationMappings[$assocName])) {
1894 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
1895
        }
1896
1897 3
        return $this->associationMappings[$assocName]['targetDocument'];
1898
    }
1899
1900
    /**
1901
     * {@inheritDoc}
1902
     */
1903
    public function isAssociationInverseSide($fieldName)
1904
    {
1905
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
1906
    }
1907
1908
    /**
1909
     * {@inheritDoc}
1910
     */
1911
    public function getAssociationMappedByTargetField($fieldName)
1912
    {
1913
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
1914
    }
1915
}
1916