Completed
Push — master ( a45a67...eb1ffc )
by Andreas
21s queued 11s
created

ClassMetadataInfo::markReadOnly()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 3
cts 3
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB\Mapping;
21
22
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
23
use Doctrine\ODM\MongoDB\LockException;
24
use Doctrine\ODM\MongoDB\Proxy\Proxy;
25
use Doctrine\ODM\MongoDB\Types\Type;
26
use InvalidArgumentException;
27
28
/**
29
 * A <tt>ClassMetadata</tt> instance holds all the object-document mapping metadata
30
 * of a document and it's references.
31
 *
32
 * Once populated, ClassMetadata instances are usually cached in a serialized form.
33
 *
34
 * <b>IMPORTANT NOTE:</b>
35
 *
36
 * The fields of this class are only public for 2 reasons:
37
 * 1) To allow fast READ access.
38
 * 2) To drastically reduce the size of a serialized instance (private/protected members
39
 *    get the whole class name, namespace inclusive, prepended to every property in
40
 *    the serialized representation).
41
 *
42
 * @since       1.0
43
 */
44
class ClassMetadataInfo implements \Doctrine\Common\Persistence\Mapping\ClassMetadata
45
{
46
    /* The Id generator types. */
47
    /**
48
     * AUTO means Doctrine will automatically create a new \MongoId instance for us.
49
     */
50
    const GENERATOR_TYPE_AUTO = 1;
51
52
    /**
53
     * INCREMENT means a separate collection is used for maintaining and incrementing id generation.
54
     * Offers full portability.
55
     */
56
    const GENERATOR_TYPE_INCREMENT = 2;
57
58
    /**
59
     * UUID means Doctrine will generate a uuid for us.
60
     */
61
    const GENERATOR_TYPE_UUID = 3;
62
63
    /**
64
     * ALNUM means Doctrine will generate Alpha-numeric string identifiers, using the INCREMENT
65
     * generator to ensure identifier uniqueness
66
     */
67
    const GENERATOR_TYPE_ALNUM = 4;
68
69
    /**
70
     * CUSTOM means Doctrine expect a class parameter. It will then try to initiate that class
71
     * and pass other options to the generator. It will throw an Exception if the class
72
     * does not exist or if an option was passed for that there is not setter in the new
73
     * generator class.
74
     *
75
     * The class  will have to be a subtype of AbstractIdGenerator.
76
     */
77
    const GENERATOR_TYPE_CUSTOM = 5;
78
79
    /**
80
     * NONE means Doctrine will not generate any id for us and you are responsible for manually
81
     * assigning an id.
82
     */
83
    const GENERATOR_TYPE_NONE = 6;
84
85
    /**
86
     * Default discriminator field name.
87
     *
88
     * This is used for associations value for associations where a that do not define a "targetDocument" or
89
     * "discriminatorField" option in their mapping.
90
     */
91
    const DEFAULT_DISCRIMINATOR_FIELD = '_doctrine_class_name';
92
93
    const REFERENCE_ONE = 1;
94
    const REFERENCE_MANY = 2;
95
    const EMBED_ONE = 3;
96
    const EMBED_MANY = 4;
97
    const MANY = 'many';
98
    const ONE = 'one';
99
100
    /**
101
     * The types of storeAs references
102
     */
103
    const REFERENCE_STORE_AS_ID = 'id';
104
    const REFERENCE_STORE_AS_DB_REF = 'dbRef';
105
    const REFERENCE_STORE_AS_DB_REF_WITH_DB = 'dbRefWithDb';
106
107
    /* The inheritance mapping types */
108
    /**
109
     * NONE means the class does not participate in an inheritance hierarchy
110
     * and therefore does not need an inheritance mapping type.
111
     */
112
    const INHERITANCE_TYPE_NONE = 1;
113
114
    /**
115
     * SINGLE_COLLECTION means the class will be persisted according to the rules of
116
     * <tt>Single Collection Inheritance</tt>.
117
     */
118
    const INHERITANCE_TYPE_SINGLE_COLLECTION = 2;
119
120
    /**
121
     * COLLECTION_PER_CLASS means the class will be persisted according to the rules
122
     * of <tt>Concrete Collection Inheritance</tt>.
123
     */
124
    const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3;
125
126
    /**
127
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
128
     * by doing a property-by-property comparison with the original data. This will
129
     * be done for all entities that are in MANAGED state at commit-time.
130
     *
131
     * This is the default change tracking policy.
132
     */
133
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
134
135
    /**
136
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
137
     * by doing a property-by-property comparison with the original data. This will
138
     * be done only for entities that were explicitly saved (through persist() or a cascade).
139
     */
140
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
141
142
    /**
143
     * NOTIFY means that Doctrine relies on the entities sending out notifications
144
     * when their properties change. Such entity classes must implement
145
     * the <tt>NotifyPropertyChanged</tt> interface.
146
     */
147
    const CHANGETRACKING_NOTIFY = 3;
148
149
    /**
150
     * SET means that fields will be written to the database using a $set operator
151
     */
152
    const STORAGE_STRATEGY_SET = 'set';
153
154
    /**
155
     * INCREMENT means that fields will be written to the database by calculating
156
     * the difference and using the $inc operator
157
     */
158
    const STORAGE_STRATEGY_INCREMENT = 'increment';
159
160
    const STORAGE_STRATEGY_PUSH_ALL = 'pushAll';
161
    const STORAGE_STRATEGY_ADD_TO_SET = 'addToSet';
162
    const STORAGE_STRATEGY_ATOMIC_SET = 'atomicSet';
163
    const STORAGE_STRATEGY_ATOMIC_SET_ARRAY = 'atomicSetArray';
164
    const STORAGE_STRATEGY_SET_ARRAY = 'setArray';
165
166
    /**
167
     * READ-ONLY: The name of the mongo database the document is mapped to.
168
     */
169
    public $db;
170
171
    /**
172
     * READ-ONLY: The name of the mongo collection the document is mapped to.
173
     */
174
    public $collection;
175
176
    /**
177
     * READ-ONLY: If the collection should be a fixed size.
178
     */
179
    public $collectionCapped;
180
181
    /**
182
     * READ-ONLY: If the collection is fixed size, its size in bytes.
183
     */
184
    public $collectionSize;
185
186
    /**
187
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
188
     */
189
    public $collectionMax;
190
191
    /**
192
     * READ-ONLY: Describes the level of acknowledgement requested from MongoDB for write operations.
193
     */
194
    public $writeConcern;
195
196
    /**
197
     * READ-ONLY: The field name of the document identifier.
198
     */
199
    public $identifier;
200
201
    /**
202
     * READ-ONLY: The field that stores a file reference and indicates the
203
     * document is a file and should be stored on the MongoGridFS.
204
     */
205
    public $file;
206
207
    /**
208
     * READ-ONLY: The field that stores the calculated distance when performing geo spatial
209
     * queries.
210
     */
211
    public $distance;
212
213
    /**
214
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
215
     *
216
     * @deprecated in version 1.2 and will be removed in 2.0.
217
     */
218
    public $slaveOkay;
219
220
    /**
221
     * READ-ONLY: The array of indexes for the document collection.
222
     */
223
    public $indexes = array();
224
225
    /**
226
     * READ-ONLY: Keys and options describing shard key. Only for sharded collections.
227
     */
228
    public $shardKey;
229
230
    /**
231
     * READ-ONLY: Whether or not queries on this document should require indexes.
232
     *
233
     * @deprecated property was deprecated in 1.2 and will be removed in 2.0
234
     */
235
    public $requireIndexes = false;
236
237
    /**
238
     * READ-ONLY: The name of the document class.
239
     */
240
    public $name;
241
242
    /**
243
     * READ-ONLY: The namespace the document class is contained in.
244
     *
245
     * @var string
246
     * @todo Not really needed. Usage could be localized.
247
     */
248
    public $namespace;
249
250
    /**
251
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
252
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
253
     * as {@link $documentName}.
254
     *
255
     * @var string
256
     */
257
    public $rootDocumentName;
258
259
    /**
260
     * The name of the custom repository class used for the document class.
261
     * (Optional).
262
     *
263
     * @var string
264
     */
265
    public $customRepositoryClassName;
266
267
    /**
268
     * READ-ONLY: The names of the parent classes (ancestors).
269
     *
270
     * @var array
271
     */
272
    public $parentClasses = array();
273
274
    /**
275
     * READ-ONLY: The names of all subclasses (descendants).
276
     *
277
     * @var array
278
     */
279
    public $subClasses = array();
280
281
    /**
282
     * The ReflectionProperty instances of the mapped class.
283
     *
284
     * @var \ReflectionProperty[]
285
     */
286
    public $reflFields = array();
287
288
    /**
289
     * READ-ONLY: The inheritance mapping type used by the class.
290
     *
291
     * @var integer
292
     */
293
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
294
295
    /**
296
     * READ-ONLY: The Id generator type used by the class.
297
     *
298
     * @var string
299
     */
300
    public $generatorType = self::GENERATOR_TYPE_AUTO;
301
302
    /**
303
     * READ-ONLY: The Id generator options.
304
     *
305
     * @var array
306
     */
307
    public $generatorOptions = array();
308
309
    /**
310
     * READ-ONLY: The ID generator used for generating IDs for this class.
311
     *
312
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
313
     */
314
    public $idGenerator;
315
316
    /**
317
     * READ-ONLY: The field mappings of the class.
318
     * Keys are field names and values are mapping definitions.
319
     *
320
     * The mapping definition array has the following values:
321
     *
322
     * - <b>fieldName</b> (string)
323
     * The name of the field in the Document.
324
     *
325
     * - <b>id</b> (boolean, optional)
326
     * Marks the field as the primary key of the document. Multiple fields of an
327
     * document can have the id attribute, forming a composite key.
328
     *
329
     * @var array
330
     */
331
    public $fieldMappings = array();
332
333
    /**
334
     * READ-ONLY: The association mappings of the class.
335
     * Keys are field names and values are mapping definitions.
336
     *
337
     * @var array
338
     */
339
    public $associationMappings = array();
340
341
    /**
342
     * READ-ONLY: Array of fields to also load with a given method.
343
     *
344
     * @var array
345
     */
346
    public $alsoLoadMethods = array();
347
348
    /**
349
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
350
     *
351
     * @var array
352
     */
353
    public $lifecycleCallbacks = array();
354
355
    /**
356
     * READ-ONLY: The discriminator value of this class.
357
     *
358
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
359
     * where a discriminator field is used.</b>
360
     *
361
     * @var mixed
362
     * @see discriminatorField
363
     */
364
    public $discriminatorValue;
365
366
    /**
367
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
368
     *
369
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
370
     * where a discriminator field is used.</b>
371
     *
372
     * @var mixed
373
     * @see discriminatorField
374
     */
375
    public $discriminatorMap = array();
376
377
    /**
378
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
379
     * inheritance mapping.
380
     *
381
     * @var string
382
     */
383
    public $discriminatorField;
384
385
    /**
386
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
387
     *
388
     * @var string
389
     * @see discriminatorField
390
     */
391
    public $defaultDiscriminatorValue;
392
393
    /**
394
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
395
     *
396
     * @var boolean
397
     */
398
    public $isMappedSuperclass = false;
399
400
    /**
401
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
402
     *
403
     * @var boolean
404
     */
405
    public $isEmbeddedDocument = false;
406
407
    /**
408
     * READ-ONLY: Whether this class describes the mapping of an aggregation result document.
409
     *
410
     * @var boolean
411
     */
412
    public $isQueryResultDocument = false;
413
414
    /**
415
     * READ-ONLY: The policy used for change-tracking on entities of this class.
416
     *
417
     * @var integer
418
     */
419
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
420
421
    /**
422
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
423
     * with optimistic locking.
424
     *
425
     * @var boolean $isVersioned
426
     */
427
    public $isVersioned;
428
429
    /**
430
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
431
     *
432
     * @var mixed $versionField
433
     */
434
    public $versionField;
435
436
    /**
437
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
438
     * locking.
439
     *
440
     * @var boolean $isLockable
441
     */
442
    public $isLockable;
443
444
    /**
445
     * READ-ONLY: The name of the field which is used for locking a document.
446
     *
447
     * @var mixed $lockField
448
     */
449
    public $lockField;
450
451
    /**
452
     * The ReflectionClass instance of the mapped class.
453
     *
454
     * @var \ReflectionClass
455
     */
456
    public $reflClass;
457
458
    /**
459
     * READ_ONLY: A flag for whether or not this document is read-only.
460
     *
461
     * @var bool
462
     */
463
    public $isReadOnly;
464
465
    /**
466
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
467
     * metadata of the class with the given name.
468
     *
469
     * @param string $documentName The name of the document class the new instance is used for.
470
     */
471 992
    public function __construct($documentName)
472
    {
473 992
        $this->name = $documentName;
474 992
        $this->rootDocumentName = $documentName;
475 992
    }
476
477
    /**
478
     * {@inheritDoc}
479
     */
480 920
    public function getReflectionClass()
481
    {
482 920
        if ( ! $this->reflClass) {
483 2
            $this->reflClass = new \ReflectionClass($this->name);
484
        }
485
486 920
        return $this->reflClass;
487
    }
488
489
    /**
490
     * {@inheritDoc}
491
     */
492 330
    public function isIdentifier($fieldName)
493
    {
494 330
        return $this->identifier === $fieldName;
495
    }
496
497
    /**
498
     * INTERNAL:
499
     * Sets the mapped identifier field of this class.
500
     *
501
     * @param string $identifier
502
     */
503 370
    public function setIdentifier($identifier)
504
    {
505 370
        $this->identifier = $identifier;
506 370
    }
507
508
    /**
509
     * {@inheritDoc}
510
     *
511
     * Since MongoDB only allows exactly one identifier field
512
     * this will always return an array with only one value
513
     */
514 40
    public function getIdentifier()
515
    {
516 40
        return array($this->identifier);
517
    }
518
519
    /**
520
     * {@inheritDoc}
521
     *
522
     * Since MongoDB only allows exactly one identifier field
523
     * this will always return an array with only one value
524
     */
525 98
    public function getIdentifierFieldNames()
526
    {
527 98
        return array($this->identifier);
528
    }
529
530
    /**
531
     * {@inheritDoc}
532
     */
533 565
    public function hasField($fieldName)
534
    {
535 565
        return isset($this->fieldMappings[$fieldName]);
536
    }
537
538
    /**
539
     * Sets the inheritance type used by the class and it's subclasses.
540
     *
541
     * @param integer $type
542
     */
543 386
    public function setInheritanceType($type)
544
    {
545 386
        $this->inheritanceType = $type;
546 386
    }
547
548
    /**
549
     * Checks whether a mapped field is inherited from an entity superclass.
550
     *
551
     * @param  string $fieldName
552
     *
553
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
554
     */
555 920
    public function isInheritedField($fieldName)
556
    {
557 920
        return isset($this->fieldMappings[$fieldName]['inherited']);
558
    }
559
560
    /**
561
     * Registers a custom repository class for the document class.
562
     *
563
     * @param string $repositoryClassName The class name of the custom repository.
564
     */
565 318
    public function setCustomRepositoryClass($repositoryClassName)
566
    {
567 318
        if ($this->isEmbeddedDocument || $this->isQueryResultDocument) {
568
            return;
569
        }
570
571 318 View Code Duplication
        if ($repositoryClassName && strpos($repositoryClassName, '\\') === 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...
572 4
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
573
        }
574
575 318
        $this->customRepositoryClassName = $repositoryClassName;
576 318
    }
577
578
    /**
579
     * Dispatches the lifecycle event of the given document by invoking all
580
     * registered callbacks.
581
     *
582
     * @param string $event     Lifecycle event
583
     * @param object $document  Document on which the event occurred
584
     * @param array  $arguments Arguments to pass to all callbacks
585
     * @throws \InvalidArgumentException if document class is not this class or
586
     *                                   a Proxy of this class
587
     */
588 673
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
589
    {
590 673
        if ( ! $document instanceof $this->name) {
591 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
592
        }
593
594 672
        if (empty($this->lifecycleCallbacks[$event])) {
595 658
            return;
596
        }
597
598 196
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
599 196
            if ($arguments !== null) {
600 195
                call_user_func_array(array($document, $callback), $arguments);
601
            } else {
602 196
                $document->$callback();
603
            }
604
        }
605 196
    }
606
607
    /**
608
     * Checks whether the class has callbacks registered for a lifecycle event.
609
     *
610
     * @param string $event Lifecycle event
611
     *
612
     * @return boolean
613
     */
614
    public function hasLifecycleCallbacks($event)
615
    {
616
        return ! empty($this->lifecycleCallbacks[$event]);
617
    }
618
619
    /**
620
     * Gets the registered lifecycle callbacks for an event.
621
     *
622
     * @param string $event
623
     * @return array
624
     */
625
    public function getLifecycleCallbacks($event)
626
    {
627
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
628
    }
629
630
    /**
631
     * Adds a lifecycle callback for documents of this class.
632
     *
633
     * If the callback is already registered, this is a NOOP.
634
     *
635
     * @param string $callback
636
     * @param string $event
637
     */
638 297
    public function addLifecycleCallback($callback, $event)
639
    {
640 297
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
641 1
            return;
642
        }
643
644 297
        $this->lifecycleCallbacks[$event][] = $callback;
645 297
    }
646
647
    /**
648
     * Sets the lifecycle callbacks for documents of this class.
649
     *
650
     * Any previously registered callbacks are overwritten.
651
     *
652
     * @param array $callbacks
653
     */
654 369
    public function setLifecycleCallbacks(array $callbacks)
655
    {
656 369
        $this->lifecycleCallbacks = $callbacks;
657 369
    }
658
659
    /**
660
     * Registers a method for loading document data before field hydration.
661
     *
662
     * Note: A method may be registered multiple times for different fields.
663
     * it will be invoked only once for the first field found.
664
     *
665
     * @param string       $method Method name
666
     * @param array|string $fields Database field name(s)
667
     */
668 15
    public function registerAlsoLoadMethod($method, $fields)
669
    {
670 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
671 15
    }
672
673
    /**
674
     * Sets the AlsoLoad methods for documents of this class.
675
     *
676
     * Any previously registered methods are overwritten.
677
     *
678
     * @param array $methods
679
     */
680 369
    public function setAlsoLoadMethods(array $methods)
681
    {
682 369
        $this->alsoLoadMethods = $methods;
683 369
    }
684
685
    /**
686
     * Sets the discriminator field.
687
     *
688
     * The field name is the the unmapped database field. Discriminator values
689
     * are only used to discern the hydration class and are not mapped to class
690
     * properties.
691
     *
692
     * @param string $discriminatorField
693
     *
694
     * @throws MappingException If the discriminator field conflicts with the
695
     *                          "name" attribute of a mapped field.
696
     */
697 399
    public function setDiscriminatorField($discriminatorField)
698
    {
699 399
        if ($discriminatorField === null) {
700 326
            $this->discriminatorField = null;
701
702 326
            return;
703
        }
704
705
        // Handle array argument with name/fieldName keys for BC
706 130
        if (is_array($discriminatorField)) {
707
            if (isset($discriminatorField['name'])) {
708
                $discriminatorField = $discriminatorField['name'];
709
            } elseif (isset($discriminatorField['fieldName'])) {
710
                $discriminatorField = $discriminatorField['fieldName'];
711
            }
712
        }
713
714 130
        foreach ($this->fieldMappings as $fieldMapping) {
715 4
            if ($discriminatorField == $fieldMapping['name']) {
716 4
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
717
            }
718
        }
719
720 129
        $this->discriminatorField = $discriminatorField;
721 129
    }
722
723
    /**
724
     * Sets the discriminator values used by this class.
725
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
726
     *
727
     * @param array $map
728
     *
729
     * @throws MappingException
730
     */
731 392
    public function setDiscriminatorMap(array $map)
732
    {
733 392
        foreach ($map as $value => $className) {
734 125
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
735 91
                $className = $this->namespace . '\\' . $className;
736
            }
737 125
            $this->discriminatorMap[$value] = $className;
738 125
            if ($this->name == $className) {
739 117
                $this->discriminatorValue = $value;
740
            } else {
741 120
                if ( ! class_exists($className)) {
742
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
743
                }
744 120
                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...
745 125
                    $this->subClasses[] = $className;
746
                }
747
            }
748
        }
749 392
    }
750
751
    /**
752
     * Sets the default discriminator value to be used for this class
753
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
754
     *
755
     * @param string $defaultDiscriminatorValue
756
     *
757
     * @throws MappingException
758
     */
759 376
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
760
    {
761 376
        if ($defaultDiscriminatorValue === null) {
762 369
            $this->defaultDiscriminatorValue = null;
763
764 369
            return;
765
        }
766
767 60
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
768
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
769
        }
770
771 60
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
772 60
    }
773
774
    /**
775
     * Sets the discriminator value for this class.
776
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
777
     * collection.
778
     *
779
     * @param string $value
780
     */
781 3
    public function setDiscriminatorValue($value)
782
    {
783 3
        $this->discriminatorMap[$value] = $this->name;
784 3
        $this->discriminatorValue = $value;
785 3
    }
786
787
    /**
788
     * Sets the slaveOkay option applied to collections for this class.
789
     *
790
     * @param boolean|null $slaveOkay
791
     *
792
     * @deprecated in version 1.2 and will be removed in 2.0.
793
     */
794 3
    public function setSlaveOkay($slaveOkay)
795
    {
796 3
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
797 3
            sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.'),
798 3
            E_USER_DEPRECATED
799
        );
800 3
        $this->slaveOkay = $slaveOkay === null ? null : (boolean) $slaveOkay;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...etadataInfo::$slaveOkay has been deprecated with message: in version 1.2 and will be removed in 2.0.

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
801 3
    }
802
803
    /**
804
     * Add a index for this Document.
805
     *
806
     * @param array $keys Array of keys for the index.
807
     * @param array $options Array of options for the index.
808
     */
809 232
    public function addIndex($keys, array $options = array())
810
    {
811 232
        $this->indexes[] = array(
812 232 View Code Duplication
            'keys' => array_map(function($value) {
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...
813 232
                if ($value == 1 || $value == -1) {
814 63
                    return (int) $value;
815
                }
816 224
                if (is_string($value)) {
817 224
                    $lower = strtolower($value);
818 224
                    if ($lower === 'asc') {
819 217
                        return 1;
820 11
                    } elseif ($lower === 'desc') {
821 4
                        return -1;
822
                    }
823
                }
824 7
                return $value;
825 232
            }, $keys),
826 232
            'options' => $options
827
        );
828 232
    }
829
830
    /**
831
     * Set whether or not queries on this document should require indexes.
832
     *
833
     * @param bool $requireIndexes
834
     *
835
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
836
     */
837 911
    public function setRequireIndexes($requireIndexes)
838
    {
839 911
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
840 911
            'requireIndexes was deprecated in version 1.2 and will be removed altogether in 2.0.',
841 911
            E_USER_DEPRECATED
842
        );
843 911
        $this->requireIndexes = $requireIndexes;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...taInfo::$requireIndexes has been deprecated with message: property was deprecated in 1.2 and will be removed in 2.0

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
844 911
    }
845
846
    /**
847
     * Returns the array of indexes for this Document.
848
     *
849
     * @return array $indexes The array of indexes.
850
     */
851 54
    public function getIndexes()
852
    {
853 54
        return $this->indexes;
854
    }
855
856
    /**
857
     * Checks whether this document has indexes or not.
858
     *
859
     * @return boolean
860
     */
861
    public function hasIndexes()
862
    {
863
        return $this->indexes ? true : false;
864
    }
865
866
    /**
867
     * Set shard key for this Document.
868
     *
869
     * @param array $keys Array of document keys.
870
     * @param array $options Array of sharding options.
871
     *
872
     * @throws MappingException
873
     */
874 87
    public function setShardKey(array $keys, array $options = array())
875
    {
876 87
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
877 2
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
878
        }
879
880 87
        if ($this->isEmbeddedDocument) {
881 2
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
882
        }
883
884 85
        foreach (array_keys($keys) as $field) {
885 85
            if (! isset($this->fieldMappings[$field])) {
886 78
                continue;
887
            }
888
889 7
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
890 3
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
891
            }
892
893 4
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
894 4
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
895
            }
896
        }
897
898 81
        $this->shardKey = array(
899 81 View Code Duplication
            'keys' => array_map(function($value) {
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...
900 81
                if ($value == 1 || $value == -1) {
901 6
                    return (int) $value;
902
                }
903 80
                if (is_string($value)) {
904 80
                    $lower = strtolower($value);
905 80
                    if ($lower === 'asc') {
906 79
                        return 1;
907 53
                    } elseif ($lower === 'desc') {
908
                        return -1;
909
                    }
910
                }
911 53
                return $value;
912 81
            }, $keys),
913 81
            'options' => $options
914
        );
915 81
    }
916
917
    /**
918
     * @return array
919
     */
920 28
    public function getShardKey()
921
    {
922 28
        return $this->shardKey;
923
    }
924
925
    /**
926
     * Checks whether this document has shard key or not.
927
     *
928
     * @return bool
929
     */
930 612
    public function isSharded()
931
    {
932 612
        return $this->shardKey ? true : false;
933
    }
934
935
    /**
936
     * Sets the write concern used by this class.
937
     *
938
     * @param string $writeConcern
939
     */
940 383
    public function setWriteConcern($writeConcern)
941
    {
942 383
        $this->writeConcern = $writeConcern;
943 383
    }
944
945
    /**
946
     * @return string
947
     */
948 12
    public function getWriteConcern()
949
    {
950 12
        return $this->writeConcern;
951
    }
952
953
    /**
954
     * Whether there is a write concern configured for this class.
955
     *
956
     * @return bool
957
     */
958 618
    public function hasWriteConcern()
959
    {
960 618
        return $this->writeConcern !== null;
961
    }
962
963
    /**
964
     * Sets the change tracking policy used by this class.
965
     *
966
     * @param integer $policy
967
     */
968 374
    public function setChangeTrackingPolicy($policy)
969
    {
970 374
        $this->changeTrackingPolicy = $policy;
971 374
    }
972
973
    /**
974
     * Whether the change tracking policy of this class is "deferred explicit".
975
     *
976
     * @return boolean
977
     */
978 75
    public function isChangeTrackingDeferredExplicit()
979
    {
980 75
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
981
    }
982
983
    /**
984
     * Whether the change tracking policy of this class is "deferred implicit".
985
     *
986
     * @return boolean
987
     */
988 638
    public function isChangeTrackingDeferredImplicit()
989
    {
990 638
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
991
    }
992
993
    /**
994
     * Whether the change tracking policy of this class is "notify".
995
     *
996
     * @return boolean
997
     */
998 352
    public function isChangeTrackingNotify()
999
    {
1000 352
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1001
    }
1002
1003
    /**
1004
     * Gets the ReflectionProperties of the mapped class.
1005
     *
1006
     * @return array An array of ReflectionProperty instances.
1007
     */
1008 98
    public function getReflectionProperties()
1009
    {
1010 98
        return $this->reflFields;
1011
    }
1012
1013
    /**
1014
     * Gets a ReflectionProperty for a specific field of the mapped class.
1015
     *
1016
     * @param string $name
1017
     *
1018
     * @return \ReflectionProperty
1019
     */
1020
    public function getReflectionProperty($name)
1021
    {
1022
        return $this->reflFields[$name];
1023
    }
1024
1025
    /**
1026
     * {@inheritDoc}
1027
     */
1028 926
    public function getName()
1029
    {
1030 926
        return $this->name;
1031
    }
1032
1033
    /**
1034
     * The namespace this Document class belongs to.
1035
     *
1036
     * @return string $namespace The namespace name.
1037
     */
1038
    public function getNamespace()
1039
    {
1040
        return $this->namespace;
1041
    }
1042
1043
    /**
1044
     * Returns the database this Document is mapped to.
1045
     *
1046
     * @return string $db The database name.
1047
     */
1048 846
    public function getDatabase()
1049
    {
1050 846
        return $this->db;
1051
    }
1052
1053
    /**
1054
     * Set the database this Document is mapped to.
1055
     *
1056
     * @param string $db The database name
1057
     */
1058 104
    public function setDatabase($db)
1059
    {
1060 104
        $this->db = $db;
1061 104
    }
1062
1063
    /**
1064
     * Get the collection this Document is mapped to.
1065
     *
1066
     * @return string $collection The collection name.
1067
     */
1068 851
    public function getCollection()
1069
    {
1070 851
        return $this->collection;
1071
    }
1072
1073
    /**
1074
     * Sets the collection this Document is mapped to.
1075
     *
1076
     * @param array|string $name
1077
     *
1078
     * @throws \InvalidArgumentException
1079
     */
1080 956
    public function setCollection($name)
1081
    {
1082 956
        if (is_array($name)) {
1083
            if ( ! isset($name['name'])) {
1084
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
1085
            }
1086
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
1087
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
1088
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
1089
            $this->collection = $name['name'];
1090
        } else {
1091 956
            $this->collection = $name;
1092
        }
1093 956
    }
1094
1095
    /**
1096
     * Get whether or not the documents collection is capped.
1097
     *
1098
     * @return boolean
1099
     */
1100 4
    public function getCollectionCapped()
1101
    {
1102 4
        return $this->collectionCapped;
1103
    }
1104
1105
    /**
1106
     * Set whether or not the documents collection is capped.
1107
     *
1108
     * @param boolean $bool
1109
     */
1110 1
    public function setCollectionCapped($bool)
1111
    {
1112 1
        $this->collectionCapped = $bool;
1113 1
    }
1114
1115
    /**
1116
     * Get the collection size
1117
     *
1118
     * @return integer
1119
     */
1120 4
    public function getCollectionSize()
1121
    {
1122 4
        return $this->collectionSize;
1123
    }
1124
1125
    /**
1126
     * Set the collection size.
1127
     *
1128
     * @param integer $size
1129
     */
1130 1
    public function setCollectionSize($size)
1131
    {
1132 1
        $this->collectionSize = $size;
1133 1
    }
1134
1135
    /**
1136
     * Get the collection max.
1137
     *
1138
     * @return integer
1139
     */
1140 4
    public function getCollectionMax()
1141
    {
1142 4
        return $this->collectionMax;
1143
    }
1144
1145
    /**
1146
     * Set the collection max.
1147
     *
1148
     * @param integer $max
1149
     */
1150 1
    public function setCollectionMax($max)
1151
    {
1152 1
        $this->collectionMax = $max;
1153 1
    }
1154
1155
    /**
1156
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1157
     *
1158
     * @return boolean
1159
     */
1160
    public function isMappedToCollection()
1161
    {
1162
        return $this->collection ? true : false;
1163
    }
1164
1165
    /**
1166
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1167
     *
1168
     * @return boolean
1169
     */
1170 792
    public function isFile()
1171
    {
1172 792
        return $this->file ? true : false;
1173
    }
1174
1175
    /**
1176
     * Returns the file field name.
1177
     *
1178
     * @return string $file The file field name.
1179
     */
1180 369
    public function getFile()
1181
    {
1182 369
        return $this->file;
1183
    }
1184
1185
    /**
1186
     * Set the field name that stores the grid file.
1187
     *
1188
     * @param string $file
1189
     */
1190 370
    public function setFile($file)
1191
    {
1192 370
        $this->file = $file;
1193 370
    }
1194
1195
    /**
1196
     * Returns the distance field name.
1197
     *
1198
     * @return string $distance The distance field name.
1199
     */
1200
    public function getDistance()
1201
    {
1202
        return $this->distance;
1203
    }
1204
1205
    /**
1206
     * Set the field name that stores the distance.
1207
     *
1208
     * @param string $distance
1209
     */
1210 1
    public function setDistance($distance)
1211
    {
1212 1
        $this->distance = $distance;
1213 1
    }
1214
1215
    /**
1216
     * Map a field.
1217
     *
1218
     * @param array $mapping The mapping information.
1219
     *
1220
     * @return array
1221
     *
1222
     * @throws MappingException
1223
     */
1224 970
    public function mapField(array $mapping)
1225
    {
1226 970
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1227 10
            $mapping['fieldName'] = $mapping['name'];
1228
        }
1229 970
        if ( ! isset($mapping['fieldName'])) {
1230
            throw MappingException::missingFieldName($this->name);
1231
        }
1232 970
        if ( ! isset($mapping['name'])) {
1233 960
            $mapping['name'] = $mapping['fieldName'];
1234
        }
1235 970
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1236 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1237
        }
1238 969
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1239
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1240
        }
1241 969
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1242 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1243
        }
1244 968 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...
1245 618
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1246
        }
1247 968
        if (isset($mapping['collectionClass'])) {
1248 65 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...
1249 63
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1250
            }
1251 65
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1252
        }
1253 968
        if ( ! empty($mapping['collectionClass'])) {
1254 65
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1255 65
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1256 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1257
            }
1258
        }
1259
1260 967
        if (isset($mapping['discriminatorMap'])) {
1261 125
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1262 125 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...
1263 125
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1264
                }
1265
            }
1266
        }
1267
1268 967
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1269 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1270
        }
1271
1272 966
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1273
1274 966
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1275 646
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1276
        }
1277
1278 966
        if (isset($mapping['embedded'])) {
1279 603
            unset($mapping['cascade']);
1280 961
        } elseif (isset($mapping['cascade'])) {
1281 408
            $mapping['cascade'] = $cascades;
1282
        }
1283
1284 966
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1285 966
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1286 966
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1287 966
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1288 966
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1289
1290 966
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1291 63
            $mapping['file'] = true;
1292
        }
1293 966
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1294 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1295
        }
1296 966 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...
1297 63
            $this->file = $mapping['fieldName'];
1298 63
            $mapping['name'] = 'file';
1299
        }
1300 966 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...
1301 7
            $this->distance = $mapping['fieldName'];
1302
        }
1303 966
        if (isset($mapping['id']) && $mapping['id'] === true) {
1304 938
            $mapping['name'] = '_id';
1305 938
            $this->identifier = $mapping['fieldName'];
1306 938 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...
1307 919
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1308
            }
1309 938
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1310 938
            switch ($this->generatorType) {
1311 938
                case self::GENERATOR_TYPE_AUTO:
1312 864
                    $mapping['type'] = 'id';
1313 864
                    break;
1314
                default:
1315 153
                    if ( ! empty($this->generatorOptions['type'])) {
1316 52
                        $mapping['type'] = $this->generatorOptions['type'];
1317 101
                    } elseif (empty($mapping['type'])) {
1318 86
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1319
                    }
1320
            }
1321 938
            unset($this->generatorOptions['type']);
1322
        }
1323
1324 966
        if ( ! isset($mapping['nullable'])) {
1325 53
            $mapping['nullable'] = false;
1326
        }
1327
1328
        // Synchronize the "simple" and "storeAs" mapping information for backwards compatibility
1329 966
        if (isset($mapping['simple']) && ($mapping['simple'] === true || $mapping['simple'] === 'true')) {
1330 293
            $mapping['storeAs'] = ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1331 293
            @trigger_error('"simple" attribute of a reference is deprecated - use storeAs="id" instead.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1332
        }
1333
        // Provide the correct value for the "simple" field for backwards compatibility
1334 966
        if (isset($mapping['storeAs'])) {
1335 585
            $mapping['simple'] = $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1336
        }
1337
1338 966
        if (isset($mapping['reference'])
1339 966
            && isset($mapping['storeAs'])
1340 966
            && $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID
1341 966
            && ! isset($mapping['targetDocument'])
1342
        ) {
1343 3
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1344
        }
1345
1346 963
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1347 963
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1348 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1349
        }
1350
1351 959
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1352 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1353
        }
1354
1355 958 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...
1356 518
            $mapping['association'] = self::REFERENCE_ONE;
1357
        }
1358 958 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...
1359 460
            $mapping['association'] = self::REFERENCE_MANY;
1360
        }
1361 958 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...
1362 464
            $mapping['association'] = self::EMBED_ONE;
1363
        }
1364 958 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...
1365 505
            $mapping['association'] = self::EMBED_MANY;
1366
        }
1367
1368 958
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1369 133
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1370
        }
1371
1372
        /*
1373
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1374
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1375
        }
1376
        */
1377 958
        if (isset($mapping['version'])) {
1378 102
            $mapping['notSaved'] = true;
1379 102
            $this->setVersionMapping($mapping);
1380
        }
1381 958
        if (isset($mapping['lock'])) {
1382 27
            $mapping['notSaved'] = true;
1383 27
            $this->setLockMapping($mapping);
1384
        }
1385 958
        $mapping['isOwningSide'] = true;
1386 958
        $mapping['isInverseSide'] = false;
1387 958
        if (isset($mapping['reference'])) {
1388 590 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...
1389 92
                $mapping['isOwningSide'] = true;
1390 92
                $mapping['isInverseSide'] = false;
1391
            }
1392 590 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...
1393 295
                $mapping['isInverseSide'] = true;
1394 295
                $mapping['isOwningSide'] = false;
1395
            }
1396 590 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...
1397 67
                $mapping['isInverseSide'] = true;
1398 67
                $mapping['isOwningSide'] = false;
1399
            }
1400 590
            if (!isset($mapping['orphanRemoval'])) {
1401 565
                $mapping['orphanRemoval'] = false;
1402
            }
1403
        }
1404
1405 958
        if (!empty($mapping['prime']) && ($mapping['association'] !== self::REFERENCE_MANY || !$mapping['isInverseSide'])) {
1406
            throw MappingException::referencePrimersOnlySupportedForInverseReferenceMany($this->name, $mapping['fieldName']);
1407
        }
1408
1409 958
        $this->applyStorageStrategy($mapping);
1410
1411 957
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1412 957
        if (isset($mapping['association'])) {
1413 743
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1414
        }
1415
1416 957
        return $mapping;
1417
    }
1418
1419
    /**
1420
     * Validates the storage strategy of a mapping for consistency
1421
     * @param array $mapping
1422
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1423
     */
1424 958
    private function applyStorageStrategy(array &$mapping)
1425
    {
1426 958
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1427 940
            return;
1428
        }
1429
1430
        switch (true) {
1431 920
            case $mapping['type'] == 'int':
1432 919
            case $mapping['type'] == 'float':
1433 919
            case $mapping['type'] == 'increment':
1434 336
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1435 336
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1436 336
                break;
1437
1438 918
            case $mapping['type'] == 'many':
1439 618
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1440
                $allowedStrategies = [
1441 618
                    self::STORAGE_STRATEGY_PUSH_ALL,
1442 618
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1443 618
                    self::STORAGE_STRATEGY_SET,
1444 618
                    self::STORAGE_STRATEGY_SET_ARRAY,
1445 618
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1446 618
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1447
                ];
1448 618
                break;
1449
1450
            default:
1451 906
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1452 906
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1453
        }
1454
1455 920
        if (! isset($mapping['strategy'])) {
1456 909
            $mapping['strategy'] = $defaultStrategy;
1457
        }
1458
1459 920
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1460
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1461
        }
1462
1463 920
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1464 920
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1465 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1466
        }
1467 919
    }
1468
1469
    /**
1470
     * Map a MongoGridFSFile.
1471
     *
1472
     * @param array $mapping The mapping information.
1473
     */
1474
    public function mapFile(array $mapping)
1475
    {
1476
        $mapping['file'] = true;
1477
        $mapping['type'] = 'file';
1478
        $this->mapField($mapping);
1479
    }
1480
1481
    /**
1482
     * Map a single embedded document.
1483
     *
1484
     * @param array $mapping The mapping information.
1485
     */
1486 6
    public function mapOneEmbedded(array $mapping)
1487
    {
1488 6
        $mapping['embedded'] = true;
1489 6
        $mapping['type'] = 'one';
1490 6
        $this->mapField($mapping);
1491 5
    }
1492
1493
    /**
1494
     * Map a collection of embedded documents.
1495
     *
1496
     * @param array $mapping The mapping information.
1497
     */
1498 5
    public function mapManyEmbedded(array $mapping)
1499
    {
1500 5
        $mapping['embedded'] = true;
1501 5
        $mapping['type'] = 'many';
1502 5
        $this->mapField($mapping);
1503 5
    }
1504
1505
    /**
1506
     * Map a single document reference.
1507
     *
1508
     * @param array $mapping The mapping information.
1509
     */
1510 8
    public function mapOneReference(array $mapping)
1511
    {
1512 8
        $mapping['reference'] = true;
1513 8
        $mapping['type'] = 'one';
1514 8
        $this->mapField($mapping);
1515 8
    }
1516
1517
    /**
1518
     * Map a collection of document references.
1519
     *
1520
     * @param array $mapping The mapping information.
1521
     */
1522 8
    public function mapManyReference(array $mapping)
1523
    {
1524 8
        $mapping['reference'] = true;
1525 8
        $mapping['type'] = 'many';
1526 8
        $this->mapField($mapping);
1527 8
    }
1528
1529
    /**
1530
     * INTERNAL:
1531
     * Adds a field mapping without completing/validating it.
1532
     * This is mainly used to add inherited field mappings to derived classes.
1533
     *
1534
     * @param array $fieldMapping
1535
     */
1536 129
    public function addInheritedFieldMapping(array $fieldMapping)
1537
    {
1538 129
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1539
1540 129
        if (isset($fieldMapping['association'])) {
1541 77
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1542
        }
1543 129
    }
1544
1545
    /**
1546
     * INTERNAL:
1547
     * Adds an association mapping without completing/validating it.
1548
     * This is mainly used to add inherited association mappings to derived classes.
1549
     *
1550
     * @param array $mapping
1551
     *
1552
     * @return void
1553
     *
1554
     * @throws MappingException
1555
     */
1556 78
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1557
    {
1558 78
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1559 78
    }
1560
1561
    /**
1562
     * Checks whether the class has a mapped association with the given field name.
1563
     *
1564
     * @param string $fieldName
1565
     * @return boolean
1566
     */
1567 14
    public function hasReference($fieldName)
1568
    {
1569 14
        return isset($this->fieldMappings[$fieldName]['reference']);
1570
    }
1571
1572
    /**
1573
     * Checks whether the class has a mapped embed with the given field name.
1574
     *
1575
     * @param string $fieldName
1576
     * @return boolean
1577
     */
1578 5
    public function hasEmbed($fieldName)
1579
    {
1580 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1581
    }
1582
1583
    /**
1584
     * {@inheritDoc}
1585
     *
1586
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1587
     */
1588 7
    public function hasAssociation($fieldName)
1589
    {
1590 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1591
    }
1592
1593
    /**
1594
     * {@inheritDoc}
1595
     *
1596
     * Checks whether the class has a mapped reference or embed for the specified field and
1597
     * is a single valued association.
1598
     */
1599
    public function isSingleValuedAssociation($fieldName)
1600
    {
1601
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1602
    }
1603
1604
    /**
1605
     * {@inheritDoc}
1606
     *
1607
     * Checks whether the class has a mapped reference or embed for the specified field and
1608
     * is a collection valued association.
1609
     */
1610
    public function isCollectionValuedAssociation($fieldName)
1611
    {
1612
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1613
    }
1614
1615
    /**
1616
     * Checks whether the class has a mapped association for the specified field
1617
     * and if yes, checks whether it is a single-valued association (to-one).
1618
     *
1619
     * @param string $fieldName
1620
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1621
     */
1622
    public function isSingleValuedReference($fieldName)
1623
    {
1624
        return isset($this->fieldMappings[$fieldName]['association']) &&
1625
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1626
    }
1627
1628
    /**
1629
     * Checks whether the class has a mapped association for the specified field
1630
     * and if yes, checks whether it is a collection-valued association (to-many).
1631
     *
1632
     * @param string $fieldName
1633
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1634
     */
1635
    public function isCollectionValuedReference($fieldName)
1636
    {
1637
        return isset($this->fieldMappings[$fieldName]['association']) &&
1638
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1639
    }
1640
1641
    /**
1642
     * Checks whether the class has a mapped embedded document for the specified field
1643
     * and if yes, checks whether it is a single-valued association (to-one).
1644
     *
1645
     * @param string $fieldName
1646
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1647
     */
1648
    public function isSingleValuedEmbed($fieldName)
1649
    {
1650
        return isset($this->fieldMappings[$fieldName]['association']) &&
1651
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1652
    }
1653
1654
    /**
1655
     * Checks whether the class has a mapped embedded document for the specified field
1656
     * and if yes, checks whether it is a collection-valued association (to-many).
1657
     *
1658
     * @param string $fieldName
1659
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1660
     */
1661
    public function isCollectionValuedEmbed($fieldName)
1662
    {
1663
        return isset($this->fieldMappings[$fieldName]['association']) &&
1664
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1665
    }
1666
1667
    /**
1668
     * Sets the ID generator used to generate IDs for instances of this class.
1669
     *
1670
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1671
     */
1672 859
    public function setIdGenerator($generator)
1673
    {
1674 859
        $this->idGenerator = $generator;
1675 859
    }
1676
1677
    /**
1678
     * Casts the identifier to its portable PHP type.
1679
     *
1680
     * @param mixed $id
1681
     * @return mixed $id
1682
     */
1683 660
    public function getPHPIdentifierValue($id)
1684
    {
1685 660
        $idType = $this->fieldMappings[$this->identifier]['type'];
1686 660
        return Type::getType($idType)->convertToPHPValue($id);
1687
    }
1688
1689
    /**
1690
     * Casts the identifier to its database type.
1691
     *
1692
     * @param mixed $id
1693
     * @return mixed $id
1694
     */
1695 728
    public function getDatabaseIdentifierValue($id)
1696
    {
1697 728
        $idType = $this->fieldMappings[$this->identifier]['type'];
1698 728
        return Type::getType($idType)->convertToDatabaseValue($id);
1699
    }
1700
1701
    /**
1702
     * Sets the document identifier of a document.
1703
     *
1704
     * The value will be converted to a PHP type before being set.
1705
     *
1706
     * @param object $document
1707
     * @param mixed $id
1708
     */
1709 586
    public function setIdentifierValue($document, $id)
1710
    {
1711 586
        $id = $this->getPHPIdentifierValue($id);
1712 586
        $this->reflFields[$this->identifier]->setValue($document, $id);
1713 586
    }
1714
1715
    /**
1716
     * Gets the document identifier as a PHP type.
1717
     *
1718
     * @param object $document
1719
     * @return mixed $id
1720
     */
1721 677
    public function getIdentifierValue($document)
1722
    {
1723 677
        return $this->reflFields[$this->identifier]->getValue($document);
1724
    }
1725
1726
    /**
1727
     * {@inheritDoc}
1728
     *
1729
     * Since MongoDB only allows exactly one identifier field this is a proxy
1730
     * to {@see getIdentifierValue()} and returns an array with the identifier
1731
     * field as a key.
1732
     */
1733
    public function getIdentifierValues($object)
1734
    {
1735
        return array($this->identifier => $this->getIdentifierValue($object));
1736
    }
1737
1738
    /**
1739
     * Get the document identifier object as a database type.
1740
     *
1741
     * @param object $document
1742
     *
1743
     * @return \MongoId $id The MongoID object.
1744
     */
1745 36
    public function getIdentifierObject($document)
1746
    {
1747 36
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1748
    }
1749
1750
    /**
1751
     * Sets the specified field to the specified value on the given document.
1752
     *
1753
     * @param object $document
1754
     * @param string $field
1755
     * @param mixed $value
1756
     */
1757 11
    public function setFieldValue($document, $field, $value)
1758
    {
1759 11
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1760
            //property changes to an uninitialized proxy will not be tracked or persisted,
1761
            //so the proxy needs to be loaded first.
1762 1
            $document->__load();
1763
        }
1764
1765 11
        $this->reflFields[$field]->setValue($document, $value);
1766 11
    }
1767
1768
    /**
1769
     * Gets the specified field's value off the given document.
1770
     *
1771
     * @param object $document
1772
     * @param string $field
1773
     *
1774
     * @return mixed
1775
     */
1776 31
    public function getFieldValue($document, $field)
1777
    {
1778 31
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1779 1
            $document->__load();
1780
        }
1781
1782 31
        return $this->reflFields[$field]->getValue($document);
1783
    }
1784
1785
    /**
1786
     * Gets the mapping of a field.
1787
     *
1788
     * @param string $fieldName  The field name.
1789
     *
1790
     * @return array  The field mapping.
1791
     *
1792
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1793
     */
1794 102
    public function getFieldMapping($fieldName)
1795
    {
1796 102
        if ( ! isset($this->fieldMappings[$fieldName])) {
1797 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1798
        }
1799 100
        return $this->fieldMappings[$fieldName];
1800
    }
1801
1802
    /**
1803
     * Gets mappings of fields holding embedded document(s).
1804
     *
1805
     * @return array of field mappings
1806
     */
1807 630
    public function getEmbeddedFieldsMappings()
1808
    {
1809 630
        return array_filter(
1810 630
            $this->associationMappings,
1811
            function($assoc) { return ! empty($assoc['embedded']); }
1812
        );
1813
    }
1814
1815
    /**
1816
     * Gets the field mapping by its DB name.
1817
     * E.g. it returns identifier's mapping when called with _id.
1818
     *
1819
     * @param string $dbFieldName
1820
     *
1821
     * @return array
1822
     * @throws MappingException
1823
     */
1824 9
    public function getFieldMappingByDbFieldName($dbFieldName)
1825
    {
1826 9
        foreach ($this->fieldMappings as $mapping) {
1827 9
            if ($mapping['name'] == $dbFieldName) {
1828 9
                return $mapping;
1829
            }
1830
        }
1831
1832
        throw MappingException::mappingNotFoundByDbName($this->name, $dbFieldName);
1833
    }
1834
1835
    /**
1836
     * Check if the field is not null.
1837
     *
1838
     * @param string $fieldName  The field name
1839
     *
1840
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1841
     */
1842 1
    public function isNullable($fieldName)
1843
    {
1844 1
        $mapping = $this->getFieldMapping($fieldName);
1845 1
        if ($mapping !== false) {
1846 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1847
        }
1848
        return false;
1849
    }
1850
1851
    /**
1852
     * Checks whether the document has a discriminator field and value configured.
1853
     *
1854
     * @return boolean
1855
     */
1856 535
    public function hasDiscriminator()
1857
    {
1858 535
        return isset($this->discriminatorField, $this->discriminatorValue);
1859
    }
1860
1861
    /**
1862
     * Sets the type of Id generator to use for the mapped class.
1863
     *
1864
     * @param string $generatorType Generator type.
1865
     */
1866 375
    public function setIdGeneratorType($generatorType)
1867
    {
1868 375
        $this->generatorType = $generatorType;
1869 375
    }
1870
1871
    /**
1872
     * Sets the Id generator options.
1873
     *
1874
     * @param array $generatorOptions Generator options.
1875
     */
1876
    public function setIdGeneratorOptions($generatorOptions)
1877
    {
1878
        $this->generatorOptions = $generatorOptions;
1879
    }
1880
1881
    /**
1882
     * @return boolean
1883
     */
1884 636
    public function isInheritanceTypeNone()
1885
    {
1886 636
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1887
    }
1888
1889
    /**
1890
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1891
     *
1892
     * @return boolean
1893
     */
1894 368
    public function isInheritanceTypeSingleCollection()
1895
    {
1896 368
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1897
    }
1898
1899
    /**
1900
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1901
     *
1902
     * @return boolean
1903
     */
1904
    public function isInheritanceTypeCollectionPerClass()
1905
    {
1906
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1907
    }
1908
1909
    /**
1910
     * Sets the mapped subclasses of this class.
1911
     *
1912
     * @param string[] $subclasses The names of all mapped subclasses.
1913
     */
1914 2
    public function setSubclasses(array $subclasses)
1915
    {
1916 2
        foreach ($subclasses as $subclass) {
1917 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1918 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1919
            } else {
1920 2
                $this->subClasses[] = $subclass;
1921
            }
1922
        }
1923 2
    }
1924
1925
    /**
1926
     * Sets the parent class names.
1927
     * Assumes that the class names in the passed array are in the order:
1928
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1929
     *
1930
     * @param string[] $classNames
1931
     */
1932 917
    public function setParentClasses(array $classNames)
1933
    {
1934 917
        $this->parentClasses = $classNames;
1935
1936 917
        if (count($classNames) > 0) {
1937 113
            $this->rootDocumentName = array_pop($classNames);
1938
        }
1939 917
    }
1940
1941
    /**
1942
     * Checks whether the class will generate a new \MongoId instance for us.
1943
     *
1944
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1945
     */
1946
    public function isIdGeneratorAuto()
1947
    {
1948
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1949
    }
1950
1951
    /**
1952
     * Checks whether the class will use a collection to generate incremented identifiers.
1953
     *
1954
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1955
     */
1956
    public function isIdGeneratorIncrement()
1957
    {
1958
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1959
    }
1960
1961
    /**
1962
     * Checks whether the class will generate a uuid id.
1963
     *
1964
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1965
     */
1966
    public function isIdGeneratorUuid()
1967
    {
1968
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1969
    }
1970
1971
    /**
1972
     * Checks whether the class uses no id generator.
1973
     *
1974
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1975
     */
1976
    public function isIdGeneratorNone()
1977
    {
1978
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1979
    }
1980
1981
    /**
1982
     * Sets the version field mapping used for versioning. Sets the default
1983
     * value to use depending on the column type.
1984
     *
1985
     * @param array $mapping   The version field mapping array
1986
     *
1987
     * @throws LockException
1988
     */
1989 102
    public function setVersionMapping(array &$mapping)
1990
    {
1991 102
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
1992 1
            throw LockException::invalidVersionFieldType($mapping['type']);
1993
        }
1994
1995 101
        $this->isVersioned  = true;
1996 101
        $this->versionField = $mapping['fieldName'];
1997 101
    }
1998
1999
    /**
2000
     * Sets whether this class is to be versioned for optimistic locking.
2001
     *
2002
     * @param boolean $bool
2003
     */
2004 369
    public function setVersioned($bool)
2005
    {
2006 369
        $this->isVersioned = $bool;
2007 369
    }
2008
2009
    /**
2010
     * Sets the name of the field that is to be used for versioning if this class is
2011
     * versioned for optimistic locking.
2012
     *
2013
     * @param string $versionField
2014
     */
2015 369
    public function setVersionField($versionField)
2016
    {
2017 369
        $this->versionField = $versionField;
2018 369
    }
2019
2020
    /**
2021
     * Sets the version field mapping used for versioning. Sets the default
2022
     * value to use depending on the column type.
2023
     *
2024
     * @param array $mapping   The version field mapping array
2025
     *
2026
     * @throws \Doctrine\ODM\MongoDB\LockException
2027
     */
2028 27
    public function setLockMapping(array &$mapping)
2029
    {
2030 27
        if ($mapping['type'] !== 'int') {
2031 1
            throw LockException::invalidLockFieldType($mapping['type']);
2032
        }
2033
2034 26
        $this->isLockable = true;
2035 26
        $this->lockField = $mapping['fieldName'];
2036 26
    }
2037
2038
    /**
2039
     * Sets whether this class is to allow pessimistic locking.
2040
     *
2041
     * @param boolean $bool
2042
     */
2043
    public function setLockable($bool)
2044
    {
2045
        $this->isLockable = $bool;
2046
    }
2047
2048
    /**
2049
     * Sets the name of the field that is to be used for storing whether a document
2050
     * is currently locked or not.
2051
     *
2052
     * @param string $lockField
2053
     */
2054
    public function setLockField($lockField)
2055
    {
2056
        $this->lockField = $lockField;
2057
    }
2058
2059
    /**
2060
     * Marks this class as read only, no change tracking is applied to it.
2061
     */
2062 9
    public function markReadOnly()
2063
    {
2064 9
        $this->isReadOnly = true;
2065 9
    }
2066
2067
    /**
2068
     * {@inheritDoc}
2069
     */
2070
    public function getFieldNames()
2071
    {
2072
        return array_keys($this->fieldMappings);
2073
    }
2074
2075
    /**
2076
     * {@inheritDoc}
2077
     */
2078
    public function getAssociationNames()
2079
    {
2080
        return array_keys($this->associationMappings);
2081
    }
2082
2083
    /**
2084
     * {@inheritDoc}
2085
     */
2086 22
    public function getTypeOfField($fieldName)
2087
    {
2088 22
        return isset($this->fieldMappings[$fieldName]) ?
2089 22
            $this->fieldMappings[$fieldName]['type'] : null;
2090
    }
2091
2092
    /**
2093
     * {@inheritDoc}
2094
     */
2095 6
    public function getAssociationTargetClass($assocName)
2096
    {
2097 6
        if ( ! isset($this->associationMappings[$assocName])) {
2098 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2099
        }
2100
2101 3
        return $this->associationMappings[$assocName]['targetDocument'];
2102
    }
2103
2104
    /**
2105
     * Retrieve the collectionClass associated with an association
2106
     *
2107
     * @param string $assocName
2108
     */
2109 2
    public function getAssociationCollectionClass($assocName)
2110
    {
2111 2
        if ( ! isset($this->associationMappings[$assocName])) {
2112
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2113
        }
2114
2115 2
        if ( ! array_key_exists('collectionClass', $this->associationMappings[$assocName])) {
2116
            throw new InvalidArgumentException("collectionClass can only be applied to 'embedMany' and 'referenceMany' associations.");
2117
        }
2118
2119 2
        return $this->associationMappings[$assocName]['collectionClass'];
2120
    }
2121
2122
    /**
2123
     * {@inheritDoc}
2124
     */
2125
    public function isAssociationInverseSide($fieldName)
2126
    {
2127
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2128
    }
2129
2130
    /**
2131
     * {@inheritDoc}
2132
     */
2133
    public function getAssociationMappedByTargetField($fieldName)
2134
    {
2135
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2136
    }
2137
}
2138