Completed
Pull Request — master (#1352)
by Andreas
12:33
created

ClassMetadataInfo::setDiscriminatorMap()   B

Complexity

Conditions 7
Paths 9

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7.0119
Metric Value
dl 0
loc 19
ccs 15
cts 16
cp 0.9375
rs 8.2222
cc 7
eloc 12
nc 9
nop 1
crap 7.0119
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
    /**
156
     * READ-ONLY: The name of the mongo database the document is mapped to.
157
     */
158
    public $db;
159
160
    /**
161
     * READ-ONLY: The name of the mongo collection the document is mapped to.
162
     */
163
    public $collection;
164
165
    /**
166
     * READ-ONLY: If the collection should be a fixed size.
167
     */
168
    public $collectionCapped;
169
170
    /**
171
     * READ-ONLY: If the collection is fixed size, its size in bytes.
172
     */
173
    public $collectionSize;
174
175
    /**
176
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
177
     */
178
    public $collectionMax;
179
180
    /**
181
     * READ-ONLY: The field name of the document identifier.
182
     */
183
    public $identifier;
184
185
    /**
186
     * READ-ONLY: The field that stores a file reference and indicates the
187
     * document is a file and should be stored on the MongoGridFS.
188
     */
189
    public $file;
190
191
    /**
192
     * READ-ONLY: The field that stores the calculated distance when performing geo spatial
193
     * queries.
194
     */
195
    public $distance;
196
197
    /**
198
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
199
     */
200
    public $slaveOkay;
201
202
    /**
203
     * READ-ONLY: The array of indexes for the document collection.
204
     */
205
    public $indexes = array();
206
207
    /**
208
     * READ-ONLY: Whether or not queries on this document should require indexes.
209
     */
210
    public $requireIndexes = false;
211
212
    /**
213
     * READ-ONLY: The name of the document class.
214
     */
215
    public $name;
216
217
    /**
218
     * READ-ONLY: The namespace the document class is contained in.
219
     *
220
     * @var string
221
     * @todo Not really needed. Usage could be localized.
222
     */
223
    public $namespace;
224
225
    /**
226
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
227
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
228
     * as {@link $documentName}.
229
     *
230
     * @var string
231
     */
232
    public $rootDocumentName;
233
234
    /**
235
     * The name of the custom repository class used for the document class.
236
     * (Optional).
237
     *
238
     * @var string
239
     */
240
    public $customRepositoryClassName;
241
242
    /**
243
     * READ-ONLY: The names of the parent classes (ancestors).
244
     *
245
     * @var array
246
     */
247
    public $parentClasses = array();
248
249
    /**
250
     * READ-ONLY: The names of all subclasses (descendants).
251
     *
252
     * @var array
253
     */
254
    public $subClasses = array();
255
256
    /**
257
     * The ReflectionProperty instances of the mapped class.
258
     *
259
     * @var \ReflectionProperty[]
260
     */
261
    public $reflFields = array();
262
263
    /**
264
     * READ-ONLY: The inheritance mapping type used by the class.
265
     *
266
     * @var integer
267
     */
268
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
269
270
    /**
271
     * READ-ONLY: The Id generator type used by the class.
272
     *
273
     * @var string
274
     */
275
    public $generatorType = self::GENERATOR_TYPE_AUTO;
276
277
    /**
278
     * READ-ONLY: The Id generator options.
279
     *
280
     * @var array
281
     */
282
    public $generatorOptions = array();
283
284
    /**
285
     * READ-ONLY: The ID generator used for generating IDs for this class.
286
     *
287
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
288
     */
289
    public $idGenerator;
290
291
    /**
292
     * READ-ONLY: The field mappings of the class.
293
     * Keys are field names and values are mapping definitions.
294
     *
295
     * The mapping definition array has the following values:
296
     *
297
     * - <b>fieldName</b> (string)
298
     * The name of the field in the Document.
299
     *
300
     * - <b>id</b> (boolean, optional)
301
     * Marks the field as the primary key of the document. Multiple fields of an
302
     * document can have the id attribute, forming a composite key.
303
     *
304
     * @var array
305
     */
306
    public $fieldMappings = array();
307
308
    /**
309
     * READ-ONLY: The association mappings of the class.
310
     * Keys are field names and values are mapping definitions.
311
     *
312
     * @var array
313
     */
314
    public $associationMappings = array();
315
316
    /**
317
     * READ-ONLY: Array of fields to also load with a given method.
318
     *
319
     * @var array
320
     */
321
    public $alsoLoadMethods = array();
322
323
    /**
324
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
325
     *
326
     * @var array
327
     */
328
    public $lifecycleCallbacks = array();
329
330
    /**
331
     * READ-ONLY: The discriminator value of this class.
332
     *
333
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
334
     * where a discriminator field is used.</b>
335
     *
336
     * @var mixed
337
     * @see discriminatorField
338
     */
339
    public $discriminatorValue;
340
341
    /**
342
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
343
     *
344
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
345
     * where a discriminator field is used.</b>
346
     *
347
     * @var mixed
348
     * @see discriminatorField
349
     */
350
    public $discriminatorMap = array();
351
352
    /**
353
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
354
     * inheritance mapping.
355
     *
356
     * @var string
357
     */
358
    public $discriminatorField;
359
360
    /**
361
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
362
     *
363
     * @var string
364
     * @see discriminatorField
365
     */
366
    public $defaultDiscriminatorValue;
367
368
    /**
369
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
370
     *
371
     * @var boolean
372
     */
373
    public $isMappedSuperclass = false;
374
375
    /**
376
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
377
     *
378
     * @var boolean
379
     */
380
    public $isEmbeddedDocument = false;
381
382
    /**
383
     * READ-ONLY: The policy used for change-tracking on entities of this class.
384
     *
385
     * @var integer
386
     */
387
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
388
389
    /**
390
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
391
     * with optimistic locking.
392
     *
393
     * @var boolean $isVersioned
394
     */
395
    public $isVersioned;
396
397
    /**
398
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
399
     *
400
     * @var mixed $versionField
401
     */
402
    public $versionField;
403
404
    /**
405
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
406
     * locking.
407
     *
408
     * @var boolean $isLockable
409
     */
410
    public $isLockable;
411
412
    /**
413
     * READ-ONLY: The name of the field which is used for locking a document.
414
     *
415
     * @var mixed $lockField
416
     */
417
    public $lockField;
418
419
    /**
420
     * The ReflectionClass instance of the mapped class.
421
     *
422
     * @var \ReflectionClass
423
     */
424
    public $reflClass;
425
426
    /**
427
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
428
     * metadata of the class with the given name.
429
     *
430
     * @param string $documentName The name of the document class the new instance is used for.
431
     */
432 852
    public function __construct($documentName)
433
    {
434 852
        $this->name = $documentName;
435 852
        $this->rootDocumentName = $documentName;
436 852
    }
437
438
    /**
439
     * {@inheritDoc}
440
     */
441 803
    public function getReflectionClass()
442
    {
443 803
        if ( ! $this->reflClass) {
444 2
            $this->reflClass = new \ReflectionClass($this->name);
445 2
        }
446
447 803
        return $this->reflClass;
448
    }
449
450
    /**
451
     * {@inheritDoc}
452
     */
453 292
    public function isIdentifier($fieldName)
454
    {
455 292
        return $this->identifier === $fieldName;
456
    }
457
458
    /**
459
     * INTERNAL:
460
     * Sets the mapped identifier field of this class.
461
     *
462
     * @param string $identifier
463
     */
464 331
    public function setIdentifier($identifier)
465
    {
466 331
        $this->identifier = $identifier;
467 331
    }
468
469
    /**
470
     * {@inheritDoc}
471
     *
472
     * Since MongoDB only allows exactly one identifier field
473
     * this will always return an array with only one value
474
     */
475 25
    public function getIdentifier()
476
    {
477 25
        return array($this->identifier);
478
    }
479
480
    /**
481
     * {@inheritDoc}
482
     *
483
     * Since MongoDB only allows exactly one identifier field
484
     * this will always return an array with only one value
485
     */
486 89
    public function getIdentifierFieldNames()
487
    {
488 89
        return array($this->identifier);
489
    }
490
491
    /**
492
     * {@inheritDoc}
493
     */
494 511
    public function hasField($fieldName)
495
    {
496 511
        return isset($this->fieldMappings[$fieldName]);
497
    }
498
499
    /**
500
     * Sets the inheritance type used by the class and it's subclasses.
501
     *
502
     * @param integer $type
503
     */
504 342
    public function setInheritanceType($type)
505
    {
506 342
        $this->inheritanceType = $type;
507 342
    }
508
509
    /**
510
     * Checks whether a mapped field is inherited from an entity superclass.
511
     *
512
     * @param  string $fieldName
513
     *
514
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
515
     */
516 803
    public function isInheritedField($fieldName)
517
    {
518 803
        return isset($this->fieldMappings[$fieldName]['inherited']);
519
    }
520
521
    /**
522
     * Registers a custom repository class for the document class.
523
     *
524
     * @param string $repositoryClassName The class name of the custom repository.
525
     */
526 286 View Code Duplication
    public function setCustomRepositoryClass($repositoryClassName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
527
    {
528 286
        if ($this->isEmbeddedDocument) {
529
            return;
530
        }
531
        
532 286
        if ($repositoryClassName && strpos($repositoryClassName, '\\') === false && strlen($this->namespace)) {
533 3
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
534 3
        }
535
536 286
        $this->customRepositoryClassName = $repositoryClassName;
537 286
    }
538
539
    /**
540
     * Dispatches the lifecycle event of the given document by invoking all
541
     * registered callbacks.
542
     *
543
     * @param string $event     Lifecycle event
544
     * @param object $document  Document on which the event occurred
545
     * @param array  $arguments Arguments to pass to all callbacks
546
     * @throws \InvalidArgumentException if document class is not this class or
547
     *                                   a Proxy of this class
548
     */
549 588
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
550
    {
551 588
        if ( ! $document instanceof $this->name) {
552 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
553
        }
554
555 587
        if (empty($this->lifecycleCallbacks[$event])) {
556 574
            return;
557
        }
558
559 177
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
560 177
            if ($arguments !== null) {
561 176
                call_user_func_array(array($document, $callback), $arguments);
562 176
            } else {
563 2
                $document->$callback();
564
            }
565 177
        }
566 177
    }
567
568
    /**
569
     * Checks whether the class has callbacks registered for a lifecycle event.
570
     *
571
     * @param string $event Lifecycle event
572
     *
573
     * @return boolean
574
     */
575
    public function hasLifecycleCallbacks($event)
576
    {
577
        return ! empty($this->lifecycleCallbacks[$event]);
578
    }
579
580
    /**
581
     * Gets the registered lifecycle callbacks for an event.
582
     *
583
     * @param string $event
584
     * @return array
585
     */
586
    public function getLifecycleCallbacks($event)
587
    {
588
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
589
    }
590
591
    /**
592
     * Adds a lifecycle callback for documents of this class.
593
     *
594
     * If the callback is already registered, this is a NOOP.
595
     *
596
     * @param string $callback
597
     * @param string $event
598
     */
599 267
    public function addLifecycleCallback($callback, $event)
600
    {
601 267
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
602 1
            return;
603
        }
604
605 267
        $this->lifecycleCallbacks[$event][] = $callback;
606 267
    }
607
608
    /**
609
     * Sets the lifecycle callbacks for documents of this class.
610
     *
611
     * Any previously registered callbacks are overwritten.
612
     *
613
     * @param array $callbacks
614
     */
615 330
    public function setLifecycleCallbacks(array $callbacks)
616
    {
617 330
        $this->lifecycleCallbacks = $callbacks;
618 330
    }
619
620
    /**
621
     * Registers a method for loading document data before field hydration.
622
     *
623
     * Note: A method may be registered multiple times for different fields.
624
     * it will be invoked only once for the first field found.
625
     *
626
     * @param string       $method Method name
627
     * @param array|string $fields Database field name(s)
628
     */
629 15
    public function registerAlsoLoadMethod($method, $fields)
630
    {
631 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
632 15
    }
633
634
    /**
635
     * Sets the AlsoLoad methods for documents of this class.
636
     *
637
     * Any previously registered methods are overwritten.
638
     *
639
     * @param array $methods
640
     */
641 330
    public function setAlsoLoadMethods(array $methods)
642
    {
643 330
        $this->alsoLoadMethods = $methods;
644 330
    }
645
646
    /**
647
     * Sets the discriminator field.
648
     *
649
     * The field name is the the unmapped database field. Discriminator values
650
     * are only used to discern the hydration class and are not mapped to class
651
     * properties.
652
     *
653
     * @param string $discriminatorField
654
     *
655
     * @throws MappingException If the discriminator field conflicts with the
656
     *                          "name" attribute of a mapped field.
657
     */
658 351
    public function setDiscriminatorField($discriminatorField)
659
    {
660 351
        if ($discriminatorField === null) {
661 291
            $this->discriminatorField = null;
662
663 291
            return;
664
        }
665
666
        // Handle array argument with name/fieldName keys for BC
667 115
        if (is_array($discriminatorField)) {
668
            if (isset($discriminatorField['name'])) {
669
                $discriminatorField = $discriminatorField['name'];
670
            } elseif (isset($discriminatorField['fieldName'])) {
671
                $discriminatorField = $discriminatorField['fieldName'];
672
            }
673
        }
674
675 115
        foreach ($this->fieldMappings as $fieldMapping) {
676 4
            if ($discriminatorField == $fieldMapping['name']) {
677 1
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
678
            }
679 114
        }
680
681 114
        $this->discriminatorField = $discriminatorField;
682 114
    }
683
684
    /**
685
     * Sets the discriminator values used by this class.
686
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
687
     *
688
     * @param array $map
689
     *
690
     * @throws MappingException
691
     */
692 347
    public function setDiscriminatorMap(array $map)
693
    {
694 347
        foreach ($map as $value => $className) {
695 113
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
696 81
                $className = $this->namespace . '\\' . $className;
697 81
            }
698 113
            $this->discriminatorMap[$value] = $className;
699 113
            if ($this->name == $className) {
700 105
                $this->discriminatorValue = $value;
701 105
            } else {
702 105
                if ( ! class_exists($className)) {
703
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
704
                }
705 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...
706 91
                    $this->subClasses[] = $className;
707 91
                }
708
            }
709 347
        }
710 347
    }
711
712
    /**
713
     * Sets the default discriminator value to be used for this class
714
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
715
     *
716
     * @param string $defaultDiscriminatorValue
717
     *
718
     * @throws MappingException
719
     */
720 336
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
721
    {
722 336
        if ($defaultDiscriminatorValue === null) {
723 330
            $this->defaultDiscriminatorValue = null;
724
725 330
            return;
726
        }
727
728 53
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
729
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
730
        }
731
732 53
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
733 53
    }
734
735
    /**
736
     * Sets the discriminator value for this class.
737
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
738
     * collection.
739
     *
740
     * @param string $value
741
     */
742
    public function setDiscriminatorValue($value)
743
    {
744
        $this->discriminatorMap[$value] = $this->name;
745
        $this->discriminatorValue = $value;
746
    }
747
748
    /**
749
     * Sets the slaveOkay option applied to collections for this class.
750
     *
751
     * @param boolean|null $slaveOkay
752
     */
753 3
    public function setSlaveOkay($slaveOkay)
754
    {
755 3
        $this->slaveOkay = $slaveOkay === null ? null : (boolean) $slaveOkay;
756 3
    }
757
758
    /**
759
     * Add a index for this Document.
760
     *
761
     * @param array $keys Array of keys for the index.
762
     * @param array $options Array of options for the index.
763
     */
764 219
    public function addIndex($keys, array $options = array())
765
    {
766 219
        $this->indexes[] = array(
767
            'keys' => array_map(function($value) {
768 219
                if ($value == 1 || $value == -1) {
769 55
                    return (int) $value;
770
                }
771 211
                if (is_string($value)) {
772 211
                    $lower = strtolower($value);
773 211
                    if ($lower === 'asc') {
774 204
                        return 1;
775 10
                    } elseif ($lower === 'desc') {
776 3
                        return -1;
777
                    }
778 7
                }
779 7
                return $value;
780 219
            }, $keys),
781
            'options' => $options
782 219
        );
783 219
    }
784
785
    /**
786
     * Set whether or not queries on this document should require indexes.
787
     *
788
     * @param bool $requireIndexes
789
     */
790 795
    public function setRequireIndexes($requireIndexes)
791
    {
792 795
        $this->requireIndexes = $requireIndexes;
793 795
    }
794
795
    /**
796
     * Returns the array of indexes for this Document.
797
     *
798
     * @return array $indexes The array of indexes.
799
     */
800 53
    public function getIndexes()
801
    {
802 53
        return $this->indexes;
803
    }
804
805
    /**
806
     * Checks whether this document has indexes or not.
807
     *
808
     * @return boolean
809
     */
810
    public function hasIndexes()
811
    {
812
        return $this->indexes ? true : false;
813
    }
814
815
    /**
816
     * Sets the change tracking policy used by this class.
817
     *
818
     * @param integer $policy
819
     */
820 334
    public function setChangeTrackingPolicy($policy)
821
    {
822 334
        $this->changeTrackingPolicy = $policy;
823 334
    }
824
825
    /**
826
     * Whether the change tracking policy of this class is "deferred explicit".
827
     *
828
     * @return boolean
829
     */
830 57
    public function isChangeTrackingDeferredExplicit()
831
    {
832 57
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
833
    }
834
835
    /**
836
     * Whether the change tracking policy of this class is "deferred implicit".
837
     *
838
     * @return boolean
839
     */
840 563
    public function isChangeTrackingDeferredImplicit()
841
    {
842 563
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
843
    }
844
845
    /**
846
     * Whether the change tracking policy of this class is "notify".
847
     *
848
     * @return boolean
849
     */
850 327
    public function isChangeTrackingNotify()
851
    {
852 327
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
853
    }
854
855
    /**
856
     * Gets the ReflectionProperties of the mapped class.
857
     *
858
     * @return array An array of ReflectionProperty instances.
859
     */
860 89
    public function getReflectionProperties()
861
    {
862 89
        return $this->reflFields;
863
    }
864
865
    /**
866
     * Gets a ReflectionProperty for a specific field of the mapped class.
867
     *
868
     * @param string $name
869
     *
870
     * @return \ReflectionProperty
871
     */
872
    public function getReflectionProperty($name)
873
    {
874
        return $this->reflFields[$name];
875
    }
876
877
    /**
878
     * {@inheritDoc}
879
     */
880 800
    public function getName()
881
    {
882 800
        return $this->name;
883
    }
884
885
    /**
886
     * The namespace this Document class belongs to.
887
     *
888
     * @return string $namespace The namespace name.
889
     */
890
    public function getNamespace()
891
    {
892
        return $this->namespace;
893
    }
894
895
    /**
896
     * Returns the database this Document is mapped to.
897
     *
898
     * @return string $db The database name.
899
     */
900 741
    public function getDatabase()
901
    {
902 741
        return $this->db;
903
    }
904
905
    /**
906
     * Set the database this Document is mapped to.
907
     *
908
     * @param string $db The database name
909
     */
910 93
    public function setDatabase($db)
911
    {
912 93
        $this->db = $db;
913 93
    }
914
915
    /**
916
     * Get the collection this Document is mapped to.
917
     *
918
     * @return string $collection The collection name.
919
     */
920 745
    public function getCollection()
921
    {
922 745
        return $this->collection;
923
    }
924
925
    /**
926
     * Sets the collection this Document is mapped to.
927
     *
928
     * @param array|string $name
929
     *
930
     * @throws \InvalidArgumentException
931
     */
932 833
    public function setCollection($name)
933
    {
934 833
        if (is_array($name)) {
935
            if ( ! isset($name['name'])) {
936
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
937
            }
938
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
939
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
940
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
941
            $this->collection = $name['name'];
942
        } else {
943 833
            $this->collection = $name;
944
        }
945 833
    }
946
947
    /**
948
     * Get whether or not the documents collection is capped.
949
     *
950
     * @return boolean
951
     */
952 4
    public function getCollectionCapped()
953
    {
954 4
        return $this->collectionCapped;
955
    }
956
957
    /**
958
     * Set whether or not the documents collection is capped.
959
     *
960
     * @param boolean $bool
961
     */
962 1
    public function setCollectionCapped($bool)
963
    {
964 1
        $this->collectionCapped = $bool;
965 1
    }
966
967
    /**
968
     * Get the collection size
969
     *
970
     * @return integer
971
     */
972 4
    public function getCollectionSize()
973
    {
974 4
        return $this->collectionSize;
975
    }
976
977
    /**
978
     * Set the collection size.
979
     *
980
     * @param integer $size
981
     */
982 1
    public function setCollectionSize($size)
983
    {
984 1
        $this->collectionSize = $size;
985 1
    }
986
987
    /**
988
     * Get the collection max.
989
     *
990
     * @return integer
991
     */
992 4
    public function getCollectionMax()
993
    {
994 4
        return $this->collectionMax;
995
    }
996
997
    /**
998
     * Set the collection max.
999
     *
1000
     * @param integer $max
1001
     */
1002 1
    public function setCollectionMax($max)
1003
    {
1004 1
        $this->collectionMax = $max;
1005 1
    }
1006
1007
    /**
1008
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1009
     *
1010
     * @return boolean
1011
     */
1012
    public function isMappedToCollection()
1013
    {
1014
        return $this->collection ? true : false;
1015
    }
1016
1017
    /**
1018
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1019
     *
1020
     * @return boolean
1021
     */
1022 694
    public function isFile()
1023
    {
1024 694
        return $this->file ? true : false;
1025
    }
1026
1027
    /**
1028
     * Returns the file field name.
1029
     *
1030
     * @return string $file The file field name.
1031
     */
1032 330
    public function getFile()
1033
    {
1034 330
        return $this->file;
1035
    }
1036
1037
    /**
1038
     * Set the field name that stores the grid file.
1039
     *
1040
     * @param string $file
1041
     */
1042 331
    public function setFile($file)
1043
    {
1044 331
        $this->file = $file;
1045 331
    }
1046
1047
    /**
1048
     * Returns the distance field name.
1049
     *
1050
     * @return string $distance The distance field name.
1051
     */
1052
    public function getDistance()
1053
    {
1054
        return $this->distance;
1055
    }
1056
1057
    /**
1058
     * Set the field name that stores the distance.
1059
     *
1060
     * @param string $distance
1061
     */
1062 1
    public function setDistance($distance)
1063
    {
1064 1
        $this->distance = $distance;
1065 1
    }
1066
1067
    /**
1068
     * Map a field.
1069
     *
1070
     * @param array $mapping The mapping information.
1071
     *
1072
     * @return array
1073
     *
1074
     * @throws MappingException
1075
     */
1076 838
    public function mapField(array $mapping)
1077
    {
1078 838
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1079 8
            $mapping['fieldName'] = $mapping['name'];
1080 8
        }
1081 838
        if ( ! isset($mapping['fieldName'])) {
1082
            throw MappingException::missingFieldName($this->name);
1083
        }
1084 838
        if ( ! isset($mapping['name'])) {
1085 830
            $mapping['name'] = $mapping['fieldName'];
1086 830
        }
1087 838
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1088 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1089
        }
1090 837
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1091
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1092 53
        }
1093 837
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1094 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1095
        }
1096 836
        if (isset($mapping['targetDocument']) && strpos($mapping['targetDocument'], '\\') === false && strlen($this->namespace)) {
1097 533
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1098 533
        }
1099
1100 836
        if (isset($mapping['discriminatorMap'])) {
1101 103
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1102 103
                if (strpos($class, '\\') === false && strlen($this->namespace)) {
1103 68
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1104 68
                }
1105 103
            }
1106 103
        }
1107
1108 836
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1109 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1110
        }
1111
1112 835
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1113
1114 835
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1115 559
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1116 559
        }
1117
1118 835
        if (isset($mapping['embedded'])) {
1119 525
            unset($mapping['cascade']);
1120 835
        } elseif (isset($mapping['cascade'])) {
1121 365
            $mapping['cascade'] = $cascades;
1122 365
        }
1123
1124 835
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1125 835
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1126 835
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1127 835
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1128 835
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1129
        
1130 835
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1131 56
            $mapping['file'] = true;
1132 56
        }
1133 835
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1134
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1135
        }
1136 835 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...
1137 56
            $this->file = $mapping['fieldName'];
1138 56
            $mapping['name'] = 'file';
1139 56
        }
1140 835 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...
1141 7
            $this->distance = $mapping['fieldName'];
1142 7
        }
1143 835
        if (isset($mapping['id']) && $mapping['id'] === true) {
1144 816
            $mapping['name'] = '_id';
1145 816
            $this->identifier = $mapping['fieldName'];
1146 816 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...
1147 801
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1148 801
            }
1149 816
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1150 816
            switch ($this->generatorType) {
1151 816
                case self::GENERATOR_TYPE_AUTO:
1152 749
                    $mapping['type'] = 'id';
1153 749
                    break;
1154 149
                default:
1155 149
                    if ( ! empty($this->generatorOptions['type'])) {
1156 52
                        $mapping['type'] = $this->generatorOptions['type'];
1157 149
                    } elseif (empty($mapping['type'])) {
1158 74
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1159 74
                    }
1160 816
            }
1161 816
            unset($this->generatorOptions['type']);
1162 835
        } elseif (!isset($mapping['embedded']) && !isset($mapping['reference']) && isset($mapping['type'])) {
1163 763
            switch ($mapping['type']) {
1164 763
                case 'int':
1165 763
                case 'float':
1166 298
                    $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1167 298
                    break;
1168 762
                default:
1169 762
                    $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1170 763
            }
1171
1172 763
            if (isset($mapping['strategy'])) {
1173 224
                if (! in_array($mapping['strategy'], $allowedStrategies)) {
1174
                    throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1175
                }
1176 224
            } else {
1177 763
                $mapping['strategy'] = self::STORAGE_STRATEGY_SET;
1178
            }
1179 763
        }
1180
1181 835
        if ( ! isset($mapping['nullable'])) {
1182 39
            $mapping['nullable'] = false;
1183 39
        }
1184
1185 835
        if (isset($mapping['reference']) && ! empty($mapping['simple']) && ! isset($mapping['targetDocument'])) {
1186 1
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1187
        }
1188
1189 834
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1190 834
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1191 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1192
        }
1193
        
1194 830
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1195 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1196
        }
1197
1198 829 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...
1199 454
            $mapping['association'] = self::REFERENCE_ONE;
1200 454
        }
1201 829 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...
1202 396
            $mapping['association'] = self::REFERENCE_MANY;
1203 396
        }
1204 829 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...
1205 405
            $mapping['association'] = self::EMBED_ONE;
1206 405
        }
1207 829 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...
1208 456
            $mapping['association'] = self::EMBED_MANY;
1209 456
        }
1210
1211 829
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1212 105
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1213 105
        }
1214
1215
        /*
1216
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1217
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1218
        }
1219
        */
1220 829
        if (isset($mapping['version'])) {
1221 93
            $mapping['notSaved'] = true;
1222 93
            $this->setVersionMapping($mapping);
1223 92
        }
1224 829
        if (isset($mapping['lock'])) {
1225 26
            $mapping['notSaved'] = true;
1226 26
            $this->setLockMapping($mapping);
1227 25
        }
1228 829
        $mapping['isOwningSide'] = true;
1229 829
        $mapping['isInverseSide'] = false;
1230 829
        if (isset($mapping['reference'])) {
1231 510 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...
1232 214
                $mapping['isOwningSide'] = true;
1233 214
                $mapping['isInverseSide'] = false;
1234 214
            }
1235 510 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...
1236 253
                $mapping['isInverseSide'] = true;
1237 253
                $mapping['isOwningSide'] = false;
1238 253
            }
1239 510 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...
1240 194
                $mapping['isInverseSide'] = true;
1241 194
                $mapping['isOwningSide'] = false;
1242 194
            }
1243 510
            if (!isset($mapping['orphanRemoval'])) {
1244 490
                $mapping['orphanRemoval'] = false;
1245 490
            }
1246 510
        }
1247
1248 829
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1249 829
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1250 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1251
        }
1252
1253 828
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1254 828
        if (isset($mapping['association'])) {
1255 649
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1256 649
        }
1257
1258 828
        return $mapping;
1259
    }
1260
1261
    /**
1262
     * Map a MongoGridFSFile.
1263
     *
1264
     * @param array $mapping The mapping information.
1265
     */
1266
    public function mapFile(array $mapping)
1267
    {
1268
        $mapping['file'] = true;
1269
        $mapping['type'] = 'file';
1270
        $this->mapField($mapping);
1271
    }
1272
1273
    /**
1274
     * Map a single embedded document.
1275
     *
1276
     * @param array $mapping The mapping information.
1277
     */
1278 6
    public function mapOneEmbedded(array $mapping)
1279
    {
1280 6
        $mapping['embedded'] = true;
1281 6
        $mapping['type'] = 'one';
1282 6
        $this->mapField($mapping);
1283 5
    }
1284
1285
    /**
1286
     * Map a collection of embedded documents.
1287
     *
1288
     * @param array $mapping The mapping information.
1289
     */
1290 3
    public function mapManyEmbedded(array $mapping)
1291
    {
1292 3
        $mapping['embedded'] = true;
1293 3
        $mapping['type'] = 'many';
1294 3
        $this->mapField($mapping);
1295 3
    }
1296
1297
    /**
1298
     * Map a single document reference.
1299
     *
1300
     * @param array $mapping The mapping information.
1301
     */
1302 8
    public function mapOneReference(array $mapping)
1303
    {
1304 8
        $mapping['reference'] = true;
1305 8
        $mapping['type'] = 'one';
1306 8
        $this->mapField($mapping);
1307 8
    }
1308
1309
    /**
1310
     * Map a collection of document references.
1311
     *
1312
     * @param array $mapping The mapping information.
1313
     */
1314 8
    public function mapManyReference(array $mapping)
1315
    {
1316 8
        $mapping['reference'] = true;
1317 8
        $mapping['type'] = 'many';
1318 8
        $this->mapField($mapping);
1319 8
    }
1320
1321
    /**
1322
     * INTERNAL:
1323
     * Adds a field mapping without completing/validating it.
1324
     * This is mainly used to add inherited field mappings to derived classes.
1325
     *
1326
     * @param array $fieldMapping
1327
     */
1328 115
    public function addInheritedFieldMapping(array $fieldMapping)
1329
    {
1330 115
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1331
1332 115
        if (isset($fieldMapping['association'])) {
1333 76
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1334 76
        }
1335 115
    }
1336
1337
    /**
1338
     * INTERNAL:
1339
     * Adds an association mapping without completing/validating it.
1340
     * This is mainly used to add inherited association mappings to derived classes.
1341
     *
1342
     * @param array $mapping
1343
     *
1344
     * @return void
1345
     *
1346
     * @throws MappingException
1347
     */
1348 77
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1349
    {
1350 77
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1351 77
    }
1352
1353
    /**
1354
     * Checks whether the class has a mapped association with the given field name.
1355
     *
1356
     * @param string $fieldName
1357
     * @return boolean
1358
     */
1359 7
    public function hasReference($fieldName)
1360
    {
1361 7
        return isset($this->fieldMappings[$fieldName]['reference']);
1362
    }
1363
1364
    /**
1365
     * Checks whether the class has a mapped embed with the given field name.
1366
     *
1367
     * @param string $fieldName
1368
     * @return boolean
1369
     */
1370 5
    public function hasEmbed($fieldName)
1371
    {
1372 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1373
    }
1374
1375
    /**
1376
     * {@inheritDoc}
1377
     *
1378
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1379
     */
1380 7
    public function hasAssociation($fieldName)
1381
    {
1382 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1383
    }
1384
1385
    /**
1386
     * {@inheritDoc}
1387
     *
1388
     * Checks whether the class has a mapped reference or embed for the specified field and
1389
     * is a single valued association.
1390
     */
1391
    public function isSingleValuedAssociation($fieldName)
1392
    {
1393
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1394
    }
1395
1396
    /**
1397
     * {@inheritDoc}
1398
     *
1399
     * Checks whether the class has a mapped reference or embed for the specified field and
1400
     * is a collection valued association.
1401
     */
1402
    public function isCollectionValuedAssociation($fieldName)
1403
    {
1404
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1405
    }
1406
1407
    /**
1408
     * Checks whether the class has a mapped association for the specified field
1409
     * and if yes, checks whether it is a single-valued association (to-one).
1410
     *
1411
     * @param string $fieldName
1412
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1413
     */
1414
    public function isSingleValuedReference($fieldName)
1415
    {
1416
        return isset($this->fieldMappings[$fieldName]['association']) &&
1417
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1418
    }
1419
1420
    /**
1421
     * Checks whether the class has a mapped association for the specified field
1422
     * and if yes, checks whether it is a collection-valued association (to-many).
1423
     *
1424
     * @param string $fieldName
1425
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1426
     */
1427
    public function isCollectionValuedReference($fieldName)
1428
    {
1429
        return isset($this->fieldMappings[$fieldName]['association']) &&
1430
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1431
    }
1432
1433
    /**
1434
     * Checks whether the class has a mapped embedded document for the specified field
1435
     * and if yes, checks whether it is a single-valued association (to-one).
1436
     *
1437
     * @param string $fieldName
1438
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1439
     */
1440
    public function isSingleValuedEmbed($fieldName)
1441
    {
1442
        return isset($this->fieldMappings[$fieldName]['association']) &&
1443
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1444
    }
1445
1446
    /**
1447
     * Checks whether the class has a mapped embedded document for the specified field
1448
     * and if yes, checks whether it is a collection-valued association (to-many).
1449
     *
1450
     * @param string $fieldName
1451
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1452
     */
1453
    public function isCollectionValuedEmbed($fieldName)
1454
    {
1455
        return isset($this->fieldMappings[$fieldName]['association']) &&
1456
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1457
    }
1458
1459
    /**
1460
     * Sets the ID generator used to generate IDs for instances of this class.
1461
     *
1462
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1463
     */
1464 743
    public function setIdGenerator($generator)
1465
    {
1466 743
        $this->idGenerator = $generator;
1467 743
    }
1468
1469
    /**
1470
     * Casts the identifier to its portable PHP type.
1471
     *
1472
     * @param mixed $id
1473
     * @return mixed $id
1474
     */
1475 580
    public function getPHPIdentifierValue($id)
1476
    {
1477 580
        $idType = $this->fieldMappings[$this->identifier]['type'];
1478 580
        return Type::getType($idType)->convertToPHPValue($id);
1479
    }
1480
1481
    /**
1482
     * Casts the identifier to its database type.
1483
     *
1484
     * @param mixed $id
1485
     * @return mixed $id
1486
     */
1487 642
    public function getDatabaseIdentifierValue($id)
1488
    {
1489 642
        $idType = $this->fieldMappings[$this->identifier]['type'];
1490 642
        return Type::getType($idType)->convertToDatabaseValue($id);
1491
    }
1492
1493
    /**
1494
     * Sets the document identifier of a document.
1495
     *
1496
     * The value will be converted to a PHP type before being set.
1497
     *
1498
     * @param object $document
1499
     * @param mixed $id
1500
     */
1501 513
    public function setIdentifierValue($document, $id)
1502
    {
1503 513
        $id = $this->getPHPIdentifierValue($id);
1504 513
        $this->reflFields[$this->identifier]->setValue($document, $id);
1505 513
    }
1506
1507
    /**
1508
     * Gets the document identifier as a PHP type.
1509
     *
1510
     * @param object $document
1511
     * @return mixed $id
1512
     */
1513 593
    public function getIdentifierValue($document)
1514
    {
1515 593
        return $this->reflFields[$this->identifier]->getValue($document);
1516
    }
1517
1518
    /**
1519
     * {@inheritDoc}
1520
     *
1521
     * Since MongoDB only allows exactly one identifier field this is a proxy
1522
     * to {@see getIdentifierValue()} and returns an array with the identifier
1523
     * field as a key.
1524
     */
1525
    public function getIdentifierValues($object)
1526
    {
1527
        return array($this->identifier => $this->getIdentifierValue($object));
1528
    }
1529
1530
    /**
1531
     * Get the document identifier object as a database type.
1532
     *
1533
     * @param object $document
1534
     *
1535
     * @return \MongoId $id The MongoID object.
1536
     */
1537 32
    public function getIdentifierObject($document)
1538
    {
1539 32
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1540
    }
1541
1542
    /**
1543
     * Sets the specified field to the specified value on the given document.
1544
     *
1545
     * @param object $document
1546
     * @param string $field
1547
     * @param mixed $value
1548
     */
1549 7
    public function setFieldValue($document, $field, $value)
1550
    {
1551 7
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1552
            //property changes to an uninitialized proxy will not be tracked or persisted,
1553
            //so the proxy needs to be loaded first.
1554 1
            $document->__load();
1555 1
        }
1556
        
1557 7
        $this->reflFields[$field]->setValue($document, $value);
1558 7
    }
1559
1560
    /**
1561
     * Gets the specified field's value off the given document.
1562
     *
1563
     * @param object $document
1564
     * @param string $field
1565
     *
1566
     * @return mixed
1567
     */
1568 25
    public function getFieldValue($document, $field)
1569
    {
1570 25
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1571 1
            $document->__load();
1572 1
        }
1573
        
1574 25
        return $this->reflFields[$field]->getValue($document);
1575
    }
1576
1577
    /**
1578
     * Gets the mapping of a field.
1579
     *
1580
     * @param string $fieldName  The field name.
1581
     *
1582
     * @return array  The field mapping.
1583
     *
1584
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1585
     *
1586
     * @throws MappingException
1587
     */
1588 90
    public function getFieldMapping($fieldName)
1589
    {
1590 90
        if ( ! isset($this->fieldMappings[$fieldName])) {
1591 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1592
        }
1593 88
        return $this->fieldMappings[$fieldName];
1594
    }
1595
1596
    /**
1597
     * Gets mappings of fields holding embedded document(s).
1598
     *
1599
     * @return array of field mappings
1600
     */
1601 555
    public function getEmbeddedFieldsMappings()
1602
    {
1603 555
        return array_filter(
1604 555
            $this->associationMappings,
1605
            function($assoc) { return ! empty($assoc['embedded']); }
1606 555
        );
1607
    }
1608
1609
    /**
1610
     * Check if the field is not null.
1611
     *
1612
     * @param string $fieldName  The field name
1613
     *
1614
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1615
     */
1616 1
    public function isNullable($fieldName)
1617
    {
1618 1
        $mapping = $this->getFieldMapping($fieldName);
1619 1
        if ($mapping !== false) {
1620 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1621
        }
1622
        return false;
1623
    }
1624
1625
    /**
1626
     * Checks whether the document has a discriminator field and value configured.
1627
     *
1628
     * @return boolean
1629
     */
1630 474
    public function hasDiscriminator()
1631
    {
1632 474
        return isset($this->discriminatorField, $this->discriminatorValue);
1633
    }
1634
1635
    /**
1636
     * Sets the type of Id generator to use for the mapped class.
1637
     */
1638 336
    public function setIdGeneratorType($generatorType)
1639
    {
1640 336
        $this->generatorType = $generatorType;
1641 336
    }
1642
1643
    /**
1644
     * Sets the Id generator options.
1645
     */
1646
    public function setIdGeneratorOptions($generatorOptions)
1647
    {
1648
        $this->generatorOptions = $generatorOptions;
1649
    }
1650
1651
    /**
1652
     * @return boolean
1653
     */
1654 561
    public function isInheritanceTypeNone()
1655
    {
1656 561
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1657
    }
1658
1659
    /**
1660
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1661
     *
1662
     * @return boolean
1663
     */
1664 330
    public function isInheritanceTypeSingleCollection()
1665
    {
1666 330
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1667
    }
1668
1669
    /**
1670
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1671
     *
1672
     * @return boolean
1673
     */
1674
    public function isInheritanceTypeCollectionPerClass()
1675
    {
1676
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1677
    }
1678
1679
    /**
1680
     * Sets the mapped subclasses of this class.
1681
     *
1682
     * @param string[] $subclasses The names of all mapped subclasses.
1683
     */
1684 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...
1685
    {
1686 2
        foreach ($subclasses as $subclass) {
1687 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1688 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1689 1
            } else {
1690 1
                $this->subClasses[] = $subclass;
1691
            }
1692 2
        }
1693 2
    }
1694
1695
    /**
1696
     * Sets the parent class names.
1697
     * Assumes that the class names in the passed array are in the order:
1698
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1699
     *
1700
     * @param string[] $classNames
1701
     */
1702 798
    public function setParentClasses(array $classNames)
1703
    {
1704 798
        $this->parentClasses = $classNames;
1705
1706 798
        if (count($classNames) > 0) {
1707 101
            $this->rootDocumentName = array_pop($classNames);
1708 101
        }
1709 798
    }
1710
1711
    /**
1712
     * Checks whether the class will generate a new \MongoId instance for us.
1713
     *
1714
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1715
     */
1716
    public function isIdGeneratorAuto()
1717
    {
1718
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1719
    }
1720
1721
    /**
1722
     * Checks whether the class will use a collection to generate incremented identifiers.
1723
     *
1724
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1725
     */
1726
    public function isIdGeneratorIncrement()
1727
    {
1728
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1729
    }
1730
1731
    /**
1732
     * Checks whether the class will generate a uuid id.
1733
     *
1734
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1735
     */
1736
    public function isIdGeneratorUuid()
1737
    {
1738
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1739
    }
1740
1741
    /**
1742
     * Checks whether the class uses no id generator.
1743
     *
1744
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1745
     */
1746
    public function isIdGeneratorNone()
1747
    {
1748
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1749
    }
1750
1751
    /**
1752
     * Sets the version field mapping used for versioning. Sets the default
1753
     * value to use depending on the column type.
1754
     *
1755
     * @param array $mapping   The version field mapping array
1756
     * 
1757
     * @throws LockException
1758
     */
1759 93
    public function setVersionMapping(array &$mapping)
1760
    {
1761 93
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
1762 1
            throw LockException::invalidVersionFieldType($mapping['type']);
1763
        }
1764
1765 92
        $this->isVersioned  = true;
1766 92
        $this->versionField = $mapping['fieldName'];
1767 92
    }
1768
1769
    /**
1770
     * Sets whether this class is to be versioned for optimistic locking.
1771
     *
1772
     * @param boolean $bool
1773
     */
1774 330
    public function setVersioned($bool)
1775
    {
1776 330
        $this->isVersioned = $bool;
1777 330
    }
1778
1779
    /**
1780
     * Sets the name of the field that is to be used for versioning if this class is
1781
     * versioned for optimistic locking.
1782
     *
1783
     * @param string $versionField
1784
     */
1785 330
    public function setVersionField($versionField)
1786
    {
1787 330
        $this->versionField = $versionField;
1788 330
    }
1789
1790
    /**
1791
     * Sets the version field mapping used for versioning. Sets the default
1792
     * value to use depending on the column type.
1793
     *
1794
     * @param array $mapping   The version field mapping array
1795
     *
1796
     * @throws \Doctrine\ODM\MongoDB\LockException
1797
     */
1798 26
    public function setLockMapping(array &$mapping)
1799
    {
1800 26
        if ($mapping['type'] !== 'int') {
1801 1
            throw LockException::invalidLockFieldType($mapping['type']);
1802
        }
1803
1804 25
        $this->isLockable = true;
1805 25
        $this->lockField = $mapping['fieldName'];
1806 25
    }
1807
1808
    /**
1809
     * Sets whether this class is to allow pessimistic locking.
1810
     *
1811
     * @param boolean $bool
1812
     */
1813
    public function setLockable($bool)
1814
    {
1815
        $this->isLockable = $bool;
1816
    }
1817
1818
    /**
1819
     * Sets the name of the field that is to be used for storing whether a document
1820
     * is currently locked or not.
1821
     *
1822
     * @param string $lockField
1823
     */
1824
    public function setLockField($lockField)
1825
    {
1826
        $this->lockField = $lockField;
1827
    }
1828
1829
    /**
1830
     * {@inheritDoc}
1831
     */
1832
    public function getFieldNames()
1833
    {
1834
        return array_keys($this->fieldMappings);
1835
    }
1836
1837
    /**
1838
     * {@inheritDoc}
1839
     */
1840
    public function getAssociationNames()
1841
    {
1842
        return array_keys($this->associationMappings);
1843
    }
1844
1845
    /**
1846
     * {@inheritDoc}
1847
     */
1848 21
    public function getTypeOfField($fieldName)
1849
    {
1850 21
        return isset($this->fieldMappings[$fieldName]) ?
1851 21
            $this->fieldMappings[$fieldName]['type'] : null;
1852
    }
1853
1854
    /**
1855
     * {@inheritDoc}
1856
     */
1857 6
    public function getAssociationTargetClass($assocName)
1858
    {
1859 6
        if ( ! isset($this->associationMappings[$assocName])) {
1860 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
1861
        }
1862
1863 3
        return $this->associationMappings[$assocName]['targetDocument'];
1864
    }
1865
1866
    /**
1867
     * {@inheritDoc}
1868
     */
1869
    public function isAssociationInverseSide($fieldName)
1870
    {
1871
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
1872
    }
1873
1874
    /**
1875
     * {@inheritDoc}
1876
     */
1877
    public function getAssociationMappedByTargetField($fieldName)
1878
    {
1879
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
1880
    }
1881
}
1882