Completed
Pull Request — master (#1219)
by Maciej
14:45
created

ClassMetadataInfo::mapManyReference()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

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