Completed
Pull Request — master (#1219)
by Maciej
12:55
created

ClassMetadataInfo::isIdentifier()   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 1
crap 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB\Mapping;
21
22
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
23
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
24
use Doctrine\ODM\MongoDB\LockException;
25
use Doctrine\ODM\MongoDB\Mapping\MappingException;
26
use Doctrine\ODM\MongoDB\Proxy\Proxy;
27
use Doctrine\ODM\MongoDB\Types\Type;
28
use InvalidArgumentException;
29
30
/**
31
 * A <tt>ClassMetadata</tt> instance holds all the object-document mapping metadata
32
 * of a document and it's references.
33
 *
34
 * Once populated, ClassMetadata instances are usually cached in a serialized form.
35
 *
36
 * <b>IMPORTANT NOTE:</b>
37
 *
38
 * The fields of this class are only public for 2 reasons:
39
 * 1) To allow fast READ access.
40
 * 2) To drastically reduce the size of a serialized instance (private/protected members
41
 *    get the whole class name, namespace inclusive, prepended to every property in
42
 *    the serialized representation).
43
 *
44
 * @since       1.0
45
 */
46
class ClassMetadataInfo implements \Doctrine\Common\Persistence\Mapping\ClassMetadata
47
{
48
    /* The Id generator types. */
49
    /**
50
     * AUTO means Doctrine will automatically create a new \MongoId instance for us.
51
     */
52
    const GENERATOR_TYPE_AUTO = 1;
53
54
    /**
55
     * INCREMENT means a separate collection is used for maintaining and incrementing id generation.
56
     * Offers full portability.
57
     */
58
    const GENERATOR_TYPE_INCREMENT = 2;
59
60
    /**
61
     * UUID means Doctrine will generate a uuid for us.
62
     */
63
    const GENERATOR_TYPE_UUID = 3;
64
65
    /**
66
     * ALNUM means Doctrine will generate Alpha-numeric string identifiers, using the INCREMENT
67
     * generator to ensure identifier uniqueness
68
     */
69
    const GENERATOR_TYPE_ALNUM = 4;
70
71
    /**
72
     * CUSTOM means Doctrine expect a class parameter. It will then try to initiate that class
73
     * and pass other options to the generator. It will throw an Exception if the class
74
     * does not exist or if an option was passed for that there is not setter in the new
75
     * generator class.
76
     *
77
     * The class  will have to be a subtype of AbstractIdGenerator.
78
     */
79
    const GENERATOR_TYPE_CUSTOM = 5;
80
81
    /**
82
     * NONE means Doctrine will not generate any id for us and you are responsible for manually
83
     * assigning an id.
84
     */
85
    const GENERATOR_TYPE_NONE = 6;
86
87
    /**
88
     * Default discriminator field name.
89
     *
90
     * This is used for associations value for associations where a that do not define a "targetDocument" or
91
     * "discriminatorField" option in their mapping.
92
     */
93
    const DEFAULT_DISCRIMINATOR_FIELD = '_doctrine_class_name';
94
95
    const REFERENCE_ONE = 1;
96
    const REFERENCE_MANY = 2;
97
    const EMBED_ONE = 3;
98
    const EMBED_MANY = 4;
99
    const MANY = 'many';
100
    const ONE = 'one';
101
102
    /* The inheritance mapping types */
103
    /**
104
     * NONE means the class does not participate in an inheritance hierarchy
105
     * and therefore does not need an inheritance mapping type.
106
     */
107
    const INHERITANCE_TYPE_NONE = 1;
108
109
    /**
110
     * SINGLE_COLLECTION means the class will be persisted according to the rules of
111
     * <tt>Single Collection Inheritance</tt>.
112
     */
113
    const INHERITANCE_TYPE_SINGLE_COLLECTION = 2;
114
115
    /**
116
     * COLLECTION_PER_CLASS means the class will be persisted according to the rules
117
     * of <tt>Concrete Collection Inheritance</tt>.
118
     */
119
    const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3;
120
121
    /**
122
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
123
     * by doing a property-by-property comparison with the original data. This will
124
     * be done for all entities that are in MANAGED state at commit-time.
125
     *
126
     * This is the default change tracking policy.
127
     */
128
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
129
130
    /**
131
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
132
     * by doing a property-by-property comparison with the original data. This will
133
     * be done only for entities that were explicitly saved (through persist() or a cascade).
134
     */
135
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
136
137
    /**
138
     * NOTIFY means that Doctrine relies on the entities sending out notifications
139
     * when their properties change. Such entity classes must implement
140
     * the <tt>NotifyPropertyChanged</tt> interface.
141
     */
142
    const CHANGETRACKING_NOTIFY = 3;
143
144
    /**
145
     * READ-ONLY: The name of the mongo database the document is mapped to.
146
     */
147
    public $db;
148
149
    /**
150
     * READ-ONLY: The name of the mongo collection the document is mapped to.
151
     */
152
    public $collection;
153
154
    /**
155
     * READ-ONLY: If the collection should be a fixed size.
156
     */
157
    public $collectionCapped;
158
159
    /**
160
     * READ-ONLY: If the collection is fixed size, its size in bytes.
161
     */
162
    public $collectionSize;
163
164
    /**
165
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
166
     */
167
    public $collectionMax;
168
169
    /**
170
     * READ-ONLY: The field name of the document identifier.
171
     */
172
    public $identifier;
173
174
    /**
175
     * READ-ONLY: The field that stores a file reference and indicates the
176
     * document is a file and should be stored on the MongoGridFS.
177
     */
178
    public $file;
179
180
    /**
181
     * READ-ONLY: The field that stores the calculated distance when performing geo spatial
182
     * queries.
183
     */
184
    public $distance;
185
186
    /**
187
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
188
     */
189
    public $slaveOkay;
190
191
    /**
192
     * READ-ONLY: The array of indexes for the document collection.
193
     */
194
    public $indexes = array();
195
196
    /**
197
     * READ-ONLY: Whether or not queries on this document should require indexes.
198
     */
199
    public $requireIndexes = false;
200
201
    /**
202
     * READ-ONLY: The name of the document class.
203
     */
204
    public $name;
205
206
    /**
207
     * READ-ONLY: The namespace the document class is contained in.
208
     *
209
     * @var string
210
     * @todo Not really needed. Usage could be localized.
211
     */
212
    public $namespace;
213
214
    /**
215
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
216
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
217
     * as {@link $documentName}.
218
     *
219
     * @var string
220
     */
221
    public $rootDocumentName;
222
223
    /**
224
     * The name of the custom repository class used for the document class.
225
     * (Optional).
226
     *
227
     * @var string
228
     */
229
    public $customRepositoryClassName;
230
231
    /**
232
     * READ-ONLY: The names of the parent classes (ancestors).
233
     *
234
     * @var array
235
     */
236
    public $parentClasses = array();
237
238
    /**
239
     * READ-ONLY: The names of all subclasses (descendants).
240
     *
241
     * @var array
242
     */
243
    public $subClasses = array();
244
245
    /**
246
     * The ReflectionProperty instances of the mapped class.
247
     *
248
     * @var \ReflectionProperty[]
249
     */
250
    public $reflFields = array();
251
252
    /**
253
     * READ-ONLY: The inheritance mapping type used by the class.
254
     *
255
     * @var integer
256
     */
257
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
258
259
    /**
260
     * READ-ONLY: The Id generator type used by the class.
261
     *
262
     * @var string
263
     */
264
    public $generatorType = self::GENERATOR_TYPE_AUTO;
265
266
    /**
267
     * READ-ONLY: The Id generator options.
268
     *
269
     * @var array
270
     */
271
    public $generatorOptions = array();
272
273
    /**
274
     * READ-ONLY: The ID generator used for generating IDs for this class.
275
     *
276
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
277
     */
278
    public $idGenerator;
279
280
    /**
281
     * READ-ONLY: The field mappings of the class.
282
     * Keys are field names and values are mapping definitions.
283
     *
284
     * The mapping definition array has the following values:
285
     *
286
     * - <b>fieldName</b> (string)
287
     * The name of the field in the Document.
288
     *
289
     * - <b>id</b> (boolean, optional)
290
     * Marks the field as the primary key of the document. Multiple fields of an
291
     * document can have the id attribute, forming a composite key.
292
     *
293
     * @var array
294
     */
295
    public $fieldMappings = array();
296
297
    /**
298
     * READ-ONLY: The association mappings of the class.
299
     * Keys are field names and values are mapping definitions.
300
     *
301
     * @var array
302
     */
303
    public $associationMappings = array();
304
305
    /**
306
     * READ-ONLY: Array of fields to also load with a given method.
307
     *
308
     * @var array
309
     */
310
    public $alsoLoadMethods = array();
311
312
    /**
313
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
314
     *
315
     * @var array
316
     */
317
    public $lifecycleCallbacks = array();
318
319
    /**
320
     * READ-ONLY: The discriminator value of this class.
321
     *
322
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
323
     * where a discriminator field is used.</b>
324
     *
325
     * @var mixed
326
     * @see discriminatorField
327
     */
328
    public $discriminatorValue;
329
330
    /**
331
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
332
     *
333
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
334
     * where a discriminator field is used.</b>
335
     *
336
     * @var mixed
337
     * @see discriminatorField
338
     */
339
    public $discriminatorMap = array();
340
341
    /**
342
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
343
     * inheritance mapping.
344
     *
345
     * @var string
346
     */
347
    public $discriminatorField;
348
349
    /**
350
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
351
     *
352
     * @var string
353
     * @see discriminatorField
354
     */
355
    public $defaultDiscriminatorValue;
356
357
    /**
358
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
359
     *
360
     * @var boolean
361
     */
362
    public $isMappedSuperclass = false;
363
364
    /**
365
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
366
     *
367
     * @var boolean
368
     */
369
    public $isEmbeddedDocument = false;
370
371
    /**
372
     * READ-ONLY: The policy used for change-tracking on entities of this class.
373
     *
374
     * @var integer
375
     */
376
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
377
378
    /**
379
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
380
     * with optimistic locking.
381
     *
382
     * @var boolean $isVersioned
383
     */
384
    public $isVersioned;
385
386
    /**
387
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
388
     *
389
     * @var mixed $versionField
390
     */
391
    public $versionField;
392
393
    /**
394
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
395
     * locking.
396
     *
397
     * @var boolean $isLockable
398
     */
399
    public $isLockable;
400
401
    /**
402
     * READ-ONLY: The name of the field which is used for locking a document.
403
     *
404
     * @var mixed $lockField
405
     */
406
    public $lockField;
407
408
    /**
409
     * The ReflectionClass instance of the mapped class.
410
     *
411
     * @var \ReflectionClass
412
     */
413
    public $reflClass;
414
415
    /**
416
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
417
     * metadata of the class with the given name.
418
     *
419
     * @param string $documentName The name of the document class the new instance is used for.
420
     */
421 858
    public function __construct($documentName)
422
    {
423 858
        $this->name = $documentName;
424 858
        $this->rootDocumentName = $documentName;
425 858
    }
426
427
    /**
428
     * {@inheritDoc}
429
     */
430 808
    public function getReflectionClass()
431
    {
432 808
        if ( ! $this->reflClass) {
433 2
            $this->reflClass = new \ReflectionClass($this->name);
434 2
        }
435
436 808
        return $this->reflClass;
437
    }
438
439
    /**
440
     * {@inheritDoc}
441
     */
442 293
    public function isIdentifier($fieldName)
443
    {
444 293
        return $this->identifier === $fieldName;
445
    }
446
447
    /**
448
     * INTERNAL:
449
     * Sets the mapped identifier field of this class.
450
     *
451
     * @param string $identifier
452
     */
453 331
    public function setIdentifier($identifier)
454
    {
455 331
        $this->identifier = $identifier;
456 331
    }
457
458
    /**
459
     * {@inheritDoc}
460
     *
461
     * Since MongoDB only allows exactly one identifier field
462
     * this will always return an array with only one value
463
     */
464 25
    public function getIdentifier()
465
    {
466 25
        return array($this->identifier);
467
    }
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 90
    public function getIdentifierFieldNames()
476
    {
477 90
        return array($this->identifier);
478
    }
479
480
    /**
481
     * {@inheritDoc}
482
     */
483 514
    public function hasField($fieldName)
484
    {
485 514
        return isset($this->fieldMappings[$fieldName]);
486
    }
487
488
    /**
489
     * Sets the inheritance type used by the class and it's subclasses.
490
     *
491
     * @param integer $type
492
     */
493 342
    public function setInheritanceType($type)
494
    {
495 342
        $this->inheritanceType = $type;
496 342
    }
497
498
    /**
499
     * Checks whether a mapped field is inherited from an entity superclass.
500
     *
501
     * @param  string $fieldName
502
     *
503
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
504
     */
505 808
    public function isInheritedField($fieldName)
506
    {
507 808
        return isset($this->fieldMappings[$fieldName]['inherited']);
508
    }
509
510
    /**
511
     * Registers a custom repository class for the document class.
512
     *
513
     * @param string $repositoryClassName The class name of the custom repository.
514
     */
515 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...
516
    {
517 286
        if ($this->isEmbeddedDocument) {
518
            return;
519
        }
520
        
521 286
        if ($repositoryClassName && strpos($repositoryClassName, '\\') === false && strlen($this->namespace)) {
522 3
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
523 3
        }
524
525 286
        $this->customRepositoryClassName = $repositoryClassName;
526 286
    }
527
528
    /**
529
     * Dispatches the lifecycle event of the given document by invoking all
530
     * registered callbacks.
531
     *
532
     * @param string $event     Lifecycle event
533
     * @param object $document  Document on which the event occurred
534
     * @param array  $arguments Arguments to pass to all callbacks
535
     * @throws \InvalidArgumentException if document class is not this class or
536
     *                                   a Proxy of this class
537
     */
538 591
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
539
    {
540 591
        if ( ! $document instanceof $this->name) {
541 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
542
        }
543
544 590
        if (empty($this->lifecycleCallbacks[$event])) {
545 577
            return;
546
        }
547
548 177
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
549 177
            if ($arguments !== null) {
550 176
                call_user_func_array(array($document, $callback), $arguments);
551 176
            } else {
552 2
                $document->$callback();
553
            }
554 177
        }
555 177
    }
556
557
    /**
558
     * Checks whether the class has callbacks registered for a lifecycle event.
559
     *
560
     * @param string $event Lifecycle event
561
     *
562
     * @return boolean
563
     */
564
    public function hasLifecycleCallbacks($event)
565
    {
566
        return ! empty($this->lifecycleCallbacks[$event]);
567
    }
568
569
    /**
570
     * Gets the registered lifecycle callbacks for an event.
571
     *
572
     * @param string $event
573
     * @return array
574
     */
575
    public function getLifecycleCallbacks($event)
576
    {
577
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
578
    }
579
580
    /**
581
     * Adds a lifecycle callback for documents of this class.
582
     *
583
     * If the callback is already registered, this is a NOOP.
584
     *
585
     * @param string $callback
586
     * @param string $event
587
     */
588 267
    public function addLifecycleCallback($callback, $event)
589
    {
590 267
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
591 1
            return;
592
        }
593
594 267
        $this->lifecycleCallbacks[$event][] = $callback;
595 267
    }
596
597
    /**
598
     * Sets the lifecycle callbacks for documents of this class.
599
     *
600
     * Any previously registered callbacks are overwritten.
601
     *
602
     * @param array $callbacks
603
     */
604 330
    public function setLifecycleCallbacks(array $callbacks)
605
    {
606 330
        $this->lifecycleCallbacks = $callbacks;
607 330
    }
608
609
    /**
610
     * Registers a method for loading document data before field hydration.
611
     *
612
     * Note: A method may be registered multiple times for different fields.
613
     * it will be invoked only once for the first field found.
614
     *
615
     * @param string       $method Method name
616
     * @param array|string $fields Database field name(s)
617
     */
618 15
    public function registerAlsoLoadMethod($method, $fields)
619
    {
620 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
621 15
    }
622
623
    /**
624
     * Sets the AlsoLoad methods for documents of this class.
625
     *
626
     * Any previously registered methods are overwritten.
627
     *
628
     * @param array $methods
629
     */
630 330
    public function setAlsoLoadMethods(array $methods)
631
    {
632 330
        $this->alsoLoadMethods = $methods;
633 330
    }
634
635
    /**
636
     * Sets the discriminator field.
637
     *
638
     * The field name is the the unmapped database field. Discriminator values
639
     * are only used to discern the hydration class and are not mapped to class
640
     * properties.
641
     *
642
     * @param string $discriminatorField
643
     *
644
     * @throws MappingException If the discriminator field conflicts with the
645
     *                          "name" attribute of a mapped field.
646
     */
647 351
    public function setDiscriminatorField($discriminatorField)
648
    {
649 351
        if ($discriminatorField === null) {
650 291
            $this->discriminatorField = null;
651
652 291
            return;
653
        }
654
655
        // Handle array argument with name/fieldName keys for BC
656 115
        if (is_array($discriminatorField)) {
657
            if (isset($discriminatorField['name'])) {
658
                $discriminatorField = $discriminatorField['name'];
659
            } elseif (isset($discriminatorField['fieldName'])) {
660
                $discriminatorField = $discriminatorField['fieldName'];
661
            }
662
        }
663
664 115
        foreach ($this->fieldMappings as $fieldMapping) {
665 4
            if ($discriminatorField == $fieldMapping['name']) {
666 1
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
667
            }
668 114
        }
669
670 114
        $this->discriminatorField = $discriminatorField;
671 114
    }
672
673
    /**
674
     * Sets the discriminator values used by this class.
675
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
676
     *
677
     * @param array $map
678
     *
679
     * @throws MappingException
680
     */
681 347
    public function setDiscriminatorMap(array $map)
682
    {
683 347
        foreach ($map as $value => $className) {
684 113
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
685 81
                $className = $this->namespace . '\\' . $className;
686 81
            }
687 113
            $this->discriminatorMap[$value] = $className;
688 113
            if ($this->name == $className) {
689 105
                $this->discriminatorValue = $value;
690 105
            } else {
691 105
                if ( ! class_exists($className)) {
692
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
693
                }
694 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...
695 91
                    $this->subClasses[] = $className;
696 91
                }
697
            }
698 347
        }
699 347
    }
700
701
    /**
702
     * Sets the default discriminator value to be used for this class
703
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
704
     *
705
     * @param string $defaultDiscriminatorValue
706
     *
707
     * @throws MappingException
708
     */
709 336
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
710
    {
711 336
        if ($defaultDiscriminatorValue === null) {
712 330
            $this->defaultDiscriminatorValue = null;
713
714 330
            return;
715
        }
716
717 53
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
718
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
719
        }
720
721 53
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
722 53
    }
723
724
    /**
725
     * Sets the discriminator value for this class.
726
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
727
     * collection.
728
     *
729
     * @param string $value
730
     */
731
    public function setDiscriminatorValue($value)
732
    {
733
        $this->discriminatorMap[$value] = $this->name;
734
        $this->discriminatorValue = $value;
735
    }
736
737
    /**
738
     * Sets the slaveOkay option applied to collections for this class.
739
     *
740
     * @param boolean|null $slaveOkay
741
     */
742 3
    public function setSlaveOkay($slaveOkay)
743
    {
744 3
        $this->slaveOkay = $slaveOkay === null ? null : (boolean) $slaveOkay;
745 3
    }
746
747
    /**
748
     * Add a index for this Document.
749
     *
750
     * @param array $keys Array of keys for the index.
751
     * @param array $options Array of options for the index.
752
     */
753 219
    public function addIndex($keys, array $options = array())
754
    {
755 219
        $this->indexes[] = array(
756
            'keys' => array_map(function($value) {
757 219
                if ($value == 1 || $value == -1) {
758 55
                    return (int) $value;
759
                }
760 211
                if (is_string($value)) {
761 211
                    $lower = strtolower($value);
762 211
                    if ($lower === 'asc') {
763 204
                        return 1;
764 10
                    } elseif ($lower === 'desc') {
765 3
                        return -1;
766
                    }
767 7
                }
768 7
                return $value;
769 219
            }, $keys),
770
            'options' => $options
771 219
        );
772 219
    }
773
774
    /**
775
     * Set whether or not queries on this document should require indexes.
776
     *
777
     * @param bool $requireIndexes
778
     */
779 800
    public function setRequireIndexes($requireIndexes)
780
    {
781 800
        $this->requireIndexes = $requireIndexes;
782 800
    }
783
784
    /**
785
     * Returns the array of indexes for this Document.
786
     *
787
     * @return array $indexes The array of indexes.
788
     */
789 53
    public function getIndexes()
790
    {
791 53
        return $this->indexes;
792
    }
793
794
    /**
795
     * Checks whether this document has indexes or not.
796
     *
797
     * @return boolean
798
     */
799
    public function hasIndexes()
800
    {
801
        return $this->indexes ? true : false;
802
    }
803
804
    /**
805
     * Sets the change tracking policy used by this class.
806
     *
807
     * @param integer $policy
808
     */
809 334
    public function setChangeTrackingPolicy($policy)
810
    {
811 334
        $this->changeTrackingPolicy = $policy;
812 334
    }
813
814
    /**
815
     * Whether the change tracking policy of this class is "deferred explicit".
816
     *
817
     * @return boolean
818
     */
819 57
    public function isChangeTrackingDeferredExplicit()
820
    {
821 57
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
822
    }
823
824
    /**
825
     * Whether the change tracking policy of this class is "deferred implicit".
826
     *
827
     * @return boolean
828
     */
829 566
    public function isChangeTrackingDeferredImplicit()
830
    {
831 566
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
832
    }
833
834
    /**
835
     * Whether the change tracking policy of this class is "notify".
836
     *
837
     * @return boolean
838
     */
839 329
    public function isChangeTrackingNotify()
840
    {
841 329
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
842
    }
843
844
    /**
845
     * Gets the ReflectionProperties of the mapped class.
846
     *
847
     * @return array An array of ReflectionProperty instances.
848
     */
849 90
    public function getReflectionProperties()
850
    {
851 90
        return $this->reflFields;
852
    }
853
854
    /**
855
     * Gets a ReflectionProperty for a specific field of the mapped class.
856
     *
857
     * @param string $name
858
     *
859
     * @return \ReflectionProperty
860
     */
861
    public function getReflectionProperty($name)
862
    {
863
        return $this->reflFields[$name];
864
    }
865
866
    /**
867
     * {@inheritDoc}
868
     */
869 805
    public function getName()
870
    {
871 805
        return $this->name;
872
    }
873
874
    /**
875
     * The namespace this Document class belongs to.
876
     *
877
     * @return string $namespace The namespace name.
878
     */
879
    public function getNamespace()
880
    {
881
        return $this->namespace;
882
    }
883
884
    /**
885
     * Returns the database this Document is mapped to.
886
     *
887
     * @return string $db The database name.
888
     */
889 744
    public function getDatabase()
890
    {
891 744
        return $this->db;
892
    }
893
894
    /**
895
     * Set the database this Document is mapped to.
896
     *
897
     * @param string $db The database name
898
     */
899 93
    public function setDatabase($db)
900
    {
901 93
        $this->db = $db;
902 93
    }
903
904
    /**
905
     * Get the collection this Document is mapped to.
906
     *
907
     * @return string $collection The collection name.
908
     */
909 748
    public function getCollection()
910
    {
911 748
        return $this->collection;
912
    }
913
914
    /**
915
     * Sets the collection this Document is mapped to.
916
     *
917
     * @param array|string $name
918
     *
919
     * @throws \InvalidArgumentException
920
     */
921 838
    public function setCollection($name)
922
    {
923 838
        if (is_array($name)) {
924
            if ( ! isset($name['name'])) {
925
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
926
            }
927
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
928
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
929
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
930
            $this->collection = $name['name'];
931
        } else {
932 838
            $this->collection = $name;
933
        }
934 838
    }
935
936
    /**
937
     * Get whether or not the documents collection is capped.
938
     *
939
     * @return boolean
940
     */
941 4
    public function getCollectionCapped()
942
    {
943 4
        return $this->collectionCapped;
944
    }
945
946
    /**
947
     * Set whether or not the documents collection is capped.
948
     *
949
     * @param boolean $bool
950
     */
951 1
    public function setCollectionCapped($bool)
952
    {
953 1
        $this->collectionCapped = $bool;
954 1
    }
955
956
    /**
957
     * Get the collection size
958
     *
959
     * @return integer
960
     */
961 4
    public function getCollectionSize()
962
    {
963 4
        return $this->collectionSize;
964
    }
965
966
    /**
967
     * Set the collection size.
968
     *
969
     * @param integer $size
970
     */
971 1
    public function setCollectionSize($size)
972
    {
973 1
        $this->collectionSize = $size;
974 1
    }
975
976
    /**
977
     * Get the collection max.
978
     *
979
     * @return integer
980
     */
981 4
    public function getCollectionMax()
982
    {
983 4
        return $this->collectionMax;
984
    }
985
986
    /**
987
     * Set the collection max.
988
     *
989
     * @param integer $max
990
     */
991 1
    public function setCollectionMax($max)
992
    {
993 1
        $this->collectionMax = $max;
994 1
    }
995
996
    /**
997
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
998
     *
999
     * @return boolean
1000
     */
1001
    public function isMappedToCollection()
1002
    {
1003
        return $this->collection ? true : false;
1004
    }
1005
1006
    /**
1007
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1008
     *
1009
     * @return boolean
1010
     */
1011 697
    public function isFile()
1012
    {
1013 697
        return $this->file ? true : false;
1014
    }
1015
1016
    /**
1017
     * Returns the file field name.
1018
     *
1019
     * @return string $file The file field name.
1020
     */
1021 330
    public function getFile()
1022
    {
1023 330
        return $this->file;
1024
    }
1025
1026
    /**
1027
     * Set the field name that stores the grid file.
1028
     *
1029
     * @param string $file
1030
     */
1031 331
    public function setFile($file)
1032
    {
1033 331
        $this->file = $file;
1034 331
    }
1035
1036
    /**
1037
     * Returns the distance field name.
1038
     *
1039
     * @return string $distance The distance field name.
1040
     */
1041
    public function getDistance()
1042
    {
1043
        return $this->distance;
1044
    }
1045
1046
    /**
1047
     * Set the field name that stores the distance.
1048
     *
1049
     * @param string $distance
1050
     */
1051 1
    public function setDistance($distance)
1052
    {
1053 1
        $this->distance = $distance;
1054 1
    }
1055
1056
    /**
1057
     * Map a field.
1058
     *
1059
     * @param array $mapping The mapping information.
1060
     *
1061
     * @return array
1062
     *
1063
     * @throws MappingException
1064
     */
1065 844
    public function mapField(array $mapping)
1066
    {
1067 844
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1068 8
            $mapping['fieldName'] = $mapping['name'];
1069 8
        }
1070 844
        if ( ! isset($mapping['fieldName'])) {
1071
            throw MappingException::missingFieldName($this->name);
1072
        }
1073 844
        if ( ! isset($mapping['name'])) {
1074 836
            $mapping['name'] = $mapping['fieldName'];
1075 836
        }
1076 844
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1077 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1078
        }
1079 843
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1080
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1081 53
        }
1082 843
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1083 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1084
        }
1085 842 View Code Duplication
        if (isset($mapping['targetDocument']) && strpos($mapping['targetDocument'], '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1086 538
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1087 538
        }
1088 842
        if (isset($mapping['collectionClass'])) {
1089 6 View Code Duplication
            if (strpos($mapping['collectionClass'], '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1090 5
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1091 5
            }
1092 6
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1093 6
        }
1094 842
        if ( ! empty($mapping['collectionClass'])) {
1095 6
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1096 6
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1097 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1098
            }
1099 5
        }
1100
1101 841
        if (isset($mapping['discriminatorMap'])) {
1102 103
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1103 103 View Code Duplication
                if (strpos($class, '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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