Completed
Push — master ( 11f27c...4713a5 )
by Andreas
08:33
created

ClassMetadataInfo::setAlsoLoadMethods()   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
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ODM\MongoDB\Mapping;
21
22
use Doctrine\ODM\MongoDB\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 \MongoDB\BSON\ObjectId 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
    const REFERENCE_STORE_AS_REF = 'ref';
107
108
    /* The inheritance mapping types */
109
    /**
110
     * NONE means the class does not participate in an inheritance hierarchy
111
     * and therefore does not need an inheritance mapping type.
112
     */
113
    const INHERITANCE_TYPE_NONE = 1;
114
115
    /**
116
     * SINGLE_COLLECTION means the class will be persisted according to the rules of
117
     * <tt>Single Collection Inheritance</tt>.
118
     */
119
    const INHERITANCE_TYPE_SINGLE_COLLECTION = 2;
120
121
    /**
122
     * COLLECTION_PER_CLASS means the class will be persisted according to the rules
123
     * of <tt>Concrete Collection Inheritance</tt>.
124
     */
125
    const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3;
126
127
    /**
128
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
129
     * by doing a property-by-property comparison with the original data. This will
130
     * be done for all entities that are in MANAGED state at commit-time.
131
     *
132
     * This is the default change tracking policy.
133
     */
134
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
135
136
    /**
137
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
138
     * by doing a property-by-property comparison with the original data. This will
139
     * be done only for entities that were explicitly saved (through persist() or a cascade).
140
     */
141
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
142
143
    /**
144
     * NOTIFY means that Doctrine relies on the entities sending out notifications
145
     * when their properties change. Such entity classes must implement
146
     * the <tt>NotifyPropertyChanged</tt> interface.
147
     */
148
    const CHANGETRACKING_NOTIFY = 3;
149
150
    /**
151
     * SET means that fields will be written to the database using a $set operator
152
     */
153
    const STORAGE_STRATEGY_SET = 'set';
154
155
    /**
156
     * INCREMENT means that fields will be written to the database by calculating
157
     * the difference and using the $inc operator
158
     */
159
    const STORAGE_STRATEGY_INCREMENT = 'increment';
160
161
    const STORAGE_STRATEGY_PUSH_ALL = 'pushAll';
162
    const STORAGE_STRATEGY_ADD_TO_SET = 'addToSet';
163
    const STORAGE_STRATEGY_ATOMIC_SET = 'atomicSet';
164
    const STORAGE_STRATEGY_ATOMIC_SET_ARRAY = 'atomicSetArray';
165
    const STORAGE_STRATEGY_SET_ARRAY = 'setArray';
166
167
    /**
168
     * READ-ONLY: The name of the mongo database the document is mapped to.
169
     */
170
    public $db;
171
172
    /**
173
     * READ-ONLY: The name of the mongo collection the document is mapped to.
174
     */
175
    public $collection;
176
177
    /**
178
     * READ-ONLY: If the collection should be a fixed size.
179
     */
180
    public $collectionCapped;
181
182
    /**
183
     * READ-ONLY: If the collection is fixed size, its size in bytes.
184
     */
185
    public $collectionSize;
186
187
    /**
188
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
189
     */
190
    public $collectionMax;
191
192
    /**
193
     * READ-ONLY Describes how MongoDB clients route read operations to the members of a replica set.
194
     */
195
    public $readPreference;
196
197
    /**
198
     * READ-ONLY Associated with readPreference Allows to specify criteria so that your application can target read
199
     * operations to specific members, based on custom parameters.
200
     */
201
    public $readPreferenceTags;
202
203
    /**
204
     * READ-ONLY: Describes the level of acknowledgement requested from MongoDB for write operations.
205
     */
206
    public $writeConcern;
207
208
    /**
209
     * READ-ONLY: The field name of the document identifier.
210
     */
211
    public $identifier;
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: The name of the document class.
232
     */
233
    public $name;
234
235
    /**
236
     * READ-ONLY: The namespace the document class is contained in.
237
     *
238
     * @var string
239
     * @todo Not really needed. Usage could be localized.
240
     */
241
    public $namespace;
242
243
    /**
244
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
245
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
246
     * as {@link $documentName}.
247
     *
248
     * @var string
249
     */
250
    public $rootDocumentName;
251
252
    /**
253
     * The name of the custom repository class used for the document class.
254
     * (Optional).
255
     *
256
     * @var string
257
     */
258
    public $customRepositoryClassName;
259
260
    /**
261
     * READ-ONLY: The names of the parent classes (ancestors).
262
     *
263
     * @var array
264
     */
265
    public $parentClasses = array();
266
267
    /**
268
     * READ-ONLY: The names of all subclasses (descendants).
269
     *
270
     * @var array
271
     */
272
    public $subClasses = array();
273
274
    /**
275
     * The ReflectionProperty instances of the mapped class.
276
     *
277
     * @var \ReflectionProperty[]
278
     */
279
    public $reflFields = array();
280
281
    /**
282
     * READ-ONLY: The inheritance mapping type used by the class.
283
     *
284
     * @var integer
285
     */
286
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
287
288
    /**
289
     * READ-ONLY: The Id generator type used by the class.
290
     *
291
     * @var string
292
     */
293
    public $generatorType = self::GENERATOR_TYPE_AUTO;
294
295
    /**
296
     * READ-ONLY: The Id generator options.
297
     *
298
     * @var array
299
     */
300
    public $generatorOptions = array();
301
302
    /**
303
     * READ-ONLY: The ID generator used for generating IDs for this class.
304
     *
305
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
306
     */
307
    public $idGenerator;
308
309
    /**
310
     * READ-ONLY: The field mappings of the class.
311
     * Keys are field names and values are mapping definitions.
312
     *
313
     * The mapping definition array has the following values:
314
     *
315
     * - <b>fieldName</b> (string)
316
     * The name of the field in the Document.
317
     *
318
     * - <b>id</b> (boolean, optional)
319
     * Marks the field as the primary key of the document. Multiple fields of an
320
     * document can have the id attribute, forming a composite key.
321
     *
322
     * @var array
323
     */
324
    public $fieldMappings = array();
325
326
    /**
327
     * READ-ONLY: The association mappings of the class.
328
     * Keys are field names and values are mapping definitions.
329
     *
330
     * @var array
331
     */
332
    public $associationMappings = array();
333
334
    /**
335
     * READ-ONLY: Array of fields to also load with a given method.
336
     *
337
     * @var array
338
     */
339
    public $alsoLoadMethods = array();
340
341
    /**
342
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
343
     *
344
     * @var array
345
     */
346
    public $lifecycleCallbacks = array();
347
348
    /**
349
     * READ-ONLY: The discriminator value of this class.
350
     *
351
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
352
     * where a discriminator field is used.</b>
353
     *
354
     * @var mixed
355
     * @see discriminatorField
356
     */
357
    public $discriminatorValue;
358
359
    /**
360
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
361
     *
362
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
363
     * where a discriminator field is used.</b>
364
     *
365
     * @var mixed
366
     * @see discriminatorField
367
     */
368
    public $discriminatorMap = array();
369
370
    /**
371
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
372
     * inheritance mapping.
373
     *
374
     * @var string
375
     */
376
    public $discriminatorField;
377
378
    /**
379
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
380
     *
381
     * @var string
382
     * @see discriminatorField
383
     */
384
    public $defaultDiscriminatorValue;
385
386
    /**
387
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
388
     *
389
     * @var boolean
390
     */
391
    public $isMappedSuperclass = false;
392
393
    /**
394
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
395
     *
396
     * @var boolean
397
     */
398
    public $isEmbeddedDocument = false;
399
400
    /**
401
     * READ-ONLY: Whether this class describes the mapping of an aggregation result document.
402
     *
403
     * @var boolean
404
     */
405
    public $isQueryResultDocument = false;
406
407
    /**
408
     * READ-ONLY: The policy used for change-tracking on entities of this class.
409
     *
410
     * @var integer
411
     */
412
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
413
414
    /**
415
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
416
     * with optimistic locking.
417
     *
418
     * @var boolean $isVersioned
419
     */
420
    public $isVersioned;
421
422
    /**
423
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
424
     *
425
     * @var mixed $versionField
426
     */
427
    public $versionField;
428
429
    /**
430
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
431
     * locking.
432
     *
433
     * @var boolean $isLockable
434
     */
435
    public $isLockable;
436
437
    /**
438
     * READ-ONLY: The name of the field which is used for locking a document.
439
     *
440
     * @var mixed $lockField
441
     */
442
    public $lockField;
443
444
    /**
445
     * The ReflectionClass instance of the mapped class.
446
     *
447
     * @var \ReflectionClass
448
     */
449
    public $reflClass;
450
451
    /**
452
     * READ_ONLY: A flag for whether or not this document is read-only.
453
     *
454
     * @var bool
455
     */
456
    public $isReadOnly;
457
458
    /**
459
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
460
     * metadata of the class with the given name.
461
     *
462
     * @param string $documentName The name of the document class the new instance is used for.
463
     */
464 1516
    public function __construct($documentName)
465
    {
466 1516
        $this->name = $documentName;
467 1516
        $this->rootDocumentName = $documentName;
468 1516
    }
469
470
    /**
471
     * Helper method to get reference id of ref* type references
472
     * @param mixed  $reference
473
     * @param string $storeAs
474
     * @return mixed
475
     * @internal
476
     */
477 121
    public static function getReferenceId($reference, $storeAs)
478
    {
479 121
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID ? $reference : $reference[ClassMetadataInfo::getReferencePrefix($storeAs) . 'id'];
480
    }
481
482
    /**
483
     * Returns the reference prefix used for a reference
484
     * @param string $storeAs
485
     * @return string
486
     */
487 186
    private static function getReferencePrefix($storeAs)
488
    {
489 186
        if (!in_array($storeAs, [ClassMetadataInfo::REFERENCE_STORE_AS_REF, ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF, ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB])) {
490
            throw new \LogicException('Can only get a reference prefix for DBRef and reference arrays');
491
        }
492
493 186
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_REF ? '' : '$';
494
    }
495
496
    /**
497
     * Returns a fully qualified field name for a given reference
498
     * @param string $storeAs
499
     * @param string $pathPrefix The field path prefix
500
     * @return string
501
     * @internal
502
     */
503 134
    public static function getReferenceFieldName($storeAs, $pathPrefix = '')
504
    {
505 134
        if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID) {
506 94
            return $pathPrefix;
507
        }
508
509 122
        return ($pathPrefix ? $pathPrefix . '.' : '') . static::getReferencePrefix($storeAs) . 'id';
0 ignored issues
show
Bug introduced by
Since getReferencePrefix() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getReferencePrefix() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
510
    }
511
512
    /**
513
     * {@inheritDoc}
514
     */
515 1383
    public function getReflectionClass()
516
    {
517 1383
        if ( ! $this->reflClass) {
518 2
            $this->reflClass = new \ReflectionClass($this->name);
519
        }
520
521 1383
        return $this->reflClass;
522
    }
523
524
    /**
525
     * {@inheritDoc}
526
     */
527 323
    public function isIdentifier($fieldName)
528
    {
529 323
        return $this->identifier === $fieldName;
530
    }
531
532
    /**
533
     * INTERNAL:
534
     * Sets the mapped identifier field of this class.
535
     *
536
     * @param string $identifier
537
     */
538 896
    public function setIdentifier($identifier)
539
    {
540 896
        $this->identifier = $identifier;
541 896
    }
542
543
    /**
544
     * {@inheritDoc}
545
     *
546
     * Since MongoDB only allows exactly one identifier field
547
     * this will always return an array with only one value
548
     */
549 38
    public function getIdentifier()
550
    {
551 38
        return array($this->identifier);
552
    }
553
554
    /**
555
     * {@inheritDoc}
556
     *
557
     * Since MongoDB only allows exactly one identifier field
558
     * this will always return an array with only one value
559
     */
560 99
    public function getIdentifierFieldNames()
561
    {
562 99
        return array($this->identifier);
563
    }
564
565
    /**
566
     * {@inheritDoc}
567
     */
568 892
    public function hasField($fieldName)
569
    {
570 892
        return isset($this->fieldMappings[$fieldName]);
571
    }
572
573
    /**
574
     * Sets the inheritance type used by the class and it's subclasses.
575
     *
576
     * @param integer $type
577
     */
578 912
    public function setInheritanceType($type)
579
    {
580 912
        $this->inheritanceType = $type;
581 912
    }
582
583
    /**
584
     * Checks whether a mapped field is inherited from an entity superclass.
585
     *
586
     * @param  string $fieldName
587
     *
588
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
589
     */
590 1383
    public function isInheritedField($fieldName)
591
    {
592 1383
        return isset($this->fieldMappings[$fieldName]['inherited']);
593
    }
594
595
    /**
596
     * Registers a custom repository class for the document class.
597
     *
598
     * @param string $repositoryClassName The class name of the custom repository.
599
     */
600 844
    public function setCustomRepositoryClass($repositoryClassName)
601
    {
602 844
        if ($this->isEmbeddedDocument || $this->isQueryResultDocument) {
603
            return;
604
        }
605
606 844 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...
607 3
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
608
        }
609
610 844
        $this->customRepositoryClassName = $repositoryClassName;
611 844
    }
612
613
    /**
614
     * Dispatches the lifecycle event of the given document by invoking all
615
     * registered callbacks.
616
     *
617
     * @param string $event     Lifecycle event
618
     * @param object $document  Document on which the event occurred
619
     * @param array  $arguments Arguments to pass to all callbacks
620
     * @throws \InvalidArgumentException if document class is not this class or
621
     *                                   a Proxy of this class
622
     */
623 609
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
624
    {
625 609
        if ( ! $document instanceof $this->name) {
626 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
627
        }
628
629 608
        if (empty($this->lifecycleCallbacks[$event])) {
630 593
            return;
631
        }
632
633 177
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
634 177
            if ($arguments !== null) {
635 176
                call_user_func_array(array($document, $callback), $arguments);
636
            } else {
637 177
                $document->$callback();
638
            }
639
        }
640 177
    }
641
642
    /**
643
     * Checks whether the class has callbacks registered for a lifecycle event.
644
     *
645
     * @param string $event Lifecycle event
646
     *
647
     * @return boolean
648
     */
649
    public function hasLifecycleCallbacks($event)
650
    {
651
        return ! empty($this->lifecycleCallbacks[$event]);
652
    }
653
654
    /**
655
     * Gets the registered lifecycle callbacks for an event.
656
     *
657
     * @param string $event
658
     * @return array
659
     */
660
    public function getLifecycleCallbacks($event)
661
    {
662
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
663
    }
664
665
    /**
666
     * Adds a lifecycle callback for documents of this class.
667
     *
668
     * If the callback is already registered, this is a NOOP.
669
     *
670
     * @param string $callback
671
     * @param string $event
672
     */
673 817
    public function addLifecycleCallback($callback, $event)
674
    {
675 817
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
676 1
            return;
677
        }
678
679 817
        $this->lifecycleCallbacks[$event][] = $callback;
680 817
    }
681
682
    /**
683
     * Sets the lifecycle callbacks for documents of this class.
684
     *
685
     * Any previously registered callbacks are overwritten.
686
     *
687
     * @param array $callbacks
688
     */
689 895
    public function setLifecycleCallbacks(array $callbacks)
690
    {
691 895
        $this->lifecycleCallbacks = $callbacks;
692 895
    }
693
694
    /**
695
     * Registers a method for loading document data before field hydration.
696
     *
697
     * Note: A method may be registered multiple times for different fields.
698
     * it will be invoked only once for the first field found.
699
     *
700
     * @param string       $method Method name
701
     * @param array|string $fields Database field name(s)
702
     */
703 15
    public function registerAlsoLoadMethod($method, $fields)
704
    {
705 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
706 15
    }
707
708
    /**
709
     * Sets the AlsoLoad methods for documents of this class.
710
     *
711
     * Any previously registered methods are overwritten.
712
     *
713
     * @param array $methods
714
     */
715 895
    public function setAlsoLoadMethods(array $methods)
716
    {
717 895
        $this->alsoLoadMethods = $methods;
718 895
    }
719
720
    /**
721
     * Sets the discriminator field.
722
     *
723
     * The field name is the the unmapped database field. Discriminator values
724
     * are only used to discern the hydration class and are not mapped to class
725
     * properties.
726
     *
727
     * @param string $discriminatorField
728
     *
729
     * @throws MappingException If the discriminator field conflicts with the
730
     *                          "name" attribute of a mapped field.
731
     */
732 922
    public function setDiscriminatorField($discriminatorField)
733
    {
734 922
        if ($discriminatorField === null) {
735 853
            $this->discriminatorField = null;
736
737 853
            return;
738
        }
739
740
        // Handle array argument with name/fieldName keys for BC
741 118
        if (is_array($discriminatorField)) {
742
            if (isset($discriminatorField['name'])) {
743
                $discriminatorField = $discriminatorField['name'];
744
            } elseif (isset($discriminatorField['fieldName'])) {
745
                $discriminatorField = $discriminatorField['fieldName'];
746
            }
747
        }
748
749 118
        foreach ($this->fieldMappings as $fieldMapping) {
750 4
            if ($discriminatorField == $fieldMapping['name']) {
751 4
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
752
            }
753
        }
754
755 117
        $this->discriminatorField = $discriminatorField;
756 117
    }
757
758
    /**
759
     * Sets the discriminator values used by this class.
760
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
761
     *
762
     * @param array $map
763
     *
764
     * @throws MappingException
765
     */
766 915
    public function setDiscriminatorMap(array $map)
767
    {
768 915
        foreach ($map as $value => $className) {
769 113
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
770 82
                $className = $this->namespace . '\\' . $className;
771
            }
772 113
            $this->discriminatorMap[$value] = $className;
773 113
            if ($this->name == $className) {
774 105
                $this->discriminatorValue = $value;
775
            } else {
776 111
                if ( ! class_exists($className)) {
777
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
778
                }
779 111
                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...
780 113
                    $this->subClasses[] = $className;
781
                }
782
            }
783
        }
784 915
    }
785
786
    /**
787
     * Sets the default discriminator value to be used for this class
788
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
789
     *
790
     * @param string $defaultDiscriminatorValue
791
     *
792
     * @throws MappingException
793
     */
794 899
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
795
    {
796 899
        if ($defaultDiscriminatorValue === null) {
797 895
            $this->defaultDiscriminatorValue = null;
798
799 895
            return;
800
        }
801
802 49
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
803
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
804
        }
805
806 49
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
807 49
    }
808
809
    /**
810
     * Sets the discriminator value for this class.
811
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
812
     * collection.
813
     *
814
     * @param string $value
815
     */
816 3
    public function setDiscriminatorValue($value)
817
    {
818 3
        $this->discriminatorMap[$value] = $this->name;
819 3
        $this->discriminatorValue = $value;
820 3
    }
821
822
    /**
823
     * Sets the slaveOkay option applied to collections for this class.
824
     *
825
     * @param boolean|null $slaveOkay
826
     *
827
     * @deprecated in version 1.2 and will be removed in 2.0.
828
     *
829
     * @throws MappingException
830
     */
831 2
    public function setSlaveOkay($slaveOkay)
832
    {
833 2
        if ($slaveOkay) {
834 1
            @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...
835 1
                sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.', __METHOD__),
836 1
                E_USER_DEPRECATED
837
            );
838
        }
839
840 2
        if ($this->readPreference) {
841
            throw MappingException::canNotCombineReadPreferenceAndSlaveOkay($this->getName());
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ODM\MongoDB\Map...referenceAndSlaveOkay() has been deprecated with message: Method will be removed along with slaveOkay in version 2.0.

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
842
        }
843 2
        $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...
844 2
    }
845
846
    /**
847
     * Add a index for this Document.
848
     *
849
     * @param array $keys Array of keys for the index.
850
     * @param array $options Array of options for the index.
851
     */
852 189
    public function addIndex($keys, array $options = array())
853
    {
854 189
        $this->indexes[] = array(
855 189 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...
856 189
                if ($value == 1 || $value == -1) {
857 54
                    return (int) $value;
858
                }
859 182
                if (is_string($value)) {
860 182
                    $lower = strtolower($value);
861 182
                    if ($lower === 'asc') {
862 182
                        return 1;
863 1
                    } elseif ($lower === 'desc') {
864 1
                        return -1;
865
                    }
866
                }
867
                return $value;
868 189
            }, $keys),
869 189
            'options' => $options
870
        );
871 189
    }
872
873
    /**
874
     * Returns the array of indexes for this Document.
875
     *
876
     * @return array $indexes The array of indexes.
877
     */
878 25
    public function getIndexes()
879
    {
880 25
        return $this->indexes;
881
    }
882
883
    /**
884
     * Checks whether this document has indexes or not.
885
     *
886
     * @return boolean
887
     */
888
    public function hasIndexes()
889
    {
890
        return $this->indexes ? true : false;
891
    }
892
893
    /**
894
     * Set shard key for this Document.
895
     *
896
     * @param array $keys Array of document keys.
897
     * @param array $options Array of sharding options.
898
     *
899
     * @throws MappingException
900
     */
901 72
    public function setShardKey(array $keys, array $options = array())
902
    {
903 72
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
904 2
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
905
        }
906
907 72
        if ($this->isEmbeddedDocument) {
908 2
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
909
        }
910
911 70
        foreach (array_keys($keys) as $field) {
912 70
            if (! isset($this->fieldMappings[$field])) {
913 63
                continue;
914
            }
915
916 7
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
917 3
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
918
            }
919
920 4
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
921 4
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
922
            }
923
        }
924
925 66
        $this->shardKey = array(
926 66 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...
927 66
                if ($value == 1 || $value == -1) {
928 6
                    return (int) $value;
929
                }
930 65
                if (is_string($value)) {
931 65
                    $lower = strtolower($value);
932 65
                    if ($lower === 'asc') {
933 64
                        return 1;
934 46
                    } elseif ($lower === 'desc') {
935
                        return -1;
936
                    }
937
                }
938 46
                return $value;
939 66
            }, $keys),
940 66
            'options' => $options
941
        );
942 66
    }
943
944
    /**
945
     * @return array
946
     */
947 18
    public function getShardKey()
948
    {
949 18
        return $this->shardKey;
950
    }
951
952
    /**
953
     * Checks whether this document has shard key or not.
954
     *
955
     * @return bool
956
     */
957 1111
    public function isSharded()
958
    {
959 1111
        return $this->shardKey ? true : false;
960
    }
961
962
    /**
963
     * Sets the read preference used by this class.
964
     *
965
     * @param string $readPreference
966
     * @param array|null $tags
967
     *
968
     * @throws MappingException
969
     */
970 896
    public function setReadPreference($readPreference, $tags)
971
    {
972 896
        if ($this->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...
973
            throw MappingException::canNotCombineReadPreferenceAndSlaveOkay($this->getName());
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ODM\MongoDB\Map...referenceAndSlaveOkay() has been deprecated with message: Method will be removed along with slaveOkay in version 2.0.

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
974
        }
975 896
        $this->readPreference = $readPreference;
976 896
        $this->readPreferenceTags = $tags;
977 896
    }
978
979
    /**
980
     * Sets the write concern used by this class.
981
     *
982
     * @param string $writeConcern
983
     */
984 906
    public function setWriteConcern($writeConcern)
985
    {
986 906
        $this->writeConcern = $writeConcern;
987 906
    }
988
989
    /**
990
     * @return string
991
     */
992 12
    public function getWriteConcern()
993
    {
994 12
        return $this->writeConcern;
995
    }
996
997
    /**
998
     * Whether there is a write concern configured for this class.
999
     *
1000
     * @return bool
1001
     */
1002 558
    public function hasWriteConcern()
1003
    {
1004 558
        return $this->writeConcern !== null;
1005
    }
1006
1007
    /**
1008
     * Sets the change tracking policy used by this class.
1009
     *
1010
     * @param integer $policy
1011
     */
1012 899
    public function setChangeTrackingPolicy($policy)
1013
    {
1014 899
        $this->changeTrackingPolicy = $policy;
1015 899
    }
1016
1017
    /**
1018
     * Whether the change tracking policy of this class is "deferred explicit".
1019
     *
1020
     * @return boolean
1021
     */
1022 69
    public function isChangeTrackingDeferredExplicit()
1023
    {
1024 69
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
1025
    }
1026
1027
    /**
1028
     * Whether the change tracking policy of this class is "deferred implicit".
1029
     *
1030
     * @return boolean
1031
     */
1032 578
    public function isChangeTrackingDeferredImplicit()
1033
    {
1034 578
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1035
    }
1036
1037
    /**
1038
     * Whether the change tracking policy of this class is "notify".
1039
     *
1040
     * @return boolean
1041
     */
1042 314
    public function isChangeTrackingNotify()
1043
    {
1044 314
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1045
    }
1046
1047
    /**
1048
     * Gets the ReflectionProperties of the mapped class.
1049
     *
1050
     * @return array An array of ReflectionProperty instances.
1051
     */
1052 99
    public function getReflectionProperties()
1053
    {
1054 99
        return $this->reflFields;
1055
    }
1056
1057
    /**
1058
     * Gets a ReflectionProperty for a specific field of the mapped class.
1059
     *
1060
     * @param string $name
1061
     *
1062
     * @return \ReflectionProperty
1063
     */
1064
    public function getReflectionProperty($name)
1065
    {
1066
        return $this->reflFields[$name];
1067
    }
1068
1069
    /**
1070
     * {@inheritDoc}
1071
     */
1072 1390
    public function getName()
1073
    {
1074 1390
        return $this->name;
1075
    }
1076
1077
    /**
1078
     * The namespace this Document class belongs to.
1079
     *
1080
     * @return string $namespace The namespace name.
1081
     */
1082
    public function getNamespace()
1083
    {
1084
        return $this->namespace;
1085
    }
1086
1087
    /**
1088
     * Returns the database this Document is mapped to.
1089
     *
1090
     * @return string $db The database name.
1091
     */
1092 1310
    public function getDatabase()
1093
    {
1094 1310
        return $this->db;
1095
    }
1096
1097
    /**
1098
     * Set the database this Document is mapped to.
1099
     *
1100
     * @param string $db The database name
1101
     */
1102 95
    public function setDatabase($db)
1103
    {
1104 95
        $this->db = $db;
1105 95
    }
1106
1107
    /**
1108
     * Get the collection this Document is mapped to.
1109
     *
1110
     * @return string $collection The collection name.
1111
     */
1112 1314
    public function getCollection()
1113
    {
1114 1314
        return $this->collection;
1115
    }
1116
1117
    /**
1118
     * Sets the collection this Document is mapped to.
1119
     *
1120
     * @param array|string $name
1121
     *
1122
     * @throws \InvalidArgumentException
1123
     */
1124 1481
    public function setCollection($name)
1125
    {
1126 1481
        if (is_array($name)) {
1127
            if ( ! isset($name['name'])) {
1128
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
1129
            }
1130
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
1131
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
1132
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
1133
            $this->collection = $name['name'];
1134
        } else {
1135 1481
            $this->collection = $name;
1136
        }
1137 1481
    }
1138
1139
    /**
1140
     * Get whether or not the documents collection is capped.
1141
     *
1142
     * @return boolean
1143
     */
1144 4
    public function getCollectionCapped()
1145
    {
1146 4
        return $this->collectionCapped;
1147
    }
1148
1149
    /**
1150
     * Set whether or not the documents collection is capped.
1151
     *
1152
     * @param boolean $bool
1153
     */
1154 1
    public function setCollectionCapped($bool)
1155
    {
1156 1
        $this->collectionCapped = $bool;
1157 1
    }
1158
1159
    /**
1160
     * Get the collection size
1161
     *
1162
     * @return integer
1163
     */
1164 4
    public function getCollectionSize()
1165
    {
1166 4
        return $this->collectionSize;
1167
    }
1168
1169
    /**
1170
     * Set the collection size.
1171
     *
1172
     * @param integer $size
1173
     */
1174 1
    public function setCollectionSize($size)
1175
    {
1176 1
        $this->collectionSize = $size;
1177 1
    }
1178
1179
    /**
1180
     * Get the collection max.
1181
     *
1182
     * @return integer
1183
     */
1184 4
    public function getCollectionMax()
1185
    {
1186 4
        return $this->collectionMax;
1187
    }
1188
1189
    /**
1190
     * Set the collection max.
1191
     *
1192
     * @param integer $max
1193
     */
1194 1
    public function setCollectionMax($max)
1195
    {
1196 1
        $this->collectionMax = $max;
1197 1
    }
1198
1199
    /**
1200
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1201
     *
1202
     * @return boolean
1203
     */
1204
    public function isMappedToCollection()
1205
    {
1206
        return $this->collection ? true : false;
1207
    }
1208
1209
    /**
1210
     * Map a field.
1211
     *
1212
     * @param array $mapping The mapping information.
1213
     *
1214
     * @return array
1215
     *
1216
     * @throws MappingException
1217
     */
1218 1431
    public function mapField(array $mapping)
1219
    {
1220 1431
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1221 9
            $mapping['fieldName'] = $mapping['name'];
1222
        }
1223 1431
        if ( ! isset($mapping['fieldName'])) {
1224
            throw MappingException::missingFieldName($this->name);
1225
        }
1226 1431
        if ( ! isset($mapping['name'])) {
1227 1421
            $mapping['name'] = $mapping['fieldName'];
1228
        }
1229 1431
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1230 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1231
        }
1232 1430
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1233
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1234
        }
1235 1430
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1236 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1237
        }
1238 1429 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...
1239 1118
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1240
        }
1241 1429
        if (isset($mapping['collectionClass'])) {
1242 54 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...
1243 52
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1244
            }
1245 54
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1246
        }
1247 1429
        if ( ! empty($mapping['collectionClass'])) {
1248 54
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1249 54
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1250 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1251
            }
1252
        }
1253
1254 1428
        if (isset($mapping['discriminatorMap'])) {
1255 115
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1256 115 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...
1257 115
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1258
                }
1259
            }
1260
        }
1261
1262 1428
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1263 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1264
        }
1265
1266 1427
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1267
1268 1427
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1269 1119
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1270
        }
1271
1272 1427
        if (isset($mapping['embedded'])) {
1273 1078
            unset($mapping['cascade']);
1274 1422
        } elseif (isset($mapping['cascade'])) {
1275 924
            $mapping['cascade'] = $cascades;
1276
        }
1277
1278 1427
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1279 1427
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1280 1427
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1281 1427
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1282 1427
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1283
1284 1427
        if (isset($mapping['id']) && $mapping['id'] === true) {
1285 1399
            $mapping['name'] = '_id';
1286 1399
            $this->identifier = $mapping['fieldName'];
1287 1399 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...
1288 1382
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1289
            }
1290 1399
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1291 1399
            switch ($this->generatorType) {
1292 1399
                case self::GENERATOR_TYPE_AUTO:
1293 1325
                    $mapping['type'] = 'id';
1294 1325
                    break;
1295
                default:
1296 149
                    if ( ! empty($this->generatorOptions['type'])) {
1297 56
                        $mapping['type'] = $this->generatorOptions['type'];
1298 93
                    } elseif (empty($mapping['type'])) {
1299 79
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1300
                    }
1301
            }
1302 1399
            unset($this->generatorOptions['type']);
1303
        }
1304
1305 1427
        if ( ! isset($mapping['nullable'])) {
1306 51
            $mapping['nullable'] = false;
1307
        }
1308
1309 1427
        if (isset($mapping['reference'])
1310 1427
            && isset($mapping['storeAs'])
1311 1427
            && $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID
1312 1427
            && ! isset($mapping['targetDocument'])
1313
        ) {
1314 3
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1315
        }
1316
1317 1424
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1318 1424
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1319 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1320
        }
1321
1322 1420
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && isset($mapping['strategy']) && CollectionHelper::isAtomic($mapping['strategy'])) {
1323 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1324
        }
1325
1326 1419 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...
1327 1021
            $mapping['association'] = self::REFERENCE_ONE;
1328
        }
1329 1419 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...
1330 975
            $mapping['association'] = self::REFERENCE_MANY;
1331
        }
1332 1419 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...
1333 955
            $mapping['association'] = self::EMBED_ONE;
1334
        }
1335 1419 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...
1336 987
            $mapping['association'] = self::EMBED_MANY;
1337
        }
1338
1339 1419
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1340 127
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1341
        }
1342
1343
        /*
1344
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1345
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1346
        }
1347
        */
1348 1419
        if (isset($mapping['version'])) {
1349 68
            $mapping['notSaved'] = true;
1350 68
            $this->setVersionMapping($mapping);
1351
        }
1352 1419
        if (isset($mapping['lock'])) {
1353 23
            $mapping['notSaved'] = true;
1354 23
            $this->setLockMapping($mapping);
1355
        }
1356 1419
        $mapping['isOwningSide'] = true;
1357 1419
        $mapping['isInverseSide'] = false;
1358 1419
        if (isset($mapping['reference'])) {
1359 1087 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...
1360 84
                $mapping['isOwningSide'] = true;
1361 84
                $mapping['isInverseSide'] = false;
1362
            }
1363 1087 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...
1364 826
                $mapping['isInverseSide'] = true;
1365 826
                $mapping['isOwningSide'] = false;
1366
            }
1367 1087 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...
1368 58
                $mapping['isInverseSide'] = true;
1369 58
                $mapping['isOwningSide'] = false;
1370
            }
1371 1087
            if (!isset($mapping['orphanRemoval'])) {
1372 1064
                $mapping['orphanRemoval'] = false;
1373
            }
1374
        }
1375
1376 1419
        if (!empty($mapping['prime']) && ($mapping['association'] !== self::REFERENCE_MANY || !$mapping['isInverseSide'])) {
1377
            throw MappingException::referencePrimersOnlySupportedForInverseReferenceMany($this->name, $mapping['fieldName']);
1378
        }
1379
1380 1419
        $this->applyStorageStrategy($mapping);
1381
1382 1418
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1383 1418
        if (isset($mapping['association'])) {
1384 1223
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1385
        }
1386
1387 1418
        return $mapping;
1388
    }
1389
1390
    /**
1391
     * Validates the storage strategy of a mapping for consistency
1392
     * @param array $mapping
1393
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1394
     */
1395 1419
    private function applyStorageStrategy(array &$mapping)
1396
    {
1397 1419
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1398 1401
            return;
1399
        }
1400
1401
        switch (true) {
1402 1381
            case $mapping['type'] == 'int':
1403 1380
            case $mapping['type'] == 'float':
1404 826
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1405 826
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1406 826
                break;
1407
1408 1380
            case $mapping['type'] == 'many':
1409 1100
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1410
                $allowedStrategies = [
1411 1100
                    self::STORAGE_STRATEGY_PUSH_ALL,
1412 1100
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1413 1100
                    self::STORAGE_STRATEGY_SET,
1414 1100
                    self::STORAGE_STRATEGY_SET_ARRAY,
1415 1100
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1416 1100
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1417
                ];
1418 1100
                break;
1419
1420
            default:
1421 1367
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1422 1367
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1423
        }
1424
1425 1381
        if (! isset($mapping['strategy'])) {
1426 1371
            $mapping['strategy'] = $defaultStrategy;
1427
        }
1428
1429 1381
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1430
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1431
        }
1432
1433 1381
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1434 1381
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1435 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1436
        }
1437 1380
    }
1438
1439
    /**
1440
     * Map a single embedded document.
1441
     *
1442
     * @param array $mapping The mapping information.
1443
     */
1444 6
    public function mapOneEmbedded(array $mapping)
1445
    {
1446 6
        $mapping['embedded'] = true;
1447 6
        $mapping['type'] = 'one';
1448 6
        $this->mapField($mapping);
1449 5
    }
1450
1451
    /**
1452
     * Map a collection of embedded documents.
1453
     *
1454
     * @param array $mapping The mapping information.
1455
     */
1456 5
    public function mapManyEmbedded(array $mapping)
1457
    {
1458 5
        $mapping['embedded'] = true;
1459 5
        $mapping['type'] = 'many';
1460 5
        $this->mapField($mapping);
1461 5
    }
1462
1463
    /**
1464
     * Map a single document reference.
1465
     *
1466
     * @param array $mapping The mapping information.
1467
     */
1468 8
    public function mapOneReference(array $mapping)
1469
    {
1470 8
        $mapping['reference'] = true;
1471 8
        $mapping['type'] = 'one';
1472 8
        $this->mapField($mapping);
1473 8
    }
1474
1475
    /**
1476
     * Map a collection of document references.
1477
     *
1478
     * @param array $mapping The mapping information.
1479
     */
1480 8
    public function mapManyReference(array $mapping)
1481
    {
1482 8
        $mapping['reference'] = true;
1483 8
        $mapping['type'] = 'many';
1484 8
        $this->mapField($mapping);
1485 8
    }
1486
1487
    /**
1488
     * INTERNAL:
1489
     * Adds a field mapping without completing/validating it.
1490
     * This is mainly used to add inherited field mappings to derived classes.
1491
     *
1492
     * @param array $fieldMapping
1493
     */
1494 119
    public function addInheritedFieldMapping(array $fieldMapping)
1495
    {
1496 119
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1497
1498 119
        if (isset($fieldMapping['association'])) {
1499 68
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1500
        }
1501 119
    }
1502
1503
    /**
1504
     * INTERNAL:
1505
     * Adds an association mapping without completing/validating it.
1506
     * This is mainly used to add inherited association mappings to derived classes.
1507
     *
1508
     * @param array $mapping
1509
     *
1510
     * @return void
1511
     *
1512
     * @throws MappingException
1513
     */
1514 69
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1515
    {
1516 69
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1517 69
    }
1518
1519
    /**
1520
     * Checks whether the class has a mapped association with the given field name.
1521
     *
1522
     * @param string $fieldName
1523
     * @return boolean
1524
     */
1525 32
    public function hasReference($fieldName)
1526
    {
1527 32
        return isset($this->fieldMappings[$fieldName]['reference']);
1528
    }
1529
1530
    /**
1531
     * Checks whether the class has a mapped embed with the given field name.
1532
     *
1533
     * @param string $fieldName
1534
     * @return boolean
1535
     */
1536 5
    public function hasEmbed($fieldName)
1537
    {
1538 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1539
    }
1540
1541
    /**
1542
     * {@inheritDoc}
1543
     *
1544
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1545
     */
1546 7
    public function hasAssociation($fieldName)
1547
    {
1548 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1549
    }
1550
1551
    /**
1552
     * {@inheritDoc}
1553
     *
1554
     * Checks whether the class has a mapped reference or embed for the specified field and
1555
     * is a single valued association.
1556
     */
1557
    public function isSingleValuedAssociation($fieldName)
1558
    {
1559
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1560
    }
1561
1562
    /**
1563
     * {@inheritDoc}
1564
     *
1565
     * Checks whether the class has a mapped reference or embed for the specified field and
1566
     * is a collection valued association.
1567
     */
1568
    public function isCollectionValuedAssociation($fieldName)
1569
    {
1570
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1571
    }
1572
1573
    /**
1574
     * Checks whether the class has a mapped association for the specified field
1575
     * and if yes, checks whether it is a single-valued association (to-one).
1576
     *
1577
     * @param string $fieldName
1578
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1579
     */
1580
    public function isSingleValuedReference($fieldName)
1581
    {
1582
        return isset($this->fieldMappings[$fieldName]['association']) &&
1583
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1584
    }
1585
1586
    /**
1587
     * Checks whether the class has a mapped association for the specified field
1588
     * and if yes, checks whether it is a collection-valued association (to-many).
1589
     *
1590
     * @param string $fieldName
1591
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1592
     */
1593
    public function isCollectionValuedReference($fieldName)
1594
    {
1595
        return isset($this->fieldMappings[$fieldName]['association']) &&
1596
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1597
    }
1598
1599
    /**
1600
     * Checks whether the class has a mapped embedded document for the specified field
1601
     * and if yes, checks whether it is a single-valued association (to-one).
1602
     *
1603
     * @param string $fieldName
1604
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1605
     */
1606
    public function isSingleValuedEmbed($fieldName)
1607
    {
1608
        return isset($this->fieldMappings[$fieldName]['association']) &&
1609
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1610
    }
1611
1612
    /**
1613
     * Checks whether the class has a mapped embedded document for the specified field
1614
     * and if yes, checks whether it is a collection-valued association (to-many).
1615
     *
1616
     * @param string $fieldName
1617
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1618
     */
1619
    public function isCollectionValuedEmbed($fieldName)
1620
    {
1621
        return isset($this->fieldMappings[$fieldName]['association']) &&
1622
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1623
    }
1624
1625
    /**
1626
     * Sets the ID generator used to generate IDs for instances of this class.
1627
     *
1628
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1629
     */
1630 1324
    public function setIdGenerator($generator)
1631
    {
1632 1324
        $this->idGenerator = $generator;
1633 1324
    }
1634
1635
    /**
1636
     * Casts the identifier to its portable PHP type.
1637
     *
1638
     * @param mixed $id
1639
     * @return mixed $id
1640
     */
1641 600
    public function getPHPIdentifierValue($id)
1642
    {
1643 600
        $idType = $this->fieldMappings[$this->identifier]['type'];
1644 600
        return Type::getType($idType)->convertToPHPValue($id);
1645
    }
1646
1647
    /**
1648
     * Casts the identifier to its database type.
1649
     *
1650
     * @param mixed $id
1651
     * @return mixed $id
1652
     */
1653 666
    public function getDatabaseIdentifierValue($id)
1654
    {
1655 666
        $idType = $this->fieldMappings[$this->identifier]['type'];
1656 666
        return Type::getType($idType)->convertToDatabaseValue($id);
1657
    }
1658
1659
    /**
1660
     * Sets the document identifier of a document.
1661
     *
1662
     * The value will be converted to a PHP type before being set.
1663
     *
1664
     * @param object $document
1665
     * @param mixed $id
1666
     */
1667 528
    public function setIdentifierValue($document, $id)
1668
    {
1669 528
        $id = $this->getPHPIdentifierValue($id);
1670 528
        $this->reflFields[$this->identifier]->setValue($document, $id);
1671 528
    }
1672
1673
    /**
1674
     * Gets the document identifier as a PHP type.
1675
     *
1676
     * @param object $document
1677
     * @return mixed $id
1678
     */
1679 614
    public function getIdentifierValue($document)
1680
    {
1681 614
        return $this->reflFields[$this->identifier]->getValue($document);
1682
    }
1683
1684
    /**
1685
     * {@inheritDoc}
1686
     *
1687
     * Since MongoDB only allows exactly one identifier field this is a proxy
1688
     * to {@see getIdentifierValue()} and returns an array with the identifier
1689
     * field as a key.
1690
     */
1691
    public function getIdentifierValues($object)
1692
    {
1693
        return array($this->identifier => $this->getIdentifierValue($object));
1694
    }
1695
1696
    /**
1697
     * Get the document identifier object as a database type.
1698
     *
1699
     * @param object $document
1700
     *
1701
     * @return \MongoDB\BSON\ObjectId $id The ObjectId
1702
     */
1703 32
    public function getIdentifierObject($document)
1704
    {
1705 32
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1706
    }
1707
1708
    /**
1709
     * Sets the specified field to the specified value on the given document.
1710
     *
1711
     * @param object $document
1712
     * @param string $field
1713
     * @param mixed $value
1714
     */
1715 8
    public function setFieldValue($document, $field, $value)
1716
    {
1717 8
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1718
            //property changes to an uninitialized proxy will not be tracked or persisted,
1719
            //so the proxy needs to be loaded first.
1720 1
            $document->__load();
1721
        }
1722
1723 8
        $this->reflFields[$field]->setValue($document, $value);
1724 8
    }
1725
1726
    /**
1727
     * Gets the specified field's value off the given document.
1728
     *
1729
     * @param object $document
1730
     * @param string $field
1731
     *
1732
     * @return mixed
1733
     */
1734 27
    public function getFieldValue($document, $field)
1735
    {
1736 27
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1737 1
            $document->__load();
1738
        }
1739
1740 27
        return $this->reflFields[$field]->getValue($document);
1741
    }
1742
1743
    /**
1744
     * Gets the mapping of a field.
1745
     *
1746
     * @param string $fieldName  The field name.
1747
     *
1748
     * @return array  The field mapping.
1749
     *
1750
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1751
     */
1752 167
    public function getFieldMapping($fieldName)
1753
    {
1754 167
        if ( ! isset($this->fieldMappings[$fieldName])) {
1755 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1756
        }
1757 165
        return $this->fieldMappings[$fieldName];
1758
    }
1759
1760
    /**
1761
     * Gets mappings of fields holding embedded document(s).
1762
     *
1763
     * @return array of field mappings
1764
     */
1765 569
    public function getEmbeddedFieldsMappings()
1766
    {
1767 569
        return array_filter(
1768 569
            $this->associationMappings,
1769
            function($assoc) { return ! empty($assoc['embedded']); }
1770
        );
1771
    }
1772
1773
    /**
1774
     * Gets the field mapping by its DB name.
1775
     * E.g. it returns identifier's mapping when called with _id.
1776
     *
1777
     * @param string $dbFieldName
1778
     *
1779
     * @return array
1780
     * @throws MappingException
1781
     */
1782 4
    public function getFieldMappingByDbFieldName($dbFieldName)
1783
    {
1784 4
        foreach ($this->fieldMappings as $mapping) {
1785 4
            if ($mapping['name'] == $dbFieldName) {
1786 4
                return $mapping;
1787
            }
1788
        }
1789
1790
        throw MappingException::mappingNotFoundByDbName($this->name, $dbFieldName);
1791
    }
1792
1793
    /**
1794
     * Check if the field is not null.
1795
     *
1796
     * @param string $fieldName  The field name
1797
     *
1798
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1799
     */
1800 1
    public function isNullable($fieldName)
1801
    {
1802 1
        $mapping = $this->getFieldMapping($fieldName);
1803 1
        if ($mapping !== false) {
1804 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1805
        }
1806
        return false;
1807
    }
1808
1809
    /**
1810
     * Checks whether the document has a discriminator field and value configured.
1811
     *
1812
     * @return boolean
1813
     */
1814 498
    public function hasDiscriminator()
1815
    {
1816 498
        return isset($this->discriminatorField, $this->discriminatorValue);
1817
    }
1818
1819
    /**
1820
     * Sets the type of Id generator to use for the mapped class.
1821
     *
1822
     * @param string $generatorType Generator type.
1823
     */
1824 901
    public function setIdGeneratorType($generatorType)
1825
    {
1826 901
        $this->generatorType = $generatorType;
1827 901
    }
1828
1829
    /**
1830
     * Sets the Id generator options.
1831
     *
1832
     * @param array $generatorOptions Generator options.
1833
     */
1834
    public function setIdGeneratorOptions($generatorOptions)
1835
    {
1836
        $this->generatorOptions = $generatorOptions;
1837
    }
1838
1839
    /**
1840
     * @return boolean
1841
     */
1842 576
    public function isInheritanceTypeNone()
1843
    {
1844 576
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1845
    }
1846
1847
    /**
1848
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1849
     *
1850
     * @return boolean
1851
     */
1852 894
    public function isInheritanceTypeSingleCollection()
1853
    {
1854 894
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1855
    }
1856
1857
    /**
1858
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1859
     *
1860
     * @return boolean
1861
     */
1862
    public function isInheritanceTypeCollectionPerClass()
1863
    {
1864
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1865
    }
1866
1867
    /**
1868
     * Sets the mapped subclasses of this class.
1869
     *
1870
     * @param string[] $subclasses The names of all mapped subclasses.
1871
     */
1872 2
    public function setSubclasses(array $subclasses)
1873
    {
1874 2
        foreach ($subclasses as $subclass) {
1875 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1876 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1877
            } else {
1878 2
                $this->subClasses[] = $subclass;
1879
            }
1880
        }
1881 2
    }
1882
1883
    /**
1884
     * Sets the parent class names.
1885
     * Assumes that the class names in the passed array are in the order:
1886
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1887
     *
1888
     * @param string[] $classNames
1889
     */
1890 1381
    public function setParentClasses(array $classNames)
1891
    {
1892 1381
        $this->parentClasses = $classNames;
1893
1894 1381
        if (count($classNames) > 0) {
1895 102
            $this->rootDocumentName = array_pop($classNames);
1896
        }
1897 1381
    }
1898
1899
    /**
1900
     * Checks whether the class will generate a new \MongoDB\BSON\ObjectId instance for us.
1901
     *
1902
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1903
     */
1904
    public function isIdGeneratorAuto()
1905
    {
1906
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1907
    }
1908
1909
    /**
1910
     * Checks whether the class will use a collection to generate incremented identifiers.
1911
     *
1912
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1913
     */
1914
    public function isIdGeneratorIncrement()
1915
    {
1916
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1917
    }
1918
1919
    /**
1920
     * Checks whether the class will generate a uuid id.
1921
     *
1922
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1923
     */
1924
    public function isIdGeneratorUuid()
1925
    {
1926
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1927
    }
1928
1929
    /**
1930
     * Checks whether the class uses no id generator.
1931
     *
1932
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1933
     */
1934
    public function isIdGeneratorNone()
1935
    {
1936
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1937
    }
1938
1939
    /**
1940
     * Sets the version field mapping used for versioning. Sets the default
1941
     * value to use depending on the column type.
1942
     *
1943
     * @param array $mapping   The version field mapping array
1944
     *
1945
     * @throws LockException
1946
     */
1947 68
    public function setVersionMapping(array &$mapping)
1948
    {
1949 68
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
1950 1
            throw LockException::invalidVersionFieldType($mapping['type']);
1951
        }
1952
1953 67
        $this->isVersioned  = true;
1954 67
        $this->versionField = $mapping['fieldName'];
1955 67
    }
1956
1957
    /**
1958
     * Sets whether this class is to be versioned for optimistic locking.
1959
     *
1960
     * @param boolean $bool
1961
     */
1962 895
    public function setVersioned($bool)
1963
    {
1964 895
        $this->isVersioned = $bool;
1965 895
    }
1966
1967
    /**
1968
     * Sets the name of the field that is to be used for versioning if this class is
1969
     * versioned for optimistic locking.
1970
     *
1971
     * @param string $versionField
1972
     */
1973 895
    public function setVersionField($versionField)
1974
    {
1975 895
        $this->versionField = $versionField;
1976 895
    }
1977
1978
    /**
1979
     * Sets the version field mapping used for versioning. Sets the default
1980
     * value to use depending on the column type.
1981
     *
1982
     * @param array $mapping   The version field mapping array
1983
     *
1984
     * @throws \Doctrine\ODM\MongoDB\LockException
1985
     */
1986 23
    public function setLockMapping(array &$mapping)
1987
    {
1988 23
        if ($mapping['type'] !== 'int') {
1989 1
            throw LockException::invalidLockFieldType($mapping['type']);
1990
        }
1991
1992 22
        $this->isLockable = true;
1993 22
        $this->lockField = $mapping['fieldName'];
1994 22
    }
1995
1996
    /**
1997
     * Sets whether this class is to allow pessimistic locking.
1998
     *
1999
     * @param boolean $bool
2000
     */
2001
    public function setLockable($bool)
2002
    {
2003
        $this->isLockable = $bool;
2004
    }
2005
2006
    /**
2007
     * Sets the name of the field that is to be used for storing whether a document
2008
     * is currently locked or not.
2009
     *
2010
     * @param string $lockField
2011
     */
2012
    public function setLockField($lockField)
2013
    {
2014
        $this->lockField = $lockField;
2015
    }
2016
2017
    /**
2018
     * Marks this class as read only, no change tracking is applied to it.
2019
     */
2020 6
    public function markReadOnly()
2021
    {
2022 6
        $this->isReadOnly = true;
2023 6
    }
2024
2025
    /**
2026
     * {@inheritDoc}
2027
     */
2028
    public function getFieldNames()
2029
    {
2030
        return array_keys($this->fieldMappings);
2031
    }
2032
2033
    /**
2034
     * {@inheritDoc}
2035
     */
2036
    public function getAssociationNames()
2037
    {
2038
        return array_keys($this->associationMappings);
2039
    }
2040
2041
    /**
2042
     * {@inheritDoc}
2043
     */
2044 23
    public function getTypeOfField($fieldName)
2045
    {
2046 23
        return isset($this->fieldMappings[$fieldName]) ?
2047 23
            $this->fieldMappings[$fieldName]['type'] : null;
2048
    }
2049
2050
    /**
2051
     * {@inheritDoc}
2052
     */
2053 6
    public function getAssociationTargetClass($assocName)
2054
    {
2055 6
        if ( ! isset($this->associationMappings[$assocName])) {
2056 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2057
        }
2058
2059 3
        return $this->associationMappings[$assocName]['targetDocument'];
2060
    }
2061
2062
    /**
2063
     * Retrieve the collectionClass associated with an association
2064
     *
2065
     * @param string $assocName
2066
     */
2067 2
    public function getAssociationCollectionClass($assocName)
2068
    {
2069 2
        if ( ! isset($this->associationMappings[$assocName])) {
2070
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2071
        }
2072
2073 2
        if ( ! array_key_exists('collectionClass', $this->associationMappings[$assocName])) {
2074
            throw new InvalidArgumentException("collectionClass can only be applied to 'embedMany' and 'referenceMany' associations.");
2075
        }
2076
2077 2
        return $this->associationMappings[$assocName]['collectionClass'];
2078
    }
2079
2080
    /**
2081
     * {@inheritDoc}
2082
     */
2083
    public function isAssociationInverseSide($fieldName)
2084
    {
2085
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2086
    }
2087
2088
    /**
2089
     * {@inheritDoc}
2090
     */
2091
    public function getAssociationMappedByTargetField($fieldName)
2092
    {
2093
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2094
    }
2095
}
2096