Completed
Push — master ( a6d38e...dca780 )
by Maciej
8s
created

ClassMetadataInfo::getIndexes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB\Mapping;
21
22
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
23
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
24
use Doctrine\ODM\MongoDB\LockException;
25
use Doctrine\ODM\MongoDB\Mapping\MappingException;
26
use Doctrine\ODM\MongoDB\Proxy\Proxy;
27
use Doctrine\ODM\MongoDB\Types\Type;
28
use InvalidArgumentException;
29
30
/**
31
 * A <tt>ClassMetadata</tt> instance holds all the object-document mapping metadata
32
 * of a document and it's references.
33
 *
34
 * Once populated, ClassMetadata instances are usually cached in a serialized form.
35
 *
36
 * <b>IMPORTANT NOTE:</b>
37
 *
38
 * The fields of this class are only public for 2 reasons:
39
 * 1) To allow fast READ access.
40
 * 2) To drastically reduce the size of a serialized instance (private/protected members
41
 *    get the whole class name, namespace inclusive, prepended to every property in
42
 *    the serialized representation).
43
 *
44
 * @since       1.0
45
 */
46
class ClassMetadataInfo implements \Doctrine\Common\Persistence\Mapping\ClassMetadata
47
{
48
    /* The Id generator types. */
49
    /**
50
     * AUTO means Doctrine will automatically create a new \MongoId instance for us.
51
     */
52
    const GENERATOR_TYPE_AUTO = 1;
53
54
    /**
55
     * INCREMENT means a separate collection is used for maintaining and incrementing id generation.
56
     * Offers full portability.
57
     */
58
    const GENERATOR_TYPE_INCREMENT = 2;
59
60
    /**
61
     * UUID means Doctrine will generate a uuid for us.
62
     */
63
    const GENERATOR_TYPE_UUID = 3;
64
65
    /**
66
     * ALNUM means Doctrine will generate Alpha-numeric string identifiers, using the INCREMENT
67
     * generator to ensure identifier uniqueness
68
     */
69
    const GENERATOR_TYPE_ALNUM = 4;
70
71
    /**
72
     * CUSTOM means Doctrine expect a class parameter. It will then try to initiate that class
73
     * and pass other options to the generator. It will throw an Exception if the class
74
     * does not exist or if an option was passed for that there is not setter in the new
75
     * generator class.
76
     *
77
     * The class  will have to be a subtype of AbstractIdGenerator.
78
     */
79
    const GENERATOR_TYPE_CUSTOM = 5;
80
81
    /**
82
     * NONE means Doctrine will not generate any id for us and you are responsible for manually
83
     * assigning an id.
84
     */
85
    const GENERATOR_TYPE_NONE = 6;
86
87
    /**
88
     * Default discriminator field name.
89
     *
90
     * This is used for associations value for associations where a that do not define a "targetDocument" or
91
     * "discriminatorField" option in their mapping.
92
     */
93
    const DEFAULT_DISCRIMINATOR_FIELD = '_doctrine_class_name';
94
95
    const REFERENCE_ONE = 1;
96
    const REFERENCE_MANY = 2;
97
    const EMBED_ONE = 3;
98
    const EMBED_MANY = 4;
99
    const MANY = 'many';
100
    const ONE = 'one';
101
102
    /* The inheritance mapping types */
103
    /**
104
     * NONE means the class does not participate in an inheritance hierarchy
105
     * and therefore does not need an inheritance mapping type.
106
     */
107
    const INHERITANCE_TYPE_NONE = 1;
108
109
    /**
110
     * SINGLE_COLLECTION means the class will be persisted according to the rules of
111
     * <tt>Single Collection Inheritance</tt>.
112
     */
113
    const INHERITANCE_TYPE_SINGLE_COLLECTION = 2;
114
115
    /**
116
     * COLLECTION_PER_CLASS means the class will be persisted according to the rules
117
     * of <tt>Concrete Collection Inheritance</tt>.
118
     */
119
    const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3;
120
121
    /**
122
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
123
     * by doing a property-by-property comparison with the original data. This will
124
     * be done for all entities that are in MANAGED state at commit-time.
125
     *
126
     * This is the default change tracking policy.
127
     */
128
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
129
130
    /**
131
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
132
     * by doing a property-by-property comparison with the original data. This will
133
     * be done only for entities that were explicitly saved (through persist() or a cascade).
134
     */
135
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
136
137
    /**
138
     * NOTIFY means that Doctrine relies on the entities sending out notifications
139
     * when their properties change. Such entity classes must implement
140
     * the <tt>NotifyPropertyChanged</tt> interface.
141
     */
142
    const CHANGETRACKING_NOTIFY = 3;
143
144
    /**
145
     * SET means that fields will be written to the database using a $set operator
146
     */
147
    const STORAGE_STRATEGY_SET = 'set';
148
149
    /**
150
     * INCREMENT means that fields will be written to the database by calculating
151
     * the difference and using the $inc operator
152
     */
153
    const STORAGE_STRATEGY_INCREMENT = 'increment';
154
155
    const STORAGE_STRATEGY_PUSH_ALL = 'pushAll';
156
    const STORAGE_STRATEGY_ADD_TO_SET = 'addToSet';
157
    const STORAGE_STRATEGY_ATOMIC_SET = 'atomicSet';
158
    const STORAGE_STRATEGY_ATOMIC_SET_ARRAY = 'atomicSetArray';
159
    const STORAGE_STRATEGY_SET_ARRAY = 'setArray';
160
161
    /**
162
     * READ-ONLY: The name of the mongo database the document is mapped to.
163
     */
164
    public $db;
165
166
    /**
167
     * READ-ONLY: The name of the mongo collection the document is mapped to.
168
     */
169
    public $collection;
170
171
    /**
172
     * READ-ONLY: If the collection should be a fixed size.
173
     */
174
    public $collectionCapped;
175
176
    /**
177
     * READ-ONLY: If the collection is fixed size, its size in bytes.
178
     */
179
    public $collectionSize;
180
181
    /**
182
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
183
     */
184
    public $collectionMax;
185
186
    /**
187
     * READ-ONLY: The field name of the document identifier.
188
     */
189
    public $identifier;
190
191
    /**
192
     * READ-ONLY: The field that stores a file reference and indicates the
193
     * document is a file and should be stored on the MongoGridFS.
194
     */
195
    public $file;
196
197
    /**
198
     * READ-ONLY: The field that stores the calculated distance when performing geo spatial
199
     * queries.
200
     */
201
    public $distance;
202
203
    /**
204
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
205
     */
206
    public $slaveOkay;
207
208
    /**
209
     * READ-ONLY: The array of indexes for the document collection.
210
     */
211
    public $indexes = array();
212
213
    /**
214
     * READ-ONLY: Whether or not queries on this document should require indexes.
215
     */
216
    public $requireIndexes = false;
217
218
    /**
219
     * READ-ONLY: The name of the document class.
220
     */
221
    public $name;
222
223
    /**
224
     * READ-ONLY: The namespace the document class is contained in.
225
     *
226
     * @var string
227
     * @todo Not really needed. Usage could be localized.
228
     */
229
    public $namespace;
230
231
    /**
232
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
233
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
234
     * as {@link $documentName}.
235
     *
236
     * @var string
237
     */
238
    public $rootDocumentName;
239
240
    /**
241
     * The name of the custom repository class used for the document class.
242
     * (Optional).
243
     *
244
     * @var string
245
     */
246
    public $customRepositoryClassName;
247
248
    /**
249
     * READ-ONLY: The names of the parent classes (ancestors).
250
     *
251
     * @var array
252
     */
253
    public $parentClasses = array();
254
255
    /**
256
     * READ-ONLY: The names of all subclasses (descendants).
257
     *
258
     * @var array
259
     */
260
    public $subClasses = array();
261
262
    /**
263
     * The ReflectionProperty instances of the mapped class.
264
     *
265
     * @var \ReflectionProperty[]
266
     */
267
    public $reflFields = array();
268
269
    /**
270
     * READ-ONLY: The inheritance mapping type used by the class.
271
     *
272
     * @var integer
273
     */
274
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
275
276
    /**
277
     * READ-ONLY: The Id generator type used by the class.
278
     *
279
     * @var string
280
     */
281
    public $generatorType = self::GENERATOR_TYPE_AUTO;
282
283
    /**
284
     * READ-ONLY: The Id generator options.
285
     *
286
     * @var array
287
     */
288
    public $generatorOptions = array();
289
290
    /**
291
     * READ-ONLY: The ID generator used for generating IDs for this class.
292
     *
293
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
294
     */
295
    public $idGenerator;
296
297
    /**
298
     * READ-ONLY: The field mappings of the class.
299
     * Keys are field names and values are mapping definitions.
300
     *
301
     * The mapping definition array has the following values:
302
     *
303
     * - <b>fieldName</b> (string)
304
     * The name of the field in the Document.
305
     *
306
     * - <b>id</b> (boolean, optional)
307
     * Marks the field as the primary key of the document. Multiple fields of an
308
     * document can have the id attribute, forming a composite key.
309
     *
310
     * @var array
311
     */
312
    public $fieldMappings = array();
313
314
    /**
315
     * READ-ONLY: The association mappings of the class.
316
     * Keys are field names and values are mapping definitions.
317
     *
318
     * @var array
319
     */
320
    public $associationMappings = array();
321
322
    /**
323
     * READ-ONLY: Array of fields to also load with a given method.
324
     *
325
     * @var array
326
     */
327
    public $alsoLoadMethods = array();
328
329
    /**
330
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
331
     *
332
     * @var array
333
     */
334
    public $lifecycleCallbacks = array();
335
336
    /**
337
     * READ-ONLY: The discriminator value of this class.
338
     *
339
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
340
     * where a discriminator field is used.</b>
341
     *
342
     * @var mixed
343
     * @see discriminatorField
344
     */
345
    public $discriminatorValue;
346
347
    /**
348
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
349
     *
350
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
351
     * where a discriminator field is used.</b>
352
     *
353
     * @var mixed
354
     * @see discriminatorField
355
     */
356
    public $discriminatorMap = array();
357
358
    /**
359
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
360
     * inheritance mapping.
361
     *
362
     * @var string
363
     */
364
    public $discriminatorField;
365
366
    /**
367
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
368
     *
369
     * @var string
370
     * @see discriminatorField
371
     */
372
    public $defaultDiscriminatorValue;
373
374
    /**
375
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
376
     *
377
     * @var boolean
378
     */
379
    public $isMappedSuperclass = false;
380
381
    /**
382
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
383
     *
384
     * @var boolean
385
     */
386
    public $isEmbeddedDocument = false;
387
388
    /**
389
     * READ-ONLY: The policy used for change-tracking on entities of this class.
390
     *
391
     * @var integer
392
     */
393
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
394
395
    /**
396
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
397
     * with optimistic locking.
398
     *
399
     * @var boolean $isVersioned
400
     */
401
    public $isVersioned;
402
403
    /**
404
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
405
     *
406
     * @var mixed $versionField
407
     */
408
    public $versionField;
409
410
    /**
411
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
412
     * locking.
413
     *
414
     * @var boolean $isLockable
415
     */
416
    public $isLockable;
417
418
    /**
419
     * READ-ONLY: The name of the field which is used for locking a document.
420
     *
421
     * @var mixed $lockField
422
     */
423
    public $lockField;
424
425
    /**
426
     * The ReflectionClass instance of the mapped class.
427
     *
428
     * @var \ReflectionClass
429
     */
430
    public $reflClass;
431
432
    /**
433
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
434
     * metadata of the class with the given name.
435
     *
436
     * @param string $documentName The name of the document class the new instance is used for.
437
     */
438 860
    public function __construct($documentName)
439
    {
440 860
        $this->name = $documentName;
441 860
        $this->rootDocumentName = $documentName;
442 860
    }
443
444
    /**
445
     * {@inheritDoc}
446
     */
447 810
    public function getReflectionClass()
448
    {
449 810
        if ( ! $this->reflClass) {
450 2
            $this->reflClass = new \ReflectionClass($this->name);
451 2
        }
452
453 810
        return $this->reflClass;
454
    }
455
456
    /**
457
     * {@inheritDoc}
458
     */
459 299
    public function isIdentifier($fieldName)
460
    {
461 299
        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 90
    public function getIdentifierFieldNames()
493
    {
494 90
        return array($this->identifier);
495
    }
496
497
    /**
498
     * {@inheritDoc}
499
     */
500 518
    public function hasField($fieldName)
501
    {
502 518
        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 810
    public function isInheritedField($fieldName)
523
    {
524 810
        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 595
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
556
    {
557 595
        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 594
        if (empty($this->lifecycleCallbacks[$event])) {
562 581
            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 802
    public function setRequireIndexes($requireIndexes)
797
    {
798 802
        $this->requireIndexes = $requireIndexes;
799 802
    }
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 570
    public function isChangeTrackingDeferredImplicit()
847
    {
848 570
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
849
    }
850
851
    /**
852
     * Whether the change tracking policy of this class is "notify".
853
     *
854
     * @return boolean
855
     */
856 328
    public function isChangeTrackingNotify()
857
    {
858 328
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
859
    }
860
861
    /**
862
     * Gets the ReflectionProperties of the mapped class.
863
     *
864
     * @return array An array of ReflectionProperty instances.
865
     */
866 90
    public function getReflectionProperties()
867
    {
868 90
        return $this->reflFields;
869
    }
870
871
    /**
872
     * Gets a ReflectionProperty for a specific field of the mapped class.
873
     *
874
     * @param string $name
875
     *
876
     * @return \ReflectionProperty
877
     */
878
    public function getReflectionProperty($name)
879
    {
880
        return $this->reflFields[$name];
881
    }
882
883
    /**
884
     * {@inheritDoc}
885
     */
886 807
    public function getName()
887
    {
888 807
        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 748
    public function getDatabase()
907
    {
908 748
        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 752
    public function getCollection()
927
    {
928 752
        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 840
    public function setCollection($name)
939
    {
940 840
        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 840
            $this->collection = $name;
950
        }
951 840
    }
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 701
    public function isFile()
1029
    {
1030 701
        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 846
    public function mapField(array $mapping)
1083
    {
1084 846
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1085 8
            $mapping['fieldName'] = $mapping['name'];
1086 8
        }
1087 846
        if ( ! isset($mapping['fieldName'])) {
1088
            throw MappingException::missingFieldName($this->name);
1089
        }
1090 846
        if ( ! isset($mapping['name'])) {
1091 838
            $mapping['name'] = $mapping['fieldName'];
1092 838
        }
1093 846
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1094 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1095
        }
1096 845
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1097
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1098 53
        }
1099 845
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1100 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1101
        }
1102 844
        if (isset($mapping['targetDocument']) && strpos($mapping['targetDocument'], '\\') === false && strlen($this->namespace)) {
1103 540
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1104 540
        }
1105
1106 844
        if (isset($mapping['discriminatorMap'])) {
1107 103
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1108 103
                if (strpos($class, '\\') === false && strlen($this->namespace)) {
1109 68
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1110 68
                }
1111 103
            }
1112 103
        }
1113
1114 844
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1115 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1116
        }
1117
1118 843
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1119
1120 843
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1121 565
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1122 565
        }
1123
1124 843
        if (isset($mapping['embedded'])) {
1125 531
            unset($mapping['cascade']);
1126 843
        } elseif (isset($mapping['cascade'])) {
1127 371
            $mapping['cascade'] = $cascades;
1128 371
        }
1129
1130 843
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1131 843
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1132 843
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1133 843
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1134 843
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1135
        
1136 843
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1137 56
            $mapping['file'] = true;
1138 56
        }
1139 843
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1140 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1141 1
        }
1142 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...
1143 56
            $this->file = $mapping['fieldName'];
1144 56
            $mapping['name'] = 'file';
1145 56
        }
1146 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...
1147 7
            $this->distance = $mapping['fieldName'];
1148 7
        }
1149 843
        if (isset($mapping['id']) && $mapping['id'] === true) {
1150 823
            $mapping['name'] = '_id';
1151 823
            $this->identifier = $mapping['fieldName'];
1152 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...
1153 808
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1154 808
            }
1155 823
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1156 823
            switch ($this->generatorType) {
1157 823
                case self::GENERATOR_TYPE_AUTO:
1158 756
                    $mapping['type'] = 'id';
1159 756
                    break;
1160 149
                default:
1161 149
                    if ( ! empty($this->generatorOptions['type'])) {
1162 52
                        $mapping['type'] = $this->generatorOptions['type'];
1163 149
                    } elseif (empty($mapping['type'])) {
1164 74
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1165 74
                    }
1166 823
            }
1167 823
            unset($this->generatorOptions['type']);
1168 823
        }
1169
1170 843
        if ( ! isset($mapping['nullable'])) {
1171 40
            $mapping['nullable'] = false;
1172 40
        }
1173
1174 843
        if (isset($mapping['reference']) && ! empty($mapping['simple']) && ! isset($mapping['targetDocument'])) {
1175 1
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1176
        }
1177
1178 842
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1179 842
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1180 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1181
        }
1182
        
1183 838
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1184 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1185
        }
1186
1187 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...
1188 460
            $mapping['association'] = self::REFERENCE_ONE;
1189 460
        }
1190 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...
1191 403
            $mapping['association'] = self::REFERENCE_MANY;
1192 403
        }
1193 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...
1194 411
            $mapping['association'] = self::EMBED_ONE;
1195 411
        }
1196 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...
1197 462
            $mapping['association'] = self::EMBED_MANY;
1198 462
        }
1199
1200 837
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1201 107
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1202 107
        }
1203
1204
        /*
1205
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1206
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1207
        }
1208
        */
1209 837
        if (isset($mapping['version'])) {
1210 93
            $mapping['notSaved'] = true;
1211 93
            $this->setVersionMapping($mapping);
1212 92
        }
1213 837
        if (isset($mapping['lock'])) {
1214 26
            $mapping['notSaved'] = true;
1215 26
            $this->setLockMapping($mapping);
1216 25
        }
1217 837
        $mapping['isOwningSide'] = true;
1218 837
        $mapping['isInverseSide'] = false;
1219 837
        if (isset($mapping['reference'])) {
1220 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...
1221 220
                $mapping['isOwningSide'] = true;
1222 220
                $mapping['isInverseSide'] = false;
1223 220
            }
1224 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...
1225 259
                $mapping['isInverseSide'] = true;
1226 259
                $mapping['isOwningSide'] = false;
1227 259
            }
1228 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...
1229 200
                $mapping['isInverseSide'] = true;
1230 200
                $mapping['isOwningSide'] = false;
1231 200
            }
1232 517
            if (!isset($mapping['orphanRemoval'])) {
1233 497
                $mapping['orphanRemoval'] = false;
1234 497
            }
1235 517
        }
1236
1237 837
        $this->applyStorageStrategy($mapping);
1238
1239 836
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1240 836
        if (isset($mapping['association'])) {
1241 656
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1242 656
        }
1243
1244 836
        return $mapping;
1245
    }
1246
1247
    /**
1248
     * Validates the storage strategy of a mapping for consistency
1249
     * @param array $mapping
1250
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1251
     */
1252 837
    private function applyStorageStrategy(array &$mapping)
1253
    {
1254 837
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1255 825
            return;
1256
        }
1257
1258 805
        switch (true) {
1259 805
            case $mapping['type'] == 'int':
1260 805
            case $mapping['type'] == 'float':
1261 805
            case $mapping['type'] == 'increment':
1262 305
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1263 305
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1264 305
                break;
1265
1266 804
            case $mapping['type'] == 'many':
1267 550
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1268
                $allowedStrategies = [
1269 550
                    self::STORAGE_STRATEGY_PUSH_ALL,
1270 550
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1271 550
                    self::STORAGE_STRATEGY_SET,
1272 550
                    self::STORAGE_STRATEGY_SET_ARRAY,
1273 550
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1274 550
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1275 550
                ];
1276 550
                break;
1277
1278 800
            default:
1279 800
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1280 800
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1281 800
        }
1282
1283 805
        if (! isset($mapping['strategy'])) {
1284 801
            $mapping['strategy'] = $defaultStrategy;
1285 801
        }
1286
1287 805
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1288
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1289
        }
1290
1291 805
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1292 805
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1293 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1294
        }
1295 804
    }
1296
1297
    /**
1298
     * Map a MongoGridFSFile.
1299
     *
1300
     * @param array $mapping The mapping information.
1301
     */
1302
    public function mapFile(array $mapping)
1303
    {
1304
        $mapping['file'] = true;
1305
        $mapping['type'] = 'file';
1306
        $this->mapField($mapping);
1307
    }
1308
1309
    /**
1310
     * Map a single embedded document.
1311
     *
1312
     * @param array $mapping The mapping information.
1313
     */
1314 6
    public function mapOneEmbedded(array $mapping)
1315
    {
1316 6
        $mapping['embedded'] = true;
1317 6
        $mapping['type'] = 'one';
1318 6
        $this->mapField($mapping);
1319 5
    }
1320
1321
    /**
1322
     * Map a collection of embedded documents.
1323
     *
1324
     * @param array $mapping The mapping information.
1325
     */
1326 3
    public function mapManyEmbedded(array $mapping)
1327
    {
1328 3
        $mapping['embedded'] = true;
1329 3
        $mapping['type'] = 'many';
1330 3
        $this->mapField($mapping);
1331 3
    }
1332
1333
    /**
1334
     * Map a single document reference.
1335
     *
1336
     * @param array $mapping The mapping information.
1337
     */
1338 8
    public function mapOneReference(array $mapping)
1339
    {
1340 8
        $mapping['reference'] = true;
1341 8
        $mapping['type'] = 'one';
1342 8
        $this->mapField($mapping);
1343 8
    }
1344
1345
    /**
1346
     * Map a collection of document references.
1347
     *
1348
     * @param array $mapping The mapping information.
1349
     */
1350 8
    public function mapManyReference(array $mapping)
1351
    {
1352 8
        $mapping['reference'] = true;
1353 8
        $mapping['type'] = 'many';
1354 8
        $this->mapField($mapping);
1355 8
    }
1356
1357
    /**
1358
     * INTERNAL:
1359
     * Adds a field mapping without completing/validating it.
1360
     * This is mainly used to add inherited field mappings to derived classes.
1361
     *
1362
     * @param array $fieldMapping
1363
     */
1364 115
    public function addInheritedFieldMapping(array $fieldMapping)
1365
    {
1366 115
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1367
1368 115
        if (isset($fieldMapping['association'])) {
1369 76
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1370 76
        }
1371 115
    }
1372
1373
    /**
1374
     * INTERNAL:
1375
     * Adds an association mapping without completing/validating it.
1376
     * This is mainly used to add inherited association mappings to derived classes.
1377
     *
1378
     * @param array $mapping
1379
     *
1380
     * @return void
1381
     *
1382
     * @throws MappingException
1383
     */
1384 77
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1385
    {
1386 77
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1387 77
    }
1388
1389
    /**
1390
     * Checks whether the class has a mapped association with the given field name.
1391
     *
1392
     * @param string $fieldName
1393
     * @return boolean
1394
     */
1395 7
    public function hasReference($fieldName)
1396
    {
1397 7
        return isset($this->fieldMappings[$fieldName]['reference']);
1398
    }
1399
1400
    /**
1401
     * Checks whether the class has a mapped embed with the given field name.
1402
     *
1403
     * @param string $fieldName
1404
     * @return boolean
1405
     */
1406 5
    public function hasEmbed($fieldName)
1407
    {
1408 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1409
    }
1410
1411
    /**
1412
     * {@inheritDoc}
1413
     *
1414
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1415
     */
1416 7
    public function hasAssociation($fieldName)
1417
    {
1418 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1419
    }
1420
1421
    /**
1422
     * {@inheritDoc}
1423
     *
1424
     * Checks whether the class has a mapped reference or embed for the specified field and
1425
     * is a single valued association.
1426
     */
1427
    public function isSingleValuedAssociation($fieldName)
1428
    {
1429
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($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 collection valued association.
1437
     */
1438
    public function isCollectionValuedAssociation($fieldName)
1439
    {
1440
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1441
    }
1442
1443
    /**
1444
     * Checks whether the class has a mapped association for the specified field
1445
     * and if yes, checks whether it is a single-valued association (to-one).
1446
     *
1447
     * @param string $fieldName
1448
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1449
     */
1450
    public function isSingleValuedReference($fieldName)
1451
    {
1452
        return isset($this->fieldMappings[$fieldName]['association']) &&
1453
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1454
    }
1455
1456
    /**
1457
     * Checks whether the class has a mapped association for the specified field
1458
     * and if yes, checks whether it is a collection-valued association (to-many).
1459
     *
1460
     * @param string $fieldName
1461
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1462
     */
1463
    public function isCollectionValuedReference($fieldName)
1464
    {
1465
        return isset($this->fieldMappings[$fieldName]['association']) &&
1466
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1467
    }
1468
1469
    /**
1470
     * Checks whether the class has a mapped embedded document for the specified field
1471
     * and if yes, checks whether it is a single-valued association (to-one).
1472
     *
1473
     * @param string $fieldName
1474
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1475
     */
1476
    public function isSingleValuedEmbed($fieldName)
1477
    {
1478
        return isset($this->fieldMappings[$fieldName]['association']) &&
1479
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1480
    }
1481
1482
    /**
1483
     * Checks whether the class has a mapped embedded document for the specified field
1484
     * and if yes, checks whether it is a collection-valued association (to-many).
1485
     *
1486
     * @param string $fieldName
1487
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1488
     */
1489
    public function isCollectionValuedEmbed($fieldName)
1490
    {
1491
        return isset($this->fieldMappings[$fieldName]['association']) &&
1492
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1493
    }
1494
1495
    /**
1496
     * Sets the ID generator used to generate IDs for instances of this class.
1497
     *
1498
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1499
     */
1500 750
    public function setIdGenerator($generator)
1501
    {
1502 750
        $this->idGenerator = $generator;
1503 750
    }
1504
1505
    /**
1506
     * Casts the identifier to its portable PHP type.
1507
     *
1508
     * @param mixed $id
1509
     * @return mixed $id
1510
     */
1511 587
    public function getPHPIdentifierValue($id)
1512
    {
1513 587
        $idType = $this->fieldMappings[$this->identifier]['type'];
1514 587
        return Type::getType($idType)->convertToPHPValue($id);
1515
    }
1516
1517
    /**
1518
     * Casts the identifier to its database type.
1519
     *
1520
     * @param mixed $id
1521
     * @return mixed $id
1522
     */
1523 649
    public function getDatabaseIdentifierValue($id)
1524
    {
1525 649
        $idType = $this->fieldMappings[$this->identifier]['type'];
1526 649
        return Type::getType($idType)->convertToDatabaseValue($id);
1527
    }
1528
1529
    /**
1530
     * Sets the document identifier of a document.
1531
     *
1532
     * The value will be converted to a PHP type before being set.
1533
     *
1534
     * @param object $document
1535
     * @param mixed $id
1536
     */
1537 520
    public function setIdentifierValue($document, $id)
1538
    {
1539 520
        $id = $this->getPHPIdentifierValue($id);
1540 520
        $this->reflFields[$this->identifier]->setValue($document, $id);
1541 520
    }
1542
1543
    /**
1544
     * Gets the document identifier as a PHP type.
1545
     *
1546
     * @param object $document
1547
     * @return mixed $id
1548
     */
1549 600
    public function getIdentifierValue($document)
1550
    {
1551 600
        return $this->reflFields[$this->identifier]->getValue($document);
1552
    }
1553
1554
    /**
1555
     * {@inheritDoc}
1556
     *
1557
     * Since MongoDB only allows exactly one identifier field this is a proxy
1558
     * to {@see getIdentifierValue()} and returns an array with the identifier
1559
     * field as a key.
1560
     */
1561
    public function getIdentifierValues($object)
1562
    {
1563
        return array($this->identifier => $this->getIdentifierValue($object));
1564
    }
1565
1566
    /**
1567
     * Get the document identifier object as a database type.
1568
     *
1569
     * @param object $document
1570
     *
1571
     * @return \MongoId $id The MongoID object.
1572
     */
1573 32
    public function getIdentifierObject($document)
1574
    {
1575 32
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1576
    }
1577
1578
    /**
1579
     * Sets the specified field to the specified value on the given document.
1580
     *
1581
     * @param object $document
1582
     * @param string $field
1583
     * @param mixed $value
1584
     */
1585 7
    public function setFieldValue($document, $field, $value)
1586
    {
1587 7
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1588
            //property changes to an uninitialized proxy will not be tracked or persisted,
1589
            //so the proxy needs to be loaded first.
1590 1
            $document->__load();
1591 1
        }
1592
        
1593 7
        $this->reflFields[$field]->setValue($document, $value);
1594 7
    }
1595
1596
    /**
1597
     * Gets the specified field's value off the given document.
1598
     *
1599
     * @param object $document
1600
     * @param string $field
1601
     *
1602
     * @return mixed
1603
     */
1604 25
    public function getFieldValue($document, $field)
1605
    {
1606 25
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1607 1
            $document->__load();
1608 1
        }
1609
        
1610 25
        return $this->reflFields[$field]->getValue($document);
1611
    }
1612
1613
    /**
1614
     * Gets the mapping of a field.
1615
     *
1616
     * @param string $fieldName  The field name.
1617
     *
1618
     * @return array  The field mapping.
1619
     *
1620
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1621
     *
1622
     * @throws MappingException
1623
     */
1624 90
    public function getFieldMapping($fieldName)
1625
    {
1626 90
        if ( ! isset($this->fieldMappings[$fieldName])) {
1627 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1628
        }
1629 88
        return $this->fieldMappings[$fieldName];
1630
    }
1631
1632
    /**
1633
     * Gets mappings of fields holding embedded document(s).
1634
     *
1635
     * @return array of field mappings
1636
     */
1637 562
    public function getEmbeddedFieldsMappings()
1638
    {
1639 562
        return array_filter(
1640 562
            $this->associationMappings,
1641
            function($assoc) { return ! empty($assoc['embedded']); }
1642 562
        );
1643
    }
1644
1645
    /**
1646
     * Check if the field is not null.
1647
     *
1648
     * @param string $fieldName  The field name
1649
     *
1650
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1651
     */
1652 1
    public function isNullable($fieldName)
1653
    {
1654 1
        $mapping = $this->getFieldMapping($fieldName);
1655 1
        if ($mapping !== false) {
1656 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1657
        }
1658
        return false;
1659
    }
1660
1661
    /**
1662
     * Checks whether the document has a discriminator field and value configured.
1663
     *
1664
     * @return boolean
1665
     */
1666 481
    public function hasDiscriminator()
1667
    {
1668 481
        return isset($this->discriminatorField, $this->discriminatorValue);
1669
    }
1670
1671
    /**
1672
     * Sets the type of Id generator to use for the mapped class.
1673
     */
1674 342
    public function setIdGeneratorType($generatorType)
1675
    {
1676 342
        $this->generatorType = $generatorType;
1677 342
    }
1678
1679
    /**
1680
     * Sets the Id generator options.
1681
     */
1682
    public function setIdGeneratorOptions($generatorOptions)
1683
    {
1684
        $this->generatorOptions = $generatorOptions;
1685
    }
1686
1687
    /**
1688
     * @return boolean
1689
     */
1690 568
    public function isInheritanceTypeNone()
1691
    {
1692 568
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1693
    }
1694
1695
    /**
1696
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1697
     *
1698
     * @return boolean
1699
     */
1700 336
    public function isInheritanceTypeSingleCollection()
1701
    {
1702 336
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1703
    }
1704
1705
    /**
1706
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1707
     *
1708
     * @return boolean
1709
     */
1710
    public function isInheritanceTypeCollectionPerClass()
1711
    {
1712
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1713
    }
1714
1715
    /**
1716
     * Sets the mapped subclasses of this class.
1717
     *
1718
     * @param string[] $subclasses The names of all mapped subclasses.
1719
     */
1720 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...
1721
    {
1722 2
        foreach ($subclasses as $subclass) {
1723 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1724 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1725 1
            } else {
1726 1
                $this->subClasses[] = $subclass;
1727
            }
1728 2
        }
1729 2
    }
1730
1731
    /**
1732
     * Sets the parent class names.
1733
     * Assumes that the class names in the passed array are in the order:
1734
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1735
     *
1736
     * @param string[] $classNames
1737
     */
1738 805
    public function setParentClasses(array $classNames)
1739
    {
1740 805
        $this->parentClasses = $classNames;
1741
1742 805
        if (count($classNames) > 0) {
1743 101
            $this->rootDocumentName = array_pop($classNames);
1744 101
        }
1745 805
    }
1746
1747
    /**
1748
     * Checks whether the class will generate a new \MongoId instance for us.
1749
     *
1750
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1751
     */
1752
    public function isIdGeneratorAuto()
1753
    {
1754
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1755
    }
1756
1757
    /**
1758
     * Checks whether the class will use a collection to generate incremented identifiers.
1759
     *
1760
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1761
     */
1762
    public function isIdGeneratorIncrement()
1763
    {
1764
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1765
    }
1766
1767
    /**
1768
     * Checks whether the class will generate a uuid id.
1769
     *
1770
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1771
     */
1772
    public function isIdGeneratorUuid()
1773
    {
1774
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1775
    }
1776
1777
    /**
1778
     * Checks whether the class uses no id generator.
1779
     *
1780
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1781
     */
1782
    public function isIdGeneratorNone()
1783
    {
1784
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1785
    }
1786
1787
    /**
1788
     * Sets the version field mapping used for versioning. Sets the default
1789
     * value to use depending on the column type.
1790
     *
1791
     * @param array $mapping   The version field mapping array
1792
     * 
1793
     * @throws LockException
1794
     */
1795 93
    public function setVersionMapping(array &$mapping)
1796
    {
1797 93
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
1798 1
            throw LockException::invalidVersionFieldType($mapping['type']);
1799
        }
1800
1801 92
        $this->isVersioned  = true;
1802 92
        $this->versionField = $mapping['fieldName'];
1803 92
    }
1804
1805
    /**
1806
     * Sets whether this class is to be versioned for optimistic locking.
1807
     *
1808
     * @param boolean $bool
1809
     */
1810 336
    public function setVersioned($bool)
1811
    {
1812 336
        $this->isVersioned = $bool;
1813 336
    }
1814
1815
    /**
1816
     * Sets the name of the field that is to be used for versioning if this class is
1817
     * versioned for optimistic locking.
1818
     *
1819
     * @param string $versionField
1820
     */
1821 336
    public function setVersionField($versionField)
1822
    {
1823 336
        $this->versionField = $versionField;
1824 336
    }
1825
1826
    /**
1827
     * Sets the version field mapping used for versioning. Sets the default
1828
     * value to use depending on the column type.
1829
     *
1830
     * @param array $mapping   The version field mapping array
1831
     *
1832
     * @throws \Doctrine\ODM\MongoDB\LockException
1833
     */
1834 26
    public function setLockMapping(array &$mapping)
1835
    {
1836 26
        if ($mapping['type'] !== 'int') {
1837 1
            throw LockException::invalidLockFieldType($mapping['type']);
1838
        }
1839
1840 25
        $this->isLockable = true;
1841 25
        $this->lockField = $mapping['fieldName'];
1842 25
    }
1843
1844
    /**
1845
     * Sets whether this class is to allow pessimistic locking.
1846
     *
1847
     * @param boolean $bool
1848
     */
1849
    public function setLockable($bool)
1850
    {
1851
        $this->isLockable = $bool;
1852
    }
1853
1854
    /**
1855
     * Sets the name of the field that is to be used for storing whether a document
1856
     * is currently locked or not.
1857
     *
1858
     * @param string $lockField
1859
     */
1860
    public function setLockField($lockField)
1861
    {
1862
        $this->lockField = $lockField;
1863
    }
1864
1865
    /**
1866
     * {@inheritDoc}
1867
     */
1868
    public function getFieldNames()
1869
    {
1870
        return array_keys($this->fieldMappings);
1871
    }
1872
1873
    /**
1874
     * {@inheritDoc}
1875
     */
1876
    public function getAssociationNames()
1877
    {
1878
        return array_keys($this->associationMappings);
1879
    }
1880
1881
    /**
1882
     * {@inheritDoc}
1883
     */
1884 22
    public function getTypeOfField($fieldName)
1885
    {
1886 22
        return isset($this->fieldMappings[$fieldName]) ?
1887 22
            $this->fieldMappings[$fieldName]['type'] : null;
1888
    }
1889
1890
    /**
1891
     * {@inheritDoc}
1892
     */
1893 6
    public function getAssociationTargetClass($assocName)
1894
    {
1895 6
        if ( ! isset($this->associationMappings[$assocName])) {
1896 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
1897
        }
1898
1899 3
        return $this->associationMappings[$assocName]['targetDocument'];
1900
    }
1901
1902
    /**
1903
     * {@inheritDoc}
1904
     */
1905
    public function isAssociationInverseSide($fieldName)
1906
    {
1907
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
1908
    }
1909
1910
    /**
1911
     * {@inheritDoc}
1912
     */
1913
    public function getAssociationMappedByTargetField($fieldName)
1914
    {
1915
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
1916
    }
1917
}
1918