Completed
Pull Request — master (#1620)
by Maciej
09:58
created

ClassMetadataInfo::addLifecycleCallback()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 4
nc 2
nop 2
crap 3
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 how MongoDB clients route read operations to the members of a replica set.
193
     */
194
    public $readPreference;
195
196
    /**
197
     * READ-ONLY Associated with readPreference Allows to specify criteria so that your application can target read
198
     * operations to specific members, based on custom parameters.
199
     */
200
    public $readPreferenceTags;
201
202
    /**
203
     * READ-ONLY: Describes the level of acknowledgement requested from MongoDB for write operations.
204
     */
205
    public $writeConcern;
206
207
    /**
208
     * READ-ONLY: The field name of the document identifier.
209
     */
210
    public $identifier;
211
212
    /**
213
     * READ-ONLY: The field that stores a file reference and indicates the
214
     * document is a file and should be stored on the MongoGridFS.
215
     */
216
    public $file;
217
218
    /**
219
     * READ-ONLY: The field that stores the calculated distance when performing geo spatial
220
     * queries.
221
     */
222
    public $distance;
223
224
    /**
225
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
226
     *
227
     * @deprecated in version 1.2 and will be removed in 2.0.
228
     */
229
    public $slaveOkay;
230
231
    /**
232
     * READ-ONLY: The array of indexes for the document collection.
233
     */
234
    public $indexes = array();
235
236
    /**
237
     * READ-ONLY: Keys and options describing shard key. Only for sharded collections.
238
     */
239
    public $shardKey;
240
241
    /**
242
     * READ-ONLY: Whether or not queries on this document should require indexes.
243
     *
244
     * @deprecated property was deprecated in 1.2 and will be removed in 2.0
245
     */
246
    public $requireIndexes = false;
247
248
    /**
249
     * READ-ONLY: The name of the document class.
250
     */
251
    public $name;
252
253
    /**
254
     * READ-ONLY: The namespace the document class is contained in.
255
     *
256
     * @var string
257
     * @todo Not really needed. Usage could be localized.
258
     */
259
    public $namespace;
260
261
    /**
262
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
263
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
264
     * as {@link $documentName}.
265
     *
266
     * @var string
267
     */
268
    public $rootDocumentName;
269
270
    /**
271
     * The name of the custom repository class used for the document class.
272
     * (Optional).
273
     *
274
     * @var string
275
     */
276
    public $customRepositoryClassName;
277
278
    /**
279
     * READ-ONLY: The names of the parent classes (ancestors).
280
     *
281
     * @var array
282
     */
283
    public $parentClasses = array();
284
285
    /**
286
     * READ-ONLY: The names of all subclasses (descendants).
287
     *
288
     * @var array
289
     */
290
    public $subClasses = array();
291
292
    /**
293
     * The ReflectionProperty instances of the mapped class.
294
     *
295
     * @var \ReflectionProperty[]
296
     */
297
    public $reflFields = array();
298
299
    /**
300
     * READ-ONLY: The inheritance mapping type used by the class.
301
     *
302
     * @var integer
303
     */
304
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
305
306
    /**
307
     * READ-ONLY: The Id generator type used by the class.
308
     *
309
     * @var string
310
     */
311
    public $generatorType = self::GENERATOR_TYPE_AUTO;
312
313
    /**
314
     * READ-ONLY: The Id generator options.
315
     *
316
     * @var array
317
     */
318
    public $generatorOptions = array();
319
320
    /**
321
     * READ-ONLY: The ID generator used for generating IDs for this class.
322
     *
323
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
324
     */
325
    public $idGenerator;
326
327
    /**
328
     * READ-ONLY: The field mappings of the class.
329
     * Keys are field names and values are mapping definitions.
330
     *
331
     * The mapping definition array has the following values:
332
     *
333
     * - <b>fieldName</b> (string)
334
     * The name of the field in the Document.
335
     *
336
     * - <b>id</b> (boolean, optional)
337
     * Marks the field as the primary key of the document. Multiple fields of an
338
     * document can have the id attribute, forming a composite key.
339
     *
340
     * @var array
341
     */
342
    public $fieldMappings = array();
343
344
    /**
345
     * READ-ONLY: The association mappings of the class.
346
     * Keys are field names and values are mapping definitions.
347
     *
348
     * @var array
349
     */
350
    public $associationMappings = array();
351
352
    /**
353
     * READ-ONLY: Array of fields to also load with a given method.
354
     *
355
     * @var array
356
     */
357
    public $alsoLoadMethods = array();
358
359
    /**
360
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
361
     *
362
     * @var array
363
     */
364
    public $lifecycleCallbacks = array();
365
366
    /**
367
     * READ-ONLY: The discriminator value of this class.
368
     *
369
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
370
     * where a discriminator field is used.</b>
371
     *
372
     * @var mixed
373
     * @see discriminatorField
374
     */
375
    public $discriminatorValue;
376
377
    /**
378
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
379
     *
380
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
381
     * where a discriminator field is used.</b>
382
     *
383
     * @var mixed
384
     * @see discriminatorField
385
     */
386
    public $discriminatorMap = array();
387
388
    /**
389
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
390
     * inheritance mapping.
391
     *
392
     * @var string
393
     */
394
    public $discriminatorField;
395
396
    /**
397
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
398
     *
399
     * @var string
400
     * @see discriminatorField
401
     */
402
    public $defaultDiscriminatorValue;
403
404
    /**
405
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
406
     *
407
     * @var boolean
408
     */
409
    public $isMappedSuperclass = false;
410
411
    /**
412
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
413
     *
414
     * @var boolean
415
     */
416
    public $isEmbeddedDocument = false;
417
418
    /**
419
     * READ-ONLY: Whether this class describes the mapping of an aggregation result document.
420
     *
421
     * @var boolean
422
     */
423
    public $isQueryResultDocument = false;
424
425
    /**
426
     * READ-ONLY: The policy used for change-tracking on entities of this class.
427
     *
428
     * @var integer
429
     */
430
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
431
432
    /**
433
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
434
     * with optimistic locking.
435
     *
436
     * @var boolean $isVersioned
437
     */
438
    public $isVersioned;
439
440
    /**
441
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
442
     *
443
     * @var mixed $versionField
444
     */
445
    public $versionField;
446
447
    /**
448
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
449
     * locking.
450
     *
451
     * @var boolean $isLockable
452
     */
453
    public $isLockable;
454
455
    /**
456
     * READ-ONLY: The name of the field which is used for locking a document.
457
     *
458
     * @var mixed $lockField
459
     */
460
    public $lockField;
461
462
    /**
463
     * The ReflectionClass instance of the mapped class.
464
     *
465
     * @var \ReflectionClass
466
     */
467
    public $reflClass;
468
469
    /**
470
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
471
     * metadata of the class with the given name.
472
     *
473
     * @param string $documentName The name of the document class the new instance is used for.
474
     */
475 979
    public function __construct($documentName)
476
    {
477 979
        $this->name = $documentName;
478 979
        $this->rootDocumentName = $documentName;
479 979
    }
480
481
    /**
482
     * {@inheritDoc}
483
     */
484 907
    public function getReflectionClass()
485
    {
486 907
        if ( ! $this->reflClass) {
487 2
            $this->reflClass = new \ReflectionClass($this->name);
488
        }
489
490 907
        return $this->reflClass;
491
    }
492
493
    /**
494
     * {@inheritDoc}
495
     */
496 330
    public function isIdentifier($fieldName)
497
    {
498 330
        return $this->identifier === $fieldName;
499
    }
500
501
    /**
502
     * INTERNAL:
503
     * Sets the mapped identifier field of this class.
504
     *
505
     * @param string $identifier
506
     */
507 374
    public function setIdentifier($identifier)
508
    {
509 374
        $this->identifier = $identifier;
510 374
    }
511
512
    /**
513
     * {@inheritDoc}
514
     *
515
     * Since MongoDB only allows exactly one identifier field
516
     * this will always return an array with only one value
517
     */
518 40
    public function getIdentifier()
519
    {
520 40
        return array($this->identifier);
521
    }
522
523
    /**
524
     * {@inheritDoc}
525
     *
526
     * Since MongoDB only allows exactly one identifier field
527
     * this will always return an array with only one value
528
     */
529 98
    public function getIdentifierFieldNames()
530
    {
531 98
        return array($this->identifier);
532
    }
533
534
    /**
535
     * {@inheritDoc}
536
     */
537 561
    public function hasField($fieldName)
538
    {
539 561
        return isset($this->fieldMappings[$fieldName]);
540
    }
541
542
    /**
543
     * Sets the inheritance type used by the class and it's subclasses.
544
     *
545
     * @param integer $type
546
     */
547 390
    public function setInheritanceType($type)
548
    {
549 390
        $this->inheritanceType = $type;
550 390
    }
551
552
    /**
553
     * Checks whether a mapped field is inherited from an entity superclass.
554
     *
555
     * @param  string $fieldName
556
     *
557
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
558
     */
559 907
    public function isInheritedField($fieldName)
560
    {
561 907
        return isset($this->fieldMappings[$fieldName]['inherited']);
562
    }
563
564
    /**
565
     * Registers a custom repository class for the document class.
566
     *
567
     * @param string $repositoryClassName The class name of the custom repository.
568
     */
569 322
    public function setCustomRepositoryClass($repositoryClassName)
570
    {
571 322
        if ($this->isEmbeddedDocument || $this->isQueryResultDocument) {
572
            return;
573
        }
574
575 322 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...
576 4
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
577
        }
578
579 322
        $this->customRepositoryClassName = $repositoryClassName;
580 322
    }
581
582
    /**
583
     * Dispatches the lifecycle event of the given document by invoking all
584
     * registered callbacks.
585
     *
586
     * @param string $event     Lifecycle event
587
     * @param object $document  Document on which the event occurred
588
     * @param array  $arguments Arguments to pass to all callbacks
589
     * @throws \InvalidArgumentException if document class is not this class or
590
     *                                   a Proxy of this class
591
     */
592 663
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
593
    {
594 663
        if ( ! $document instanceof $this->name) {
595 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
596
        }
597
598 662
        if (empty($this->lifecycleCallbacks[$event])) {
599 648
            return;
600
        }
601
602 199
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
603 199
            if ($arguments !== null) {
604 198
                call_user_func_array(array($document, $callback), $arguments);
605
            } else {
606 199
                $document->$callback();
607
            }
608
        }
609 199
    }
610
611
    /**
612
     * Checks whether the class has callbacks registered for a lifecycle event.
613
     *
614
     * @param string $event Lifecycle event
615
     *
616
     * @return boolean
617
     */
618
    public function hasLifecycleCallbacks($event)
619
    {
620
        return ! empty($this->lifecycleCallbacks[$event]);
621
    }
622
623
    /**
624
     * Gets the registered lifecycle callbacks for an event.
625
     *
626
     * @param string $event
627
     * @return array
628
     */
629
    public function getLifecycleCallbacks($event)
630
    {
631
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
632
    }
633
634
    /**
635
     * Adds a lifecycle callback for documents of this class.
636
     *
637
     * If the callback is already registered, this is a NOOP.
638
     *
639
     * @param string $callback
640
     * @param string $event
641
     */
642 300
    public function addLifecycleCallback($callback, $event)
643
    {
644 300
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
645 1
            return;
646
        }
647
648 300
        $this->lifecycleCallbacks[$event][] = $callback;
649 300
    }
650
651
    /**
652
     * Sets the lifecycle callbacks for documents of this class.
653
     *
654
     * Any previously registered callbacks are overwritten.
655
     *
656
     * @param array $callbacks
657
     */
658 373
    public function setLifecycleCallbacks(array $callbacks)
659
    {
660 373
        $this->lifecycleCallbacks = $callbacks;
661 373
    }
662
663
    /**
664
     * Registers a method for loading document data before field hydration.
665
     *
666
     * Note: A method may be registered multiple times for different fields.
667
     * it will be invoked only once for the first field found.
668
     *
669
     * @param string       $method Method name
670
     * @param array|string $fields Database field name(s)
671
     */
672 15
    public function registerAlsoLoadMethod($method, $fields)
673
    {
674 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
675 15
    }
676
677
    /**
678
     * Sets the AlsoLoad methods for documents of this class.
679
     *
680
     * Any previously registered methods are overwritten.
681
     *
682
     * @param array $methods
683
     */
684 373
    public function setAlsoLoadMethods(array $methods)
685
    {
686 373
        $this->alsoLoadMethods = $methods;
687 373
    }
688
689
    /**
690
     * Sets the discriminator field.
691
     *
692
     * The field name is the the unmapped database field. Discriminator values
693
     * are only used to discern the hydration class and are not mapped to class
694
     * properties.
695
     *
696
     * @param string $discriminatorField
697
     *
698
     * @throws MappingException If the discriminator field conflicts with the
699
     *                          "name" attribute of a mapped field.
700
     */
701 403
    public function setDiscriminatorField($discriminatorField)
702
    {
703 403
        if ($discriminatorField === null) {
704 330
            $this->discriminatorField = null;
705
706 330
            return;
707
        }
708
709
        // Handle array argument with name/fieldName keys for BC
710 130
        if (is_array($discriminatorField)) {
711
            if (isset($discriminatorField['name'])) {
712
                $discriminatorField = $discriminatorField['name'];
713
            } elseif (isset($discriminatorField['fieldName'])) {
714
                $discriminatorField = $discriminatorField['fieldName'];
715
            }
716
        }
717
718 130
        foreach ($this->fieldMappings as $fieldMapping) {
719 4
            if ($discriminatorField == $fieldMapping['name']) {
720 4
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
721
            }
722
        }
723
724 129
        $this->discriminatorField = $discriminatorField;
725 129
    }
726
727
    /**
728
     * Sets the discriminator values used by this class.
729
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
730
     *
731
     * @param array $map
732
     *
733
     * @throws MappingException
734
     */
735 396
    public function setDiscriminatorMap(array $map)
736
    {
737 396
        foreach ($map as $value => $className) {
738 125
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
739 91
                $className = $this->namespace . '\\' . $className;
740
            }
741 125
            $this->discriminatorMap[$value] = $className;
742 125
            if ($this->name == $className) {
743 117
                $this->discriminatorValue = $value;
744
            } else {
745 120
                if ( ! class_exists($className)) {
746
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
747
                }
748 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...
749 125
                    $this->subClasses[] = $className;
750
                }
751
            }
752
        }
753 396
    }
754
755
    /**
756
     * Sets the default discriminator value to be used for this class
757
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
758
     *
759
     * @param string $defaultDiscriminatorValue
760
     *
761
     * @throws MappingException
762
     */
763 380
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
764
    {
765 380
        if ($defaultDiscriminatorValue === null) {
766 373
            $this->defaultDiscriminatorValue = null;
767
768 373
            return;
769
        }
770
771 60
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
772
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
773
        }
774
775 60
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
776 60
    }
777
778
    /**
779
     * Sets the discriminator value for this class.
780
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
781
     * collection.
782
     *
783
     * @param string $value
784
     */
785 3
    public function setDiscriminatorValue($value)
786
    {
787 3
        $this->discriminatorMap[$value] = $this->name;
788 3
        $this->discriminatorValue = $value;
789 3
    }
790
791
    /**
792
     * Sets the slaveOkay option applied to collections for this class.
793
     *
794
     * @param boolean|null $slaveOkay
795
     *
796
     * @deprecated in version 1.2 and will be removed in 2.0.
797
     */
798 3
    public function setSlaveOkay($slaveOkay)
799
    {
800 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...
801 3
            sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.'),
802 3
            E_USER_DEPRECATED
803
        );
804 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...
805 3
    }
806
807
    /**
808
     * Add a index for this Document.
809
     *
810
     * @param array $keys Array of keys for the index.
811
     * @param array $options Array of options for the index.
812
     */
813 232
    public function addIndex($keys, array $options = array())
814
    {
815 232
        $this->indexes[] = array(
816 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...
817 232
                if ($value == 1 || $value == -1) {
818 63
                    return (int) $value;
819
                }
820 224
                if (is_string($value)) {
821 224
                    $lower = strtolower($value);
822 224
                    if ($lower === 'asc') {
823 217
                        return 1;
824 11
                    } elseif ($lower === 'desc') {
825 4
                        return -1;
826
                    }
827
                }
828 7
                return $value;
829 232
            }, $keys),
830 232
            'options' => $options
831
        );
832 232
    }
833
834
    /**
835
     * Set whether or not queries on this document should require indexes.
836
     *
837
     * @param bool $requireIndexes
838
     *
839
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
840
     */
841 898
    public function setRequireIndexes($requireIndexes)
842
    {
843 898
        @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...
844 898
            'requireIndexes was deprecated in version 1.2 and will be removed altogether in 2.0.',
845 898
            E_USER_DEPRECATED
846
        );
847 898
        $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...
848 898
    }
849
850
    /**
851
     * Returns the array of indexes for this Document.
852
     *
853
     * @return array $indexes The array of indexes.
854
     */
855 54
    public function getIndexes()
856
    {
857 54
        return $this->indexes;
858
    }
859
860
    /**
861
     * Checks whether this document has indexes or not.
862
     *
863
     * @return boolean
864
     */
865
    public function hasIndexes()
866
    {
867
        return $this->indexes ? true : false;
868
    }
869
870
    /**
871
     * Set shard key for this Document.
872
     *
873
     * @param array $keys Array of document keys.
874
     * @param array $options Array of sharding options.
875
     *
876
     * @throws MappingException
877
     */
878 77
    public function setShardKey(array $keys, array $options = array())
879
    {
880 77
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
881 2
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
882
        }
883
884 77
        if ($this->isEmbeddedDocument) {
885 2
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
886
        }
887
888 75
        foreach (array_keys($keys) as $field) {
889 75
            if (! isset($this->fieldMappings[$field])) {
890 68
                continue;
891
            }
892
893 7
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
894 3
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
895
            }
896
897 4
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
898 4
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
899
            }
900
        }
901
902 71
        $this->shardKey = array(
903 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...
904 71
                if ($value == 1 || $value == -1) {
905 6
                    return (int) $value;
906
                }
907 70
                if (is_string($value)) {
908 70
                    $lower = strtolower($value);
909 70
                    if ($lower === 'asc') {
910 69
                        return 1;
911 53
                    } elseif ($lower === 'desc') {
912
                        return -1;
913
                    }
914
                }
915 53
                return $value;
916 71
            }, $keys),
917 71
            'options' => $options
918
        );
919 71
    }
920
921
    /**
922
     * @return array
923
     */
924 18
    public function getShardKey()
925
    {
926 18
        return $this->shardKey;
927
    }
928
929
    /**
930
     * Checks whether this document has shard key or not.
931
     *
932
     * @return bool
933
     */
934 602
    public function isSharded()
935
    {
936 602
        return $this->shardKey ? true : false;
937
    }
938
939
    /**
940
     * Sets the read preference used by this class.
941
     *
942
     * @param string $readPreference
943
     * @param array|null $tags
944
     */
945 377
    public function setReadPreference($readPreference, $tags)
946
    {
947 377
        $this->readPreference = $readPreference;
948 377
        $this->readPreferenceTags = $tags;
949 377
    }
950
951
    /**
952
     * Sets the write concern used by this class.
953
     *
954
     * @param string $writeConcern
955
     */
956 387
    public function setWriteConcern($writeConcern)
957
    {
958 387
        $this->writeConcern = $writeConcern;
959 387
    }
960
961
    /**
962
     * @return string
963
     */
964 12
    public function getWriteConcern()
965
    {
966 12
        return $this->writeConcern;
967
    }
968
969
    /**
970
     * Whether there is a write concern configured for this class.
971
     *
972
     * @return bool
973
     */
974 607
    public function hasWriteConcern()
975
    {
976 607
        return $this->writeConcern !== null;
977
    }
978
979
    /**
980
     * Sets the change tracking policy used by this class.
981
     *
982
     * @param integer $policy
983
     */
984 378
    public function setChangeTrackingPolicy($policy)
985
    {
986 378
        $this->changeTrackingPolicy = $policy;
987 378
    }
988
989
    /**
990
     * Whether the change tracking policy of this class is "deferred explicit".
991
     *
992
     * @return boolean
993
     */
994 73
    public function isChangeTrackingDeferredExplicit()
995
    {
996 73
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
997
    }
998
999
    /**
1000
     * Whether the change tracking policy of this class is "deferred implicit".
1001
     *
1002
     * @return boolean
1003
     */
1004 627
    public function isChangeTrackingDeferredImplicit()
1005
    {
1006 627
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1007
    }
1008
1009
    /**
1010
     * Whether the change tracking policy of this class is "notify".
1011
     *
1012
     * @return boolean
1013
     */
1014 347
    public function isChangeTrackingNotify()
1015
    {
1016 347
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1017
    }
1018
1019
    /**
1020
     * Gets the ReflectionProperties of the mapped class.
1021
     *
1022
     * @return array An array of ReflectionProperty instances.
1023
     */
1024 98
    public function getReflectionProperties()
1025
    {
1026 98
        return $this->reflFields;
1027
    }
1028
1029
    /**
1030
     * Gets a ReflectionProperty for a specific field of the mapped class.
1031
     *
1032
     * @param string $name
1033
     *
1034
     * @return \ReflectionProperty
1035
     */
1036
    public function getReflectionProperty($name)
1037
    {
1038
        return $this->reflFields[$name];
1039
    }
1040
1041
    /**
1042
     * {@inheritDoc}
1043
     */
1044 913
    public function getName()
1045
    {
1046 913
        return $this->name;
1047
    }
1048
1049
    /**
1050
     * The namespace this Document class belongs to.
1051
     *
1052
     * @return string $namespace The namespace name.
1053
     */
1054
    public function getNamespace()
1055
    {
1056
        return $this->namespace;
1057
    }
1058
1059
    /**
1060
     * Returns the database this Document is mapped to.
1061
     *
1062
     * @return string $db The database name.
1063
     */
1064 832
    public function getDatabase()
1065
    {
1066 832
        return $this->db;
1067
    }
1068
1069
    /**
1070
     * Set the database this Document is mapped to.
1071
     *
1072
     * @param string $db The database name
1073
     */
1074 104
    public function setDatabase($db)
1075
    {
1076 104
        $this->db = $db;
1077 104
    }
1078
1079
    /**
1080
     * Get the collection this Document is mapped to.
1081
     *
1082
     * @return string $collection The collection name.
1083
     */
1084 837
    public function getCollection()
1085
    {
1086 837
        return $this->collection;
1087
    }
1088
1089
    /**
1090
     * Sets the collection this Document is mapped to.
1091
     *
1092
     * @param array|string $name
1093
     *
1094
     * @throws \InvalidArgumentException
1095
     */
1096 943
    public function setCollection($name)
1097
    {
1098 943
        if (is_array($name)) {
1099
            if ( ! isset($name['name'])) {
1100
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
1101
            }
1102
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
1103
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
1104
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
1105
            $this->collection = $name['name'];
1106
        } else {
1107 943
            $this->collection = $name;
1108
        }
1109 943
    }
1110
1111
    /**
1112
     * Get whether or not the documents collection is capped.
1113
     *
1114
     * @return boolean
1115
     */
1116 4
    public function getCollectionCapped()
1117
    {
1118 4
        return $this->collectionCapped;
1119
    }
1120
1121
    /**
1122
     * Set whether or not the documents collection is capped.
1123
     *
1124
     * @param boolean $bool
1125
     */
1126 1
    public function setCollectionCapped($bool)
1127
    {
1128 1
        $this->collectionCapped = $bool;
1129 1
    }
1130
1131
    /**
1132
     * Get the collection size
1133
     *
1134
     * @return integer
1135
     */
1136 4
    public function getCollectionSize()
1137
    {
1138 4
        return $this->collectionSize;
1139
    }
1140
1141
    /**
1142
     * Set the collection size.
1143
     *
1144
     * @param integer $size
1145
     */
1146 1
    public function setCollectionSize($size)
1147
    {
1148 1
        $this->collectionSize = $size;
1149 1
    }
1150
1151
    /**
1152
     * Get the collection max.
1153
     *
1154
     * @return integer
1155
     */
1156 4
    public function getCollectionMax()
1157
    {
1158 4
        return $this->collectionMax;
1159
    }
1160
1161
    /**
1162
     * Set the collection max.
1163
     *
1164
     * @param integer $max
1165
     */
1166 1
    public function setCollectionMax($max)
1167
    {
1168 1
        $this->collectionMax = $max;
1169 1
    }
1170
1171
    /**
1172
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1173
     *
1174
     * @return boolean
1175
     */
1176
    public function isMappedToCollection()
1177
    {
1178
        return $this->collection ? true : false;
1179
    }
1180
1181
    /**
1182
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1183
     *
1184
     * @return boolean
1185
     */
1186 778
    public function isFile()
1187
    {
1188 778
        return $this->file ? true : false;
1189
    }
1190
1191
    /**
1192
     * Returns the file field name.
1193
     *
1194
     * @return string $file The file field name.
1195
     */
1196 373
    public function getFile()
1197
    {
1198 373
        return $this->file;
1199
    }
1200
1201
    /**
1202
     * Set the field name that stores the grid file.
1203
     *
1204
     * @param string $file
1205
     */
1206 374
    public function setFile($file)
1207
    {
1208 374
        $this->file = $file;
1209 374
    }
1210
1211
    /**
1212
     * Returns the distance field name.
1213
     *
1214
     * @return string $distance The distance field name.
1215
     */
1216
    public function getDistance()
1217
    {
1218
        return $this->distance;
1219
    }
1220
1221
    /**
1222
     * Set the field name that stores the distance.
1223
     *
1224
     * @param string $distance
1225
     */
1226 1
    public function setDistance($distance)
1227
    {
1228 1
        $this->distance = $distance;
1229 1
    }
1230
1231
    /**
1232
     * Map a field.
1233
     *
1234
     * @param array $mapping The mapping information.
1235
     *
1236
     * @return array
1237
     *
1238
     * @throws MappingException
1239
     */
1240 957
    public function mapField(array $mapping)
1241
    {
1242 957
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1243 10
            $mapping['fieldName'] = $mapping['name'];
1244
        }
1245 957
        if ( ! isset($mapping['fieldName'])) {
1246
            throw MappingException::missingFieldName($this->name);
1247
        }
1248 957
        if ( ! isset($mapping['name'])) {
1249 947
            $mapping['name'] = $mapping['fieldName'];
1250
        }
1251 957
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1252 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1253
        }
1254 956
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1255
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1256
        }
1257 956
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1258 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1259
        }
1260 955 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...
1261 620
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1262
        }
1263 955
        if (isset($mapping['collectionClass'])) {
1264 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...
1265 63
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1266
            }
1267 65
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1268
        }
1269 955
        if ( ! empty($mapping['collectionClass'])) {
1270 65
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1271 65
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1272 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1273
            }
1274
        }
1275
1276 954
        if (isset($mapping['discriminatorMap'])) {
1277 123
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1278 123 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...
1279 123
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1280
                }
1281
            }
1282
        }
1283
1284 954
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1285 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1286
        }
1287
1288 953
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1289
1290 953
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1291 647
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1292
        }
1293
1294 953
        if (isset($mapping['embedded'])) {
1295 604
            unset($mapping['cascade']);
1296 948
        } elseif (isset($mapping['cascade'])) {
1297 411
            $mapping['cascade'] = $cascades;
1298
        }
1299
1300 953
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1301 953
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1302 953
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1303 953
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1304 953
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1305
1306 953
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1307 63
            $mapping['file'] = true;
1308
        }
1309 953
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1310 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1311
        }
1312 953 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...
1313 63
            $this->file = $mapping['fieldName'];
1314 63
            $mapping['name'] = 'file';
1315
        }
1316 953 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...
1317 7
            $this->distance = $mapping['fieldName'];
1318
        }
1319 953
        if (isset($mapping['id']) && $mapping['id'] === true) {
1320 925
            $mapping['name'] = '_id';
1321 925
            $this->identifier = $mapping['fieldName'];
1322 925 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...
1323 906
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1324
            }
1325 925
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1326 925
            switch ($this->generatorType) {
1327 925
                case self::GENERATOR_TYPE_AUTO:
1328 851
                    $mapping['type'] = 'id';
1329 851
                    break;
1330
                default:
1331 153
                    if ( ! empty($this->generatorOptions['type'])) {
1332 52
                        $mapping['type'] = $this->generatorOptions['type'];
1333 101
                    } elseif (empty($mapping['type'])) {
1334 86
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1335
                    }
1336
            }
1337 925
            unset($this->generatorOptions['type']);
1338
        }
1339
1340 953
        if ( ! isset($mapping['nullable'])) {
1341 53
            $mapping['nullable'] = false;
1342
        }
1343
1344
        // Synchronize the "simple" and "storeAs" mapping information for backwards compatibility
1345 953
        if (isset($mapping['simple']) && ($mapping['simple'] === true || $mapping['simple'] === 'true')) {
1346 296
            $mapping['storeAs'] = ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1347 296
            @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...
1348
        }
1349
        // Provide the correct value for the "simple" field for backwards compatibility
1350 953
        if (isset($mapping['storeAs'])) {
1351 587
            $mapping['simple'] = $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1352
        }
1353
1354 953
        if (isset($mapping['reference'])
1355 953
            && isset($mapping['storeAs'])
1356 953
            && $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID
1357 953
            && ! isset($mapping['targetDocument'])
1358
        ) {
1359 3
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1360
        }
1361
1362 950
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1363 950
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1364 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1365
        }
1366
1367 946
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1368 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1369
        }
1370
1371 945 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...
1372 520
            $mapping['association'] = self::REFERENCE_ONE;
1373
        }
1374 945 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...
1375 463
            $mapping['association'] = self::REFERENCE_MANY;
1376
        }
1377 945 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...
1378 465
            $mapping['association'] = self::EMBED_ONE;
1379
        }
1380 945 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...
1381 508
            $mapping['association'] = self::EMBED_MANY;
1382
        }
1383
1384 945
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1385 133
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1386
        }
1387
1388
        /*
1389
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1390
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1391
        }
1392
        */
1393 945
        if (isset($mapping['version'])) {
1394 100
            $mapping['notSaved'] = true;
1395 100
            $this->setVersionMapping($mapping);
1396
        }
1397 945
        if (isset($mapping['lock'])) {
1398 27
            $mapping['notSaved'] = true;
1399 27
            $this->setLockMapping($mapping);
1400
        }
1401 945
        $mapping['isOwningSide'] = true;
1402 945
        $mapping['isInverseSide'] = false;
1403 945
        if (isset($mapping['reference'])) {
1404 592 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...
1405 92
                $mapping['isOwningSide'] = true;
1406 92
                $mapping['isInverseSide'] = false;
1407
            }
1408 592 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...
1409 298
                $mapping['isInverseSide'] = true;
1410 298
                $mapping['isOwningSide'] = false;
1411
            }
1412 592 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...
1413 67
                $mapping['isInverseSide'] = true;
1414 67
                $mapping['isOwningSide'] = false;
1415
            }
1416 592
            if (!isset($mapping['orphanRemoval'])) {
1417 567
                $mapping['orphanRemoval'] = false;
1418
            }
1419
        }
1420
1421 945
        if (!empty($mapping['prime']) && ($mapping['association'] !== self::REFERENCE_MANY || !$mapping['isInverseSide'])) {
1422
            throw MappingException::referencePrimersOnlySupportedForInverseReferenceMany($this->name, $mapping['fieldName']);
1423
        }
1424
1425 945
        $this->applyStorageStrategy($mapping);
1426
1427 944
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1428 944
        if (isset($mapping['association'])) {
1429 745
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1430
        }
1431
1432 944
        return $mapping;
1433
    }
1434
1435
    /**
1436
     * Validates the storage strategy of a mapping for consistency
1437
     * @param array $mapping
1438
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1439
     */
1440 945
    private function applyStorageStrategy(array &$mapping)
1441
    {
1442 945
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1443 927
            return;
1444
        }
1445
1446
        switch (true) {
1447 907
            case $mapping['type'] == 'int':
1448 906
            case $mapping['type'] == 'float':
1449 906
            case $mapping['type'] == 'increment':
1450 337
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1451 337
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1452 337
                break;
1453
1454 905
            case $mapping['type'] == 'many':
1455 621
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1456
                $allowedStrategies = [
1457 621
                    self::STORAGE_STRATEGY_PUSH_ALL,
1458 621
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1459 621
                    self::STORAGE_STRATEGY_SET,
1460 621
                    self::STORAGE_STRATEGY_SET_ARRAY,
1461 621
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1462 621
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1463
                ];
1464 621
                break;
1465
1466
            default:
1467 893
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1468 893
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1469
        }
1470
1471 907
        if (! isset($mapping['strategy'])) {
1472 896
            $mapping['strategy'] = $defaultStrategy;
1473
        }
1474
1475 907
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1476
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1477
        }
1478
1479 907
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1480 907
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1481 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1482
        }
1483 906
    }
1484
1485
    /**
1486
     * Map a MongoGridFSFile.
1487
     *
1488
     * @param array $mapping The mapping information.
1489
     */
1490
    public function mapFile(array $mapping)
1491
    {
1492
        $mapping['file'] = true;
1493
        $mapping['type'] = 'file';
1494
        $this->mapField($mapping);
1495
    }
1496
1497
    /**
1498
     * Map a single embedded document.
1499
     *
1500
     * @param array $mapping The mapping information.
1501
     */
1502 6
    public function mapOneEmbedded(array $mapping)
1503
    {
1504 6
        $mapping['embedded'] = true;
1505 6
        $mapping['type'] = 'one';
1506 6
        $this->mapField($mapping);
1507 5
    }
1508
1509
    /**
1510
     * Map a collection of embedded documents.
1511
     *
1512
     * @param array $mapping The mapping information.
1513
     */
1514 5
    public function mapManyEmbedded(array $mapping)
1515
    {
1516 5
        $mapping['embedded'] = true;
1517 5
        $mapping['type'] = 'many';
1518 5
        $this->mapField($mapping);
1519 5
    }
1520
1521
    /**
1522
     * Map a single document reference.
1523
     *
1524
     * @param array $mapping The mapping information.
1525
     */
1526 8
    public function mapOneReference(array $mapping)
1527
    {
1528 8
        $mapping['reference'] = true;
1529 8
        $mapping['type'] = 'one';
1530 8
        $this->mapField($mapping);
1531 8
    }
1532
1533
    /**
1534
     * Map a collection of document references.
1535
     *
1536
     * @param array $mapping The mapping information.
1537
     */
1538 8
    public function mapManyReference(array $mapping)
1539
    {
1540 8
        $mapping['reference'] = true;
1541 8
        $mapping['type'] = 'many';
1542 8
        $this->mapField($mapping);
1543 8
    }
1544
1545
    /**
1546
     * INTERNAL:
1547
     * Adds a field mapping without completing/validating it.
1548
     * This is mainly used to add inherited field mappings to derived classes.
1549
     *
1550
     * @param array $fieldMapping
1551
     */
1552 130
    public function addInheritedFieldMapping(array $fieldMapping)
1553
    {
1554 130
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1555
1556 130
        if (isset($fieldMapping['association'])) {
1557 78
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1558
        }
1559 130
    }
1560
1561
    /**
1562
     * INTERNAL:
1563
     * Adds an association mapping without completing/validating it.
1564
     * This is mainly used to add inherited association mappings to derived classes.
1565
     *
1566
     * @param array $mapping
1567
     *
1568
     * @return void
1569
     *
1570
     * @throws MappingException
1571
     */
1572 79
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1573
    {
1574 79
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1575 79
    }
1576
1577
    /**
1578
     * Checks whether the class has a mapped association with the given field name.
1579
     *
1580
     * @param string $fieldName
1581
     * @return boolean
1582
     */
1583 14
    public function hasReference($fieldName)
1584
    {
1585 14
        return isset($this->fieldMappings[$fieldName]['reference']);
1586
    }
1587
1588
    /**
1589
     * Checks whether the class has a mapped embed with the given field name.
1590
     *
1591
     * @param string $fieldName
1592
     * @return boolean
1593
     */
1594 5
    public function hasEmbed($fieldName)
1595
    {
1596 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1597
    }
1598
1599
    /**
1600
     * {@inheritDoc}
1601
     *
1602
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1603
     */
1604 7
    public function hasAssociation($fieldName)
1605
    {
1606 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1607
    }
1608
1609
    /**
1610
     * {@inheritDoc}
1611
     *
1612
     * Checks whether the class has a mapped reference or embed for the specified field and
1613
     * is a single valued association.
1614
     */
1615
    public function isSingleValuedAssociation($fieldName)
1616
    {
1617
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1618
    }
1619
1620
    /**
1621
     * {@inheritDoc}
1622
     *
1623
     * Checks whether the class has a mapped reference or embed for the specified field and
1624
     * is a collection valued association.
1625
     */
1626
    public function isCollectionValuedAssociation($fieldName)
1627
    {
1628
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1629
    }
1630
1631
    /**
1632
     * Checks whether the class has a mapped association for the specified field
1633
     * and if yes, checks whether it is a single-valued association (to-one).
1634
     *
1635
     * @param string $fieldName
1636
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1637
     */
1638
    public function isSingleValuedReference($fieldName)
1639
    {
1640
        return isset($this->fieldMappings[$fieldName]['association']) &&
1641
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1642
    }
1643
1644
    /**
1645
     * Checks whether the class has a mapped association for the specified field
1646
     * and if yes, checks whether it is a collection-valued association (to-many).
1647
     *
1648
     * @param string $fieldName
1649
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1650
     */
1651
    public function isCollectionValuedReference($fieldName)
1652
    {
1653
        return isset($this->fieldMappings[$fieldName]['association']) &&
1654
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1655
    }
1656
1657
    /**
1658
     * Checks whether the class has a mapped embedded document for the specified field
1659
     * and if yes, checks whether it is a single-valued association (to-one).
1660
     *
1661
     * @param string $fieldName
1662
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1663
     */
1664
    public function isSingleValuedEmbed($fieldName)
1665
    {
1666
        return isset($this->fieldMappings[$fieldName]['association']) &&
1667
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1668
    }
1669
1670
    /**
1671
     * Checks whether the class has a mapped embedded document for the specified field
1672
     * and if yes, checks whether it is a collection-valued association (to-many).
1673
     *
1674
     * @param string $fieldName
1675
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1676
     */
1677
    public function isCollectionValuedEmbed($fieldName)
1678
    {
1679
        return isset($this->fieldMappings[$fieldName]['association']) &&
1680
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1681
    }
1682
1683
    /**
1684
     * Sets the ID generator used to generate IDs for instances of this class.
1685
     *
1686
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1687
     */
1688 846
    public function setIdGenerator($generator)
1689
    {
1690 846
        $this->idGenerator = $generator;
1691 846
    }
1692
1693
    /**
1694
     * Casts the identifier to its portable PHP type.
1695
     *
1696
     * @param mixed $id
1697
     * @return mixed $id
1698
     */
1699 650
    public function getPHPIdentifierValue($id)
1700
    {
1701 650
        $idType = $this->fieldMappings[$this->identifier]['type'];
1702 650
        return Type::getType($idType)->convertToPHPValue($id);
1703
    }
1704
1705
    /**
1706
     * Casts the identifier to its database type.
1707
     *
1708
     * @param mixed $id
1709
     * @return mixed $id
1710
     */
1711 717
    public function getDatabaseIdentifierValue($id)
1712
    {
1713 717
        $idType = $this->fieldMappings[$this->identifier]['type'];
1714 717
        return Type::getType($idType)->convertToDatabaseValue($id);
1715
    }
1716
1717
    /**
1718
     * Sets the document identifier of a document.
1719
     *
1720
     * The value will be converted to a PHP type before being set.
1721
     *
1722
     * @param object $document
1723
     * @param mixed $id
1724
     */
1725 579
    public function setIdentifierValue($document, $id)
1726
    {
1727 579
        $id = $this->getPHPIdentifierValue($id);
1728 579
        $this->reflFields[$this->identifier]->setValue($document, $id);
1729 579
    }
1730
1731
    /**
1732
     * Gets the document identifier as a PHP type.
1733
     *
1734
     * @param object $document
1735
     * @return mixed $id
1736
     */
1737 668
    public function getIdentifierValue($document)
1738
    {
1739 668
        return $this->reflFields[$this->identifier]->getValue($document);
1740
    }
1741
1742
    /**
1743
     * {@inheritDoc}
1744
     *
1745
     * Since MongoDB only allows exactly one identifier field this is a proxy
1746
     * to {@see getIdentifierValue()} and returns an array with the identifier
1747
     * field as a key.
1748
     */
1749
    public function getIdentifierValues($object)
1750
    {
1751
        return array($this->identifier => $this->getIdentifierValue($object));
1752
    }
1753
1754
    /**
1755
     * Get the document identifier object as a database type.
1756
     *
1757
     * @param object $document
1758
     *
1759
     * @return \MongoId $id The MongoID object.
1760
     */
1761 36
    public function getIdentifierObject($document)
1762
    {
1763 36
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1764
    }
1765
1766
    /**
1767
     * Sets the specified field to the specified value on the given document.
1768
     *
1769
     * @param object $document
1770
     * @param string $field
1771
     * @param mixed $value
1772
     */
1773 11
    public function setFieldValue($document, $field, $value)
1774
    {
1775 11
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1776
            //property changes to an uninitialized proxy will not be tracked or persisted,
1777
            //so the proxy needs to be loaded first.
1778 1
            $document->__load();
1779
        }
1780
1781 11
        $this->reflFields[$field]->setValue($document, $value);
1782 11
    }
1783
1784
    /**
1785
     * Gets the specified field's value off the given document.
1786
     *
1787
     * @param object $document
1788
     * @param string $field
1789
     *
1790
     * @return mixed
1791
     */
1792 31
    public function getFieldValue($document, $field)
1793
    {
1794 31
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1795 1
            $document->__load();
1796
        }
1797
1798 31
        return $this->reflFields[$field]->getValue($document);
1799
    }
1800
1801
    /**
1802
     * Gets the mapping of a field.
1803
     *
1804
     * @param string $fieldName  The field name.
1805
     *
1806
     * @return array  The field mapping.
1807
     *
1808
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1809
     */
1810 102
    public function getFieldMapping($fieldName)
1811
    {
1812 102
        if ( ! isset($this->fieldMappings[$fieldName])) {
1813 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1814
        }
1815 100
        return $this->fieldMappings[$fieldName];
1816
    }
1817
1818
    /**
1819
     * Gets mappings of fields holding embedded document(s).
1820
     *
1821
     * @return array of field mappings
1822
     */
1823 619
    public function getEmbeddedFieldsMappings()
1824
    {
1825 619
        return array_filter(
1826 619
            $this->associationMappings,
1827
            function($assoc) { return ! empty($assoc['embedded']); }
1828
        );
1829
    }
1830
1831
    /**
1832
     * Gets the field mapping by its DB name.
1833
     * E.g. it returns identifier's mapping when called with _id.
1834
     *
1835
     * @param string $dbFieldName
1836
     *
1837
     * @return array
1838
     * @throws MappingException
1839
     */
1840 3
    public function getFieldMappingByDbFieldName($dbFieldName)
1841
    {
1842 3
        foreach ($this->fieldMappings as $mapping) {
1843 3
            if ($mapping['name'] == $dbFieldName) {
1844 3
                return $mapping;
1845
            }
1846
        }
1847
1848
        throw MappingException::mappingNotFoundByDbName($this->name, $dbFieldName);
1849
    }
1850
1851
    /**
1852
     * Check if the field is not null.
1853
     *
1854
     * @param string $fieldName  The field name
1855
     *
1856
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1857
     */
1858 1
    public function isNullable($fieldName)
1859
    {
1860 1
        $mapping = $this->getFieldMapping($fieldName);
1861 1
        if ($mapping !== false) {
1862 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1863
        }
1864
        return false;
1865
    }
1866
1867
    /**
1868
     * Checks whether the document has a discriminator field and value configured.
1869
     *
1870
     * @return boolean
1871
     */
1872 533
    public function hasDiscriminator()
1873
    {
1874 533
        return isset($this->discriminatorField, $this->discriminatorValue);
1875
    }
1876
1877
    /**
1878
     * Sets the type of Id generator to use for the mapped class.
1879
     *
1880
     * @param string $generatorType Generator type.
1881
     */
1882 379
    public function setIdGeneratorType($generatorType)
1883
    {
1884 379
        $this->generatorType = $generatorType;
1885 379
    }
1886
1887
    /**
1888
     * Sets the Id generator options.
1889
     *
1890
     * @param array $generatorOptions Generator options.
1891
     */
1892
    public function setIdGeneratorOptions($generatorOptions)
1893
    {
1894
        $this->generatorOptions = $generatorOptions;
1895
    }
1896
1897
    /**
1898
     * @return boolean
1899
     */
1900 625
    public function isInheritanceTypeNone()
1901
    {
1902 625
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1903
    }
1904
1905
    /**
1906
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1907
     *
1908
     * @return boolean
1909
     */
1910 372
    public function isInheritanceTypeSingleCollection()
1911
    {
1912 372
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1913
    }
1914
1915
    /**
1916
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1917
     *
1918
     * @return boolean
1919
     */
1920
    public function isInheritanceTypeCollectionPerClass()
1921
    {
1922
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1923
    }
1924
1925
    /**
1926
     * Sets the mapped subclasses of this class.
1927
     *
1928
     * @param string[] $subclasses The names of all mapped subclasses.
1929
     */
1930 2
    public function setSubclasses(array $subclasses)
1931
    {
1932 2
        foreach ($subclasses as $subclass) {
1933 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1934 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1935
            } else {
1936 2
                $this->subClasses[] = $subclass;
1937
            }
1938
        }
1939 2
    }
1940
1941
    /**
1942
     * Sets the parent class names.
1943
     * Assumes that the class names in the passed array are in the order:
1944
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1945
     *
1946
     * @param string[] $classNames
1947
     */
1948 904
    public function setParentClasses(array $classNames)
1949
    {
1950 904
        $this->parentClasses = $classNames;
1951
1952 904
        if (count($classNames) > 0) {
1953 113
            $this->rootDocumentName = array_pop($classNames);
1954
        }
1955 904
    }
1956
1957
    /**
1958
     * Checks whether the class will generate a new \MongoId instance for us.
1959
     *
1960
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1961
     */
1962
    public function isIdGeneratorAuto()
1963
    {
1964
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1965
    }
1966
1967
    /**
1968
     * Checks whether the class will use a collection to generate incremented identifiers.
1969
     *
1970
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1971
     */
1972
    public function isIdGeneratorIncrement()
1973
    {
1974
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1975
    }
1976
1977
    /**
1978
     * Checks whether the class will generate a uuid id.
1979
     *
1980
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1981
     */
1982
    public function isIdGeneratorUuid()
1983
    {
1984
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1985
    }
1986
1987
    /**
1988
     * Checks whether the class uses no id generator.
1989
     *
1990
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1991
     */
1992
    public function isIdGeneratorNone()
1993
    {
1994
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1995
    }
1996
1997
    /**
1998
     * Sets the version field mapping used for versioning. Sets the default
1999
     * value to use depending on the column type.
2000
     *
2001
     * @param array $mapping   The version field mapping array
2002
     *
2003
     * @throws LockException
2004
     */
2005 100
    public function setVersionMapping(array &$mapping)
2006
    {
2007 100
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
2008 1
            throw LockException::invalidVersionFieldType($mapping['type']);
2009
        }
2010
2011 99
        $this->isVersioned  = true;
2012 99
        $this->versionField = $mapping['fieldName'];
2013 99
    }
2014
2015
    /**
2016
     * Sets whether this class is to be versioned for optimistic locking.
2017
     *
2018
     * @param boolean $bool
2019
     */
2020 373
    public function setVersioned($bool)
2021
    {
2022 373
        $this->isVersioned = $bool;
2023 373
    }
2024
2025
    /**
2026
     * Sets the name of the field that is to be used for versioning if this class is
2027
     * versioned for optimistic locking.
2028
     *
2029
     * @param string $versionField
2030
     */
2031 373
    public function setVersionField($versionField)
2032
    {
2033 373
        $this->versionField = $versionField;
2034 373
    }
2035
2036
    /**
2037
     * Sets the version field mapping used for versioning. Sets the default
2038
     * value to use depending on the column type.
2039
     *
2040
     * @param array $mapping   The version field mapping array
2041
     *
2042
     * @throws \Doctrine\ODM\MongoDB\LockException
2043
     */
2044 27
    public function setLockMapping(array &$mapping)
2045
    {
2046 27
        if ($mapping['type'] !== 'int') {
2047 1
            throw LockException::invalidLockFieldType($mapping['type']);
2048
        }
2049
2050 26
        $this->isLockable = true;
2051 26
        $this->lockField = $mapping['fieldName'];
2052 26
    }
2053
2054
    /**
2055
     * Sets whether this class is to allow pessimistic locking.
2056
     *
2057
     * @param boolean $bool
2058
     */
2059
    public function setLockable($bool)
2060
    {
2061
        $this->isLockable = $bool;
2062
    }
2063
2064
    /**
2065
     * Sets the name of the field that is to be used for storing whether a document
2066
     * is currently locked or not.
2067
     *
2068
     * @param string $lockField
2069
     */
2070
    public function setLockField($lockField)
2071
    {
2072
        $this->lockField = $lockField;
2073
    }
2074
2075
    /**
2076
     * {@inheritDoc}
2077
     */
2078
    public function getFieldNames()
2079
    {
2080
        return array_keys($this->fieldMappings);
2081
    }
2082
2083
    /**
2084
     * {@inheritDoc}
2085
     */
2086
    public function getAssociationNames()
2087
    {
2088
        return array_keys($this->associationMappings);
2089
    }
2090
2091
    /**
2092
     * {@inheritDoc}
2093
     */
2094 22
    public function getTypeOfField($fieldName)
2095
    {
2096 22
        return isset($this->fieldMappings[$fieldName]) ?
2097 22
            $this->fieldMappings[$fieldName]['type'] : null;
2098
    }
2099
2100
    /**
2101
     * {@inheritDoc}
2102
     */
2103 6
    public function getAssociationTargetClass($assocName)
2104
    {
2105 6
        if ( ! isset($this->associationMappings[$assocName])) {
2106 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2107
        }
2108
2109 3
        return $this->associationMappings[$assocName]['targetDocument'];
2110
    }
2111
2112
    /**
2113
     * Retrieve the collectionClass associated with an association
2114
     *
2115
     * @param string $assocName
2116
     */
2117 2
    public function getAssociationCollectionClass($assocName)
2118
    {
2119 2
        if ( ! isset($this->associationMappings[$assocName])) {
2120
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2121
        }
2122
2123 2
        if ( ! array_key_exists('collectionClass', $this->associationMappings[$assocName])) {
2124
            throw new InvalidArgumentException("collectionClass can only be applied to 'embedMany' and 'referenceMany' associations.");
2125
        }
2126
2127 2
        return $this->associationMappings[$assocName]['collectionClass'];
2128
    }
2129
2130
    /**
2131
     * {@inheritDoc}
2132
     */
2133
    public function isAssociationInverseSide($fieldName)
2134
    {
2135
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2136
    }
2137
2138
    /**
2139
     * {@inheritDoc}
2140
     */
2141
    public function getAssociationMappedByTargetField($fieldName)
2142
    {
2143
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2144
    }
2145
}
2146