Completed
Pull Request — master (#1668)
by Andreas
04:58
created

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