Completed
Push — master ( 7bfeaf...efad7d )
by Andreas
09:38
created

ClassMetadataInfo::setShardKey()   C

Complexity

Conditions 13
Paths 7

Size

Total Lines 42
Code Lines 25

Duplication

Lines 14
Ratio 33.33 %

Code Coverage

Tests 25
CRAP Score 13.0096

Importance

Changes 0
Metric Value
dl 14
loc 42
ccs 25
cts 26
cp 0.9615
rs 5.1234
c 0
b 0
f 0
cc 13
eloc 25
nc 7
nop 2
crap 13.0096

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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: Whether or not queries on this document should require indexes.
244
     *
245
     * @deprecated property was deprecated in 1.2 and will be removed in 2.0
246
     */
247
    public $requireIndexes = false;
248
249
    /**
250
     * READ-ONLY: The name of the document class.
251
     */
252
    public $name;
253
254
    /**
255
     * READ-ONLY: The namespace the document class is contained in.
256
     *
257
     * @var string
258
     * @todo Not really needed. Usage could be localized.
259
     */
260
    public $namespace;
261
262
    /**
263
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
264
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
265
     * as {@link $documentName}.
266
     *
267
     * @var string
268
     */
269
    public $rootDocumentName;
270
271
    /**
272
     * The name of the custom repository class used for the document class.
273
     * (Optional).
274
     *
275
     * @var string
276
     */
277
    public $customRepositoryClassName;
278
279
    /**
280
     * READ-ONLY: The names of the parent classes (ancestors).
281
     *
282
     * @var array
283
     */
284
    public $parentClasses = array();
285
286
    /**
287
     * READ-ONLY: The names of all subclasses (descendants).
288
     *
289
     * @var array
290
     */
291
    public $subClasses = array();
292
293
    /**
294
     * The ReflectionProperty instances of the mapped class.
295
     *
296
     * @var \ReflectionProperty[]
297
     */
298
    public $reflFields = array();
299
300
    /**
301
     * READ-ONLY: The inheritance mapping type used by the class.
302
     *
303
     * @var integer
304
     */
305
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
306
307
    /**
308
     * READ-ONLY: The Id generator type used by the class.
309
     *
310
     * @var string
311
     */
312
    public $generatorType = self::GENERATOR_TYPE_AUTO;
313
314
    /**
315
     * READ-ONLY: The Id generator options.
316
     *
317
     * @var array
318
     */
319
    public $generatorOptions = array();
320
321
    /**
322
     * READ-ONLY: The ID generator used for generating IDs for this class.
323
     *
324
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
325
     */
326
    public $idGenerator;
327
328
    /**
329
     * READ-ONLY: The field mappings of the class.
330
     * Keys are field names and values are mapping definitions.
331
     *
332
     * The mapping definition array has the following values:
333
     *
334
     * - <b>fieldName</b> (string)
335
     * The name of the field in the Document.
336
     *
337
     * - <b>id</b> (boolean, optional)
338
     * Marks the field as the primary key of the document. Multiple fields of an
339
     * document can have the id attribute, forming a composite key.
340
     *
341
     * @var array
342
     */
343
    public $fieldMappings = array();
344
345
    /**
346
     * READ-ONLY: The association mappings of the class.
347
     * Keys are field names and values are mapping definitions.
348
     *
349
     * @var array
350
     */
351
    public $associationMappings = array();
352
353
    /**
354
     * READ-ONLY: Array of fields to also load with a given method.
355
     *
356
     * @var array
357
     */
358
    public $alsoLoadMethods = array();
359
360
    /**
361
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
362
     *
363
     * @var array
364
     */
365
    public $lifecycleCallbacks = array();
366
367
    /**
368
     * READ-ONLY: The discriminator value of this class.
369
     *
370
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
371
     * where a discriminator field is used.</b>
372
     *
373
     * @var mixed
374
     * @see discriminatorField
375
     */
376
    public $discriminatorValue;
377
378
    /**
379
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
380
     *
381
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
382
     * where a discriminator field is used.</b>
383
     *
384
     * @var mixed
385
     * @see discriminatorField
386
     */
387
    public $discriminatorMap = array();
388
389
    /**
390
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
391
     * inheritance mapping.
392
     *
393
     * @var string
394
     */
395
    public $discriminatorField;
396
397
    /**
398
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
399
     *
400
     * @var string
401
     * @see discriminatorField
402
     */
403
    public $defaultDiscriminatorValue;
404
405
    /**
406
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
407
     *
408
     * @var boolean
409
     */
410
    public $isMappedSuperclass = false;
411
412
    /**
413
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
414
     *
415
     * @var boolean
416
     */
417
    public $isEmbeddedDocument = false;
418
419
    /**
420
     * READ-ONLY: Whether this class describes the mapping of an aggregation result document.
421
     *
422
     * @var boolean
423
     */
424
    public $isQueryResultDocument = false;
425
426
    /**
427
     * READ-ONLY: The policy used for change-tracking on entities of this class.
428
     *
429
     * @var integer
430
     */
431
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
432
433
    /**
434
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
435
     * with optimistic locking.
436
     *
437
     * @var boolean $isVersioned
438
     */
439
    public $isVersioned;
440
441
    /**
442
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
443
     *
444
     * @var mixed $versionField
445
     */
446
    public $versionField;
447
448
    /**
449
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
450
     * locking.
451
     *
452
     * @var boolean $isLockable
453
     */
454
    public $isLockable;
455
456
    /**
457
     * READ-ONLY: The name of the field which is used for locking a document.
458
     *
459
     * @var mixed $lockField
460
     */
461
    public $lockField;
462
463
    /**
464
     * The ReflectionClass instance of the mapped class.
465
     *
466
     * @var \ReflectionClass
467
     */
468
    public $reflClass;
469
470
    /**
471
     * READ_ONLY: A flag for whether or not this document is read-only.
472
     *
473
     * @var bool
474
     */
475
    public $isReadOnly;
476
477
    /**
478
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
479
     * metadata of the class with the given name.
480
     *
481
     * @param string $documentName The name of the document class the new instance is used for.
482
     */
483 1018
    public function __construct($documentName)
484
    {
485 1018
        $this->name = $documentName;
486 1018
        $this->rootDocumentName = $documentName;
487 1018
    }
488
489
    /**
490
     * Helper method to get reference id of ref* type references
491
     * @param mixed  $reference
492
     * @param string $storeAs
493
     * @return mixed
494
     * @internal
495
     */
496 117
    public static function getReferenceId($reference, $storeAs)
497
    {
498 117
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID ? $reference : $reference[ClassMetadataInfo::getReferencePrefix($storeAs) . 'id'];
499
    }
500
501
    /**
502
     * Returns the reference prefix used for a reference
503
     * @param string $storeAs
504
     * @return string
505
     */
506 196
    private static function getReferencePrefix($storeAs)
507
    {
508 196
        if (!in_array($storeAs, [ClassMetadataInfo::REFERENCE_STORE_AS_REF, ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF, ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB])) {
509
            throw new \LogicException('Can only get a reference prefix for DBRef and reference arrays');
510
        }
511
512 196
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_REF ? '' : '$';
513
    }
514
515
    /**
516
     * Returns a fully qualified field name for a given reference
517
     * @param string $storeAs
518
     * @param string $pathPrefix The field path prefix
519
     * @return string
520
     * @internal
521
     */
522 142
    public static function getReferenceFieldName($storeAs, $pathPrefix = '')
523
    {
524 142
        if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID) {
525 103
            return $pathPrefix;
526
        }
527
528 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...
529
    }
530
531
    /**
532
     * {@inheritDoc}
533
     */
534 946
    public function getReflectionClass()
535
    {
536 946
        if ( ! $this->reflClass) {
537 2
            $this->reflClass = new \ReflectionClass($this->name);
538
        }
539
540 946
        return $this->reflClass;
541
    }
542
543
    /**
544
     * {@inheritDoc}
545
     */
546 348
    public function isIdentifier($fieldName)
547
    {
548 348
        return $this->identifier === $fieldName;
549
    }
550
551
    /**
552
     * INTERNAL:
553
     * Sets the mapped identifier field of this class.
554
     *
555
     * @param string $identifier
556
     */
557 385
    public function setIdentifier($identifier)
558
    {
559 385
        $this->identifier = $identifier;
560 385
    }
561
562
    /**
563
     * {@inheritDoc}
564
     *
565
     * Since MongoDB only allows exactly one identifier field
566
     * this will always return an array with only one value
567
     */
568 40
    public function getIdentifier()
569
    {
570 40
        return array($this->identifier);
571
    }
572
573
    /**
574
     * {@inheritDoc}
575
     *
576
     * Since MongoDB only allows exactly one identifier field
577
     * this will always return an array with only one value
578
     */
579 98
    public function getIdentifierFieldNames()
580
    {
581 98
        return array($this->identifier);
582
    }
583
584
    /**
585
     * {@inheritDoc}
586
     */
587 585
    public function hasField($fieldName)
588
    {
589 585
        return isset($this->fieldMappings[$fieldName]);
590
    }
591
592
    /**
593
     * Sets the inheritance type used by the class and it's subclasses.
594
     *
595
     * @param integer $type
596
     */
597 401
    public function setInheritanceType($type)
598
    {
599 401
        $this->inheritanceType = $type;
600 401
    }
601
602
    /**
603
     * Checks whether a mapped field is inherited from an entity superclass.
604
     *
605
     * @param  string $fieldName
606
     *
607
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
608
     */
609 946
    public function isInheritedField($fieldName)
610
    {
611 946
        return isset($this->fieldMappings[$fieldName]['inherited']);
612
    }
613
614
    /**
615
     * Registers a custom repository class for the document class.
616
     *
617
     * @param string $repositoryClassName The class name of the custom repository.
618
     */
619 333
    public function setCustomRepositoryClass($repositoryClassName)
620
    {
621 333
        if ($this->isEmbeddedDocument || $this->isQueryResultDocument) {
622
            return;
623
        }
624
625 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...
626 4
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
627
        }
628
629 333
        $this->customRepositoryClassName = $repositoryClassName;
630 333
    }
631
632
    /**
633
     * Dispatches the lifecycle event of the given document by invoking all
634
     * registered callbacks.
635
     *
636
     * @param string $event     Lifecycle event
637
     * @param object $document  Document on which the event occurred
638
     * @param array  $arguments Arguments to pass to all callbacks
639
     * @throws \InvalidArgumentException if document class is not this class or
640
     *                                   a Proxy of this class
641
     */
642 685
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
643
    {
644 685
        if ( ! $document instanceof $this->name) {
645 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
646
        }
647
648 684
        if (empty($this->lifecycleCallbacks[$event])) {
649 670
            return;
650
        }
651
652 206
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
653 206
            if ($arguments !== null) {
654 205
                call_user_func_array(array($document, $callback), $arguments);
655
            } else {
656 206
                $document->$callback();
657
            }
658
        }
659 206
    }
660
661
    /**
662
     * Checks whether the class has callbacks registered for a lifecycle event.
663
     *
664
     * @param string $event Lifecycle event
665
     *
666
     * @return boolean
667
     */
668
    public function hasLifecycleCallbacks($event)
669
    {
670
        return ! empty($this->lifecycleCallbacks[$event]);
671
    }
672
673
    /**
674
     * Gets the registered lifecycle callbacks for an event.
675
     *
676
     * @param string $event
677
     * @return array
678
     */
679
    public function getLifecycleCallbacks($event)
680
    {
681
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
682
    }
683
684
    /**
685
     * Adds a lifecycle callback for documents of this class.
686
     *
687
     * If the callback is already registered, this is a NOOP.
688
     *
689
     * @param string $callback
690
     * @param string $event
691
     */
692 311
    public function addLifecycleCallback($callback, $event)
693
    {
694 311
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
695 1
            return;
696
        }
697
698 311
        $this->lifecycleCallbacks[$event][] = $callback;
699 311
    }
700
701
    /**
702
     * Sets the lifecycle callbacks for documents of this class.
703
     *
704
     * Any previously registered callbacks are overwritten.
705
     *
706
     * @param array $callbacks
707
     */
708 384
    public function setLifecycleCallbacks(array $callbacks)
709
    {
710 384
        $this->lifecycleCallbacks = $callbacks;
711 384
    }
712
713
    /**
714
     * Registers a method for loading document data before field hydration.
715
     *
716
     * Note: A method may be registered multiple times for different fields.
717
     * it will be invoked only once for the first field found.
718
     *
719
     * @param string       $method Method name
720
     * @param array|string $fields Database field name(s)
721
     */
722 15
    public function registerAlsoLoadMethod($method, $fields)
723
    {
724 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
725 15
    }
726
727
    /**
728
     * Sets the AlsoLoad methods for documents of this class.
729
     *
730
     * Any previously registered methods are overwritten.
731
     *
732
     * @param array $methods
733
     */
734 384
    public function setAlsoLoadMethods(array $methods)
735
    {
736 384
        $this->alsoLoadMethods = $methods;
737 384
    }
738
739
    /**
740
     * Sets the discriminator field.
741
     *
742
     * The field name is the the unmapped database field. Discriminator values
743
     * are only used to discern the hydration class and are not mapped to class
744
     * properties.
745
     *
746
     * @param string $discriminatorField
747
     *
748
     * @throws MappingException If the discriminator field conflicts with the
749
     *                          "name" attribute of a mapped field.
750
     */
751 414
    public function setDiscriminatorField($discriminatorField)
752
    {
753 414
        if ($discriminatorField === null) {
754 341
            $this->discriminatorField = null;
755
756 341
            return;
757
        }
758
759
        // Handle array argument with name/fieldName keys for BC
760 130
        if (is_array($discriminatorField)) {
761
            if (isset($discriminatorField['name'])) {
762
                $discriminatorField = $discriminatorField['name'];
763
            } elseif (isset($discriminatorField['fieldName'])) {
764
                $discriminatorField = $discriminatorField['fieldName'];
765
            }
766
        }
767
768 130
        foreach ($this->fieldMappings as $fieldMapping) {
769 4
            if ($discriminatorField == $fieldMapping['name']) {
770 4
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
771
            }
772
        }
773
774 129
        $this->discriminatorField = $discriminatorField;
775 129
    }
776
777
    /**
778
     * Sets the discriminator values used by this class.
779
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
780
     *
781
     * @param array $map
782
     *
783
     * @throws MappingException
784
     */
785 407
    public function setDiscriminatorMap(array $map)
786
    {
787 407
        foreach ($map as $value => $className) {
788 125
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
789 91
                $className = $this->namespace . '\\' . $className;
790
            }
791 125
            $this->discriminatorMap[$value] = $className;
792 125
            if ($this->name == $className) {
793 117
                $this->discriminatorValue = $value;
794
            } else {
795 120
                if ( ! class_exists($className)) {
796
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
797
                }
798 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...
799 125
                    $this->subClasses[] = $className;
800
                }
801
            }
802
        }
803 407
    }
804
805
    /**
806
     * Sets the default discriminator value to be used for this class
807
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
808
     *
809
     * @param string $defaultDiscriminatorValue
810
     *
811
     * @throws MappingException
812
     */
813 391
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
814
    {
815 391
        if ($defaultDiscriminatorValue === null) {
816 384
            $this->defaultDiscriminatorValue = null;
817
818 384
            return;
819
        }
820
821 60
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
822
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
823
        }
824
825 60
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
826 60
    }
827
828
    /**
829
     * Sets the discriminator value for this class.
830
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
831
     * collection.
832
     *
833
     * @param string $value
834
     */
835 3
    public function setDiscriminatorValue($value)
836
    {
837 3
        $this->discriminatorMap[$value] = $this->name;
838 3
        $this->discriminatorValue = $value;
839 3
    }
840
841
    /**
842
     * Sets the slaveOkay option applied to collections for this class.
843
     *
844
     * @param boolean|null $slaveOkay
845
     *
846
     * @deprecated in version 1.2 and will be removed in 2.0.
847
     *
848
     * @throws MappingException
849
     */
850 3
    public function setSlaveOkay($slaveOkay)
851
    {
852 3
        if ($slaveOkay) {
853 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...
854 2
                sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.', __METHOD__),
855 2
                E_USER_DEPRECATED
856
            );
857
        }
858
859 3
        if ($this->readPreference) {
860
            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...
861
        }
862 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...
863 3
    }
864
865
    /**
866
     * Add a index for this Document.
867
     *
868
     * @param array $keys Array of keys for the index.
869
     * @param array $options Array of options for the index.
870
     */
871 243
    public function addIndex($keys, array $options = array())
872
    {
873 243
        $this->indexes[] = array(
874 243 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...
875 243
                if ($value == 1 || $value == -1) {
876 63
                    return (int) $value;
877
                }
878 235
                if (is_string($value)) {
879 235
                    $lower = strtolower($value);
880 235
                    if ($lower === 'asc') {
881 228
                        return 1;
882 11
                    } elseif ($lower === 'desc') {
883 4
                        return -1;
884
                    }
885
                }
886 7
                return $value;
887 243
            }, $keys),
888 243
            'options' => $options
889
        );
890 243
    }
891
892
    /**
893
     * Set whether or not queries on this document should require indexes.
894
     *
895
     * @param bool $requireIndexes
896
     *
897
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
898
     */
899 937
    public function setRequireIndexes($requireIndexes)
900
    {
901 937
        if ($requireIndexes) {
902 24
            @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...
903 24
                'requireIndexes was deprecated in version 1.2 and will be removed altogether in 2.0.',
904 24
                E_USER_DEPRECATED
905
            );
906
        }
907
908 937
        $this->requireIndexes = $requireIndexes;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...taInfo::$requireIndexes has been deprecated with message: property was deprecated in 1.2 and will be removed in 2.0

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

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

Loading history...
909 937
    }
910
911
    /**
912
     * Returns the array of indexes for this Document.
913
     *
914
     * @return array $indexes The array of indexes.
915
     */
916 54
    public function getIndexes()
917
    {
918 54
        return $this->indexes;
919
    }
920
921
    /**
922
     * Checks whether this document has indexes or not.
923
     *
924
     * @return boolean
925
     */
926
    public function hasIndexes()
927
    {
928
        return $this->indexes ? true : false;
929
    }
930
931
    /**
932
     * Set shard key for this Document.
933
     *
934
     * @param array $keys Array of document keys.
935
     * @param array $options Array of sharding options.
936
     *
937
     * @throws MappingException
938
     */
939 89
    public function setShardKey(array $keys, array $options = array())
940
    {
941 89
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
942 2
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
943
        }
944
945 89
        if ($this->isEmbeddedDocument) {
946 2
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
947
        }
948
949 87
        foreach (array_keys($keys) as $field) {
950 87
            if (! isset($this->fieldMappings[$field])) {
951 80
                continue;
952
            }
953
954 7
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
955 3
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
956
            }
957
958 4
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
959 4
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
960
            }
961
        }
962
963 83
        $this->shardKey = array(
964 83 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...
965 83
                if ($value == 1 || $value == -1) {
966 6
                    return (int) $value;
967
                }
968 82
                if (is_string($value)) {
969 82
                    $lower = strtolower($value);
970 82
                    if ($lower === 'asc') {
971 81
                        return 1;
972 54
                    } elseif ($lower === 'desc') {
973
                        return -1;
974
                    }
975
                }
976 54
                return $value;
977 83
            }, $keys),
978 83
            'options' => $options
979
        );
980 83
    }
981
982
    /**
983
     * @return array
984
     */
985 28
    public function getShardKey()
986
    {
987 28
        return $this->shardKey;
988
    }
989
990
    /**
991
     * Checks whether this document has shard key or not.
992
     *
993
     * @return bool
994
     */
995 627
    public function isSharded()
996
    {
997 627
        return $this->shardKey ? true : false;
998
    }
999
1000
    /**
1001
     * Sets the read preference used by this class.
1002
     *
1003
     * @param string $readPreference
1004
     * @param array|null $tags
1005
     *
1006
     * @throws MappingException
1007
     */
1008 388
    public function setReadPreference($readPreference, $tags)
1009
    {
1010 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...
1011
            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...
1012
        }
1013 388
        $this->readPreference = $readPreference;
1014 388
        $this->readPreferenceTags = $tags;
1015 388
    }
1016
1017
    /**
1018
     * Sets the write concern used by this class.
1019
     *
1020
     * @param string $writeConcern
1021
     */
1022 398
    public function setWriteConcern($writeConcern)
1023
    {
1024 398
        $this->writeConcern = $writeConcern;
1025 398
    }
1026
1027
    /**
1028
     * @return string
1029
     */
1030 12
    public function getWriteConcern()
1031
    {
1032 12
        return $this->writeConcern;
1033
    }
1034
1035
    /**
1036
     * Whether there is a write concern configured for this class.
1037
     *
1038
     * @return bool
1039
     */
1040 629
    public function hasWriteConcern()
1041
    {
1042 629
        return $this->writeConcern !== null;
1043
    }
1044
1045
    /**
1046
     * Sets the change tracking policy used by this class.
1047
     *
1048
     * @param integer $policy
1049
     */
1050 389
    public function setChangeTrackingPolicy($policy)
1051
    {
1052 389
        $this->changeTrackingPolicy = $policy;
1053 389
    }
1054
1055
    /**
1056
     * Whether the change tracking policy of this class is "deferred explicit".
1057
     *
1058
     * @return boolean
1059
     */
1060 75
    public function isChangeTrackingDeferredExplicit()
1061
    {
1062 75
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
1063
    }
1064
1065
    /**
1066
     * Whether the change tracking policy of this class is "deferred implicit".
1067
     *
1068
     * @return boolean
1069
     */
1070 650
    public function isChangeTrackingDeferredImplicit()
1071
    {
1072 650
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1073
    }
1074
1075
    /**
1076
     * Whether the change tracking policy of this class is "notify".
1077
     *
1078
     * @return boolean
1079
     */
1080 352
    public function isChangeTrackingNotify()
1081
    {
1082 352
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1083
    }
1084
1085
    /**
1086
     * Gets the ReflectionProperties of the mapped class.
1087
     *
1088
     * @return array An array of ReflectionProperty instances.
1089
     */
1090 98
    public function getReflectionProperties()
1091
    {
1092 98
        return $this->reflFields;
1093
    }
1094
1095
    /**
1096
     * Gets a ReflectionProperty for a specific field of the mapped class.
1097
     *
1098
     * @param string $name
1099
     *
1100
     * @return \ReflectionProperty
1101
     */
1102
    public function getReflectionProperty($name)
1103
    {
1104
        return $this->reflFields[$name];
1105
    }
1106
1107
    /**
1108
     * {@inheritDoc}
1109
     */
1110 952
    public function getName()
1111
    {
1112 952
        return $this->name;
1113
    }
1114
1115
    /**
1116
     * The namespace this Document class belongs to.
1117
     *
1118
     * @return string $namespace The namespace name.
1119
     */
1120
    public function getNamespace()
1121
    {
1122
        return $this->namespace;
1123
    }
1124
1125
    /**
1126
     * Returns the database this Document is mapped to.
1127
     *
1128
     * @return string $db The database name.
1129
     */
1130 870
    public function getDatabase()
1131
    {
1132 870
        return $this->db;
1133
    }
1134
1135
    /**
1136
     * Set the database this Document is mapped to.
1137
     *
1138
     * @param string $db The database name
1139
     */
1140 104
    public function setDatabase($db)
1141
    {
1142 104
        $this->db = $db;
1143 104
    }
1144
1145
    /**
1146
     * Get the collection this Document is mapped to.
1147
     *
1148
     * @return string $collection The collection name.
1149
     */
1150 875
    public function getCollection()
1151
    {
1152 875
        return $this->collection;
1153
    }
1154
1155
    /**
1156
     * Sets the collection this Document is mapped to.
1157
     *
1158
     * @param array|string $name
1159
     *
1160
     * @throws \InvalidArgumentException
1161
     */
1162 982
    public function setCollection($name)
1163
    {
1164 982
        if (is_array($name)) {
1165
            if ( ! isset($name['name'])) {
1166
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
1167
            }
1168
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
1169
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
1170
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
1171
            $this->collection = $name['name'];
1172
        } else {
1173 982
            $this->collection = $name;
1174
        }
1175 982
    }
1176
1177
    /**
1178
     * Get whether or not the documents collection is capped.
1179
     *
1180
     * @return boolean
1181
     */
1182 4
    public function getCollectionCapped()
1183
    {
1184 4
        return $this->collectionCapped;
1185
    }
1186
1187
    /**
1188
     * Set whether or not the documents collection is capped.
1189
     *
1190
     * @param boolean $bool
1191
     */
1192 1
    public function setCollectionCapped($bool)
1193
    {
1194 1
        $this->collectionCapped = $bool;
1195 1
    }
1196
1197
    /**
1198
     * Get the collection size
1199
     *
1200
     * @return integer
1201
     */
1202 4
    public function getCollectionSize()
1203
    {
1204 4
        return $this->collectionSize;
1205
    }
1206
1207
    /**
1208
     * Set the collection size.
1209
     *
1210
     * @param integer $size
1211
     */
1212 1
    public function setCollectionSize($size)
1213
    {
1214 1
        $this->collectionSize = $size;
1215 1
    }
1216
1217
    /**
1218
     * Get the collection max.
1219
     *
1220
     * @return integer
1221
     */
1222 4
    public function getCollectionMax()
1223
    {
1224 4
        return $this->collectionMax;
1225
    }
1226
1227
    /**
1228
     * Set the collection max.
1229
     *
1230
     * @param integer $max
1231
     */
1232 1
    public function setCollectionMax($max)
1233
    {
1234 1
        $this->collectionMax = $max;
1235 1
    }
1236
1237
    /**
1238
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1239
     *
1240
     * @return boolean
1241
     */
1242
    public function isMappedToCollection()
1243
    {
1244
        return $this->collection ? true : false;
1245
    }
1246
1247
    /**
1248
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1249
     *
1250
     * @return boolean
1251
     */
1252 816
    public function isFile()
1253
    {
1254 816
        return $this->file ? true : false;
1255
    }
1256
1257
    /**
1258
     * Returns the file field name.
1259
     *
1260
     * @return string $file The file field name.
1261
     */
1262 384
    public function getFile()
1263
    {
1264 384
        return $this->file;
1265
    }
1266
1267
    /**
1268
     * Set the field name that stores the grid file.
1269
     *
1270
     * @param string $file
1271
     */
1272 385
    public function setFile($file)
1273
    {
1274 385
        $this->file = $file;
1275 385
    }
1276
1277
    /**
1278
     * Returns the distance field name.
1279
     *
1280
     * @return string $distance The distance field name.
1281
     */
1282
    public function getDistance()
1283
    {
1284
        return $this->distance;
1285
    }
1286
1287
    /**
1288
     * Set the field name that stores the distance.
1289
     *
1290
     * @param string $distance
1291
     */
1292 1
    public function setDistance($distance)
1293
    {
1294 1
        $this->distance = $distance;
1295 1
    }
1296
1297
    /**
1298
     * Map a field.
1299
     *
1300
     * @param array $mapping The mapping information.
1301
     *
1302
     * @return array
1303
     *
1304
     * @throws MappingException
1305
     */
1306 996
    public function mapField(array $mapping)
1307
    {
1308 996
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1309 10
            $mapping['fieldName'] = $mapping['name'];
1310
        }
1311 996
        if ( ! isset($mapping['fieldName'])) {
1312
            throw MappingException::missingFieldName($this->name);
1313
        }
1314 996
        if ( ! isset($mapping['name'])) {
1315 986
            $mapping['name'] = $mapping['fieldName'];
1316
        }
1317 996
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1318 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1319
        }
1320 995
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1321
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1322
        }
1323 995
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1324 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1325
        }
1326 994 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...
1327 644
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1328
        }
1329 994
        if (isset($mapping['collectionClass'])) {
1330 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...
1331 63
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1332
            }
1333 65
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1334
        }
1335 994
        if ( ! empty($mapping['collectionClass'])) {
1336 65
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1337 65
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1338 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1339
            }
1340
        }
1341
1342 993
        if (isset($mapping['discriminatorMap'])) {
1343 129
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1344 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...
1345 129
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1346
                }
1347
            }
1348
        }
1349
1350 993
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1351 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1352
        }
1353
1354 992
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1355
1356 992
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1357 665
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1358
        }
1359
1360 992
        if (isset($mapping['embedded'])) {
1361 622
            unset($mapping['cascade']);
1362 987
        } elseif (isset($mapping['cascade'])) {
1363 424
            $mapping['cascade'] = $cascades;
1364
        }
1365
1366 992
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1367 992
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1368 992
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1369 992
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1370 992
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1371
1372 992
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1373 63
            $mapping['file'] = true;
1374
        }
1375 992
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1376 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1377
        }
1378 992 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...
1379 63
            $this->file = $mapping['fieldName'];
1380 63
            $mapping['name'] = 'file';
1381
        }
1382 992 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...
1383 7
            $this->distance = $mapping['fieldName'];
1384
        }
1385 992
        if (isset($mapping['id']) && $mapping['id'] === true) {
1386 964
            $mapping['name'] = '_id';
1387 964
            $this->identifier = $mapping['fieldName'];
1388 964 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...
1389 945
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1390
            }
1391 964
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1392 964
            switch ($this->generatorType) {
1393 964
                case self::GENERATOR_TYPE_AUTO:
1394 889
                    $mapping['type'] = 'id';
1395 889
                    break;
1396
                default:
1397 158
                    if ( ! empty($this->generatorOptions['type'])) {
1398 56
                        $mapping['type'] = $this->generatorOptions['type'];
1399 102
                    } elseif (empty($mapping['type'])) {
1400 87
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1401
                    }
1402
            }
1403 964
            unset($this->generatorOptions['type']);
1404
        }
1405
1406 992
        if ( ! isset($mapping['nullable'])) {
1407 53
            $mapping['nullable'] = false;
1408
        }
1409
1410
        // Synchronize the "simple" and "storeAs" mapping information for backwards compatibility
1411 992
        if (isset($mapping['simple']) && ($mapping['simple'] === true || $mapping['simple'] === 'true')) {
1412 307
            $mapping['storeAs'] = ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1413 307
            @trigger_error('"simple" attribute of a reference is deprecated - use storeAs="id" instead.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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