Completed
Pull Request — master (#1620)
by Maciej
24:08
created

ClassMetadataInfo::setReadPreference()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 2
crap 2
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 1001
     *
473
     * @var bool
474 1001
     */
475 1001
    public $isReadOnly;
476 1001
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
    public function __construct($documentName)
484
    {
485 117
        $this->name = $documentName;
486
        $this->rootDocumentName = $documentName;
487 117
    }
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 196
     */
496
    public static function getReferenceId($reference, $storeAs)
497 196
    {
498
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID ? $reference : $reference[ClassMetadataInfo::getReferencePrefix($storeAs) . 'id'];
499
    }
500
501 196
    /**
502
     * Returns the reference prefix used for a reference
503
     * @param string $storeAs
504
     * @return string
505
     */
506
    private static function getReferencePrefix($storeAs)
507
    {
508
        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 142
512
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_REF ? '' : '$';
513 142
    }
514 103
515
    /**
516
     * Returns a fully qualified field name for a given reference
517 133
     * @param string $storeAs
518
     * @param string $pathPrefix The field path prefix
519
     * @return string
520
     * @internal
521
     */
522
    public static function getReferenceFieldName($storeAs, $pathPrefix = '')
523 929
    {
524
        if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID) {
525 929
            return $pathPrefix;
526 2
        }
527
528
        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 929
    }
530
531
    /**
532
     * {@inheritDoc}
533
     */
534
    public function getReflectionClass()
535 337
    {
536
        if ( ! $this->reflClass) {
537 337
            $this->reflClass = new \ReflectionClass($this->name);
538
        }
539
540
        return $this->reflClass;
541
    }
542
543
    /**
544
     * {@inheritDoc}
545
     */
546 374
    public function isIdentifier($fieldName)
547
    {
548 374
        return $this->identifier === $fieldName;
549 374
    }
550
551
    /**
552
     * INTERNAL:
553
     * Sets the mapped identifier field of this class.
554
     *
555
     * @param string $identifier
556
     */
557 40
    public function setIdentifier($identifier)
558
    {
559 40
        $this->identifier = $identifier;
560
    }
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 98
    public function getIdentifier()
569
    {
570 98
        return array($this->identifier);
571
    }
572
573
    /**
574
     * {@inheritDoc}
575
     *
576 573
     * Since MongoDB only allows exactly one identifier field
577
     * this will always return an array with only one value
578 573
     */
579
    public function getIdentifierFieldNames()
580
    {
581
        return array($this->identifier);
582
    }
583
584
    /**
585
     * {@inheritDoc}
586 390
     */
587
    public function hasField($fieldName)
588 390
    {
589 390
        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
    public function setInheritanceType($type)
598 929
    {
599
        $this->inheritanceType = $type;
600 929
    }
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 322
     */
609
    public function isInheritedField($fieldName)
610 322
    {
611
        return isset($this->fieldMappings[$fieldName]['inherited']);
612
    }
613
614 322
    /**
615 4
     * Registers a custom repository class for the document class.
616
     *
617
     * @param string $repositoryClassName The class name of the custom repository.
618 322
     */
619 322
    public function setCustomRepositoryClass($repositoryClassName)
620
    {
621
        if ($this->isEmbeddedDocument || $this->isQueryResultDocument) {
622
            return;
623
        }
624
625 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
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
627
        }
628
629
        $this->customRepositoryClassName = $repositoryClassName;
630
    }
631 678
632
    /**
633 678
     * Dispatches the lifecycle event of the given document by invoking all
634 1
     * registered callbacks.
635
     *
636
     * @param string $event     Lifecycle event
637 677
     * @param object $document  Document on which the event occurred
638 663
     * @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 200
     */
642 200
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
643 199
    {
644
        if ( ! $document instanceof $this->name) {
645 200
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
646
        }
647
648 200
        if (empty($this->lifecycleCallbacks[$event])) {
649
            return;
650
        }
651
652
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
653
            if ($arguments !== null) {
654
                call_user_func_array(array($document, $callback), $arguments);
655
            } else {
656
                $document->$callback();
657
            }
658
        }
659
    }
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 301
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
682
    }
683 301
684 1
    /**
685
     * Adds a lifecycle callback for documents of this class.
686
     *
687 301
     * If the callback is already registered, this is a NOOP.
688 301
     *
689
     * @param string $callback
690
     * @param string $event
691
     */
692
    public function addLifecycleCallback($callback, $event)
693
    {
694
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
695
            return;
696
        }
697 373
698
        $this->lifecycleCallbacks[$event][] = $callback;
699 373
    }
700 373
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
    public function setLifecycleCallbacks(array $callbacks)
709
    {
710
        $this->lifecycleCallbacks = $callbacks;
711 15
    }
712
713 15
    /**
714 15
     * 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
    public function registerAlsoLoadMethod($method, $fields)
723 373
    {
724
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
725 373
    }
726 373
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
    public function setAlsoLoadMethods(array $methods)
735
    {
736
        $this->alsoLoadMethods = $methods;
737
    }
738
739
    /**
740 403
     * Sets the discriminator field.
741
     *
742 403
     * The field name is the the unmapped database field. Discriminator values
743 330
     * are only used to discern the hydration class and are not mapped to class
744
     * properties.
745 330
     *
746
     * @param string $discriminatorField
747
     *
748
     * @throws MappingException If the discriminator field conflicts with the
749 130
     *                          "name" attribute of a mapped field.
750
     */
751
    public function setDiscriminatorField($discriminatorField)
752
    {
753
        if ($discriminatorField === null) {
754
            $this->discriminatorField = null;
755
756
            return;
757 130
        }
758 4
759 4
        // Handle array argument with name/fieldName keys for BC
760
        if (is_array($discriminatorField)) {
761
            if (isset($discriminatorField['name'])) {
762
                $discriminatorField = $discriminatorField['name'];
763 129
            } elseif (isset($discriminatorField['fieldName'])) {
764 129
                $discriminatorField = $discriminatorField['fieldName'];
765
            }
766
        }
767
768
        foreach ($this->fieldMappings as $fieldMapping) {
769
            if ($discriminatorField == $fieldMapping['name']) {
770
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
771
            }
772
        }
773
774 396
        $this->discriminatorField = $discriminatorField;
775
    }
776 396
777 125
    /**
778 91
     * Sets the discriminator values used by this class.
779
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
780 125
     *
781 125
     * @param array $map
782 117
     *
783
     * @throws MappingException
784 120
     */
785
    public function setDiscriminatorMap(array $map)
786
    {
787 120
        foreach ($map as $value => $className) {
788 125
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
789
                $className = $this->namespace . '\\' . $className;
790
            }
791
            $this->discriminatorMap[$value] = $className;
792 396
            if ($this->name == $className) {
793
                $this->discriminatorValue = $value;
794
            } else {
795
                if ( ! class_exists($className)) {
796
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
797
                }
798
                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
                    $this->subClasses[] = $className;
800
                }
801
            }
802 380
        }
803
    }
804 380
805 373
    /**
806
     * Sets the default discriminator value to be used for this class
807 373
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
808
     *
809
     * @param string $defaultDiscriminatorValue
810 60
     *
811
     * @throws MappingException
812
     */
813
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
814 60
    {
815 60
        if ($defaultDiscriminatorValue === null) {
816
            $this->defaultDiscriminatorValue = null;
817
818
            return;
819
        }
820
821
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
822
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
823
        }
824 3
825
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
826 3
    }
827 3
828 3
    /**
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
    public function setDiscriminatorValue($value)
836
    {
837 3
        $this->discriminatorMap[$value] = $this->name;
838
        $this->discriminatorValue = $value;
839 3
    }
840 3
841 3
    /**
842
     * Sets the slaveOkay option applied to collections for this class.
843 3
     *
844 3
     * @param boolean|null $slaveOkay
845
     *
846
     * @deprecated in version 1.2 and will be removed in 2.0.
847
     *
848
     * @throws MappingException
849
     */
850
    public function setSlaveOkay($slaveOkay)
851
    {
852 235
        if ($slaveOkay) {
853
            @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 235
                sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.', __METHOD__),
855 235
                E_USER_DEPRECATED
856 235
            );
857 63
        }
858
859 227
        if ($this->readPreference) {
860 227
            throw MappingException::canNotCombineReadPreferenceAndSlaveOkay($this->getName());
861 227
        }
862 220
        $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 11
    }
864 4
865
    /**
866
     * Add a index for this Document.
867 7
     *
868 235
     * @param array $keys Array of keys for the index.
869 235
     * @param array $options Array of options for the index.
870
     */
871 235
    public function addIndex($keys, array $options = array())
872
    {
873
        $this->indexes[] = array(
874 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
                if ($value == 1 || $value == -1) {
876
                    return (int) $value;
877
                }
878
                if (is_string($value)) {
879
                    $lower = strtolower($value);
880 920
                    if ($lower === 'asc') {
881
                        return 1;
882 920
                    } elseif ($lower === 'desc') {
883 920
                        return -1;
884 920
                    }
885
                }
886 920
                return $value;
887 920
            }, $keys),
888
            'options' => $options
889
        );
890
    }
891
892
    /**
893
     * Set whether or not queries on this document should require indexes.
894 54
     *
895
     * @param bool $requireIndexes
896 54
     *
897
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
898
     */
899
    public function setRequireIndexes($requireIndexes)
900
    {
901
        if ($requireIndexes) {
902
            @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
                'requireIndexes was deprecated in version 1.2 and will be removed altogether in 2.0.',
904
                E_USER_DEPRECATED
905
            );
906
        }
907
908
        $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
    }
910
911
    /**
912
     * Returns the array of indexes for this Document.
913
     *
914
     * @return array $indexes The array of indexes.
915
     */
916
    public function getIndexes()
917 87
    {
918
        return $this->indexes;
919 87
    }
920 2
921
    /**
922
     * Checks whether this document has indexes or not.
923 87
     *
924 2
     * @return boolean
925
     */
926
    public function hasIndexes()
927 85
    {
928 85
        return $this->indexes ? true : false;
929 78
    }
930
931
    /**
932 7
     * Set shard key for this Document.
933 3
     *
934
     * @param array $keys Array of document keys.
935
     * @param array $options Array of sharding options.
936 4
     *
937 4
     * @throws MappingException
938
     */
939
    public function setShardKey(array $keys, array $options = array())
940
    {
941 81
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
942 81
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
943 81
        }
944 6
945
        if ($this->isEmbeddedDocument) {
946 80
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
947 80
        }
948 80
949 79
        foreach (array_keys($keys) as $field) {
950 53
            if (! isset($this->fieldMappings[$field])) {
951
                continue;
952
            }
953
954 53
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
955 81
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
956 81
            }
957
958 81
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
959
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
960
            }
961
        }
962
963 28
        $this->shardKey = array(
964 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 28
                if ($value == 1 || $value == -1) {
966
                    return (int) $value;
967
                }
968
                if (is_string($value)) {
969
                    $lower = strtolower($value);
970
                    if ($lower === 'asc') {
971
                        return 1;
972
                    } elseif ($lower === 'desc') {
973 616
                        return -1;
974
                    }
975 616
                }
976
                return $value;
977
            }, $keys),
978
            'options' => $options
979
        );
980
    }
981
982
    /**
983 387
     * @return array
984
     */
985 387
    public function getShardKey()
986 387
    {
987
        return $this->shardKey;
988
    }
989
990
    /**
991 12
     * Checks whether this document has shard key or not.
992
     *
993 12
     * @return bool
994
     */
995
    public function isSharded()
996
    {
997
        return $this->shardKey ? true : false;
998
    }
999
1000
    /**
1001 622
     * Sets the read preference used by this class.
1002
     *
1003 622
     * @param string $readPreference
1004
     * @param array|null $tags
1005
     *
1006
     * @throws MappingException
1007
     */
1008
    public function setReadPreference($readPreference, $tags)
1009
    {
1010
        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 378
            throw MappingException::canNotCombineReadPreferenceAndSlaveOkay($this->getName());
1012
        }
1013 378
        $this->readPreference = $readPreference;
1014 378
        $this->readPreferenceTags = $tags;
1015
    }
1016
1017
    /**
1018
     * Sets the write concern used by this class.
1019
     *
1020
     * @param string $writeConcern
1021 75
     */
1022
    public function setWriteConcern($writeConcern)
1023 75
    {
1024
        $this->writeConcern = $writeConcern;
1025
    }
1026
1027
    /**
1028
     * @return string
1029
     */
1030
    public function getWriteConcern()
1031 643
    {
1032
        return $this->writeConcern;
1033 643
    }
1034
1035
    /**
1036
     * Whether there is a write concern configured for this class.
1037
     *
1038
     * @return bool
1039
     */
1040
    public function hasWriteConcern()
1041 352
    {
1042
        return $this->writeConcern !== null;
1043 352
    }
1044
1045
    /**
1046
     * Sets the change tracking policy used by this class.
1047
     *
1048
     * @param integer $policy
1049
     */
1050
    public function setChangeTrackingPolicy($policy)
1051 98
    {
1052
        $this->changeTrackingPolicy = $policy;
1053 98
    }
1054
1055
    /**
1056
     * Whether the change tracking policy of this class is "deferred explicit".
1057
     *
1058
     * @return boolean
1059
     */
1060
    public function isChangeTrackingDeferredExplicit()
1061
    {
1062
        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
    public function isChangeTrackingDeferredImplicit()
1071 935
    {
1072
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1073 935
    }
1074
1075
    /**
1076
     * Whether the change tracking policy of this class is "notify".
1077
     *
1078
     * @return boolean
1079
     */
1080
    public function isChangeTrackingNotify()
1081
    {
1082
        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
    public function getReflectionProperties()
1091 854
    {
1092
        return $this->reflFields;
1093 854
    }
1094
1095
    /**
1096
     * Gets a ReflectionProperty for a specific field of the mapped class.
1097
     *
1098
     * @param string $name
1099
     *
1100
     * @return \ReflectionProperty
1101 104
     */
1102
    public function getReflectionProperty($name)
1103 104
    {
1104 104
        return $this->reflFields[$name];
1105
    }
1106
1107
    /**
1108
     * {@inheritDoc}
1109
     */
1110
    public function getName()
1111 859
    {
1112
        return $this->name;
1113 859
    }
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 965
    }
1124
1125 965
    /**
1126
     * Returns the database this Document is mapped to.
1127
     *
1128
     * @return string $db The database name.
1129
     */
1130
    public function getDatabase()
1131
    {
1132
        return $this->db;
1133
    }
1134 965
1135
    /**
1136 965
     * Set the database this Document is mapped to.
1137
     *
1138
     * @param string $db The database name
1139
     */
1140
    public function setDatabase($db)
1141
    {
1142
        $this->db = $db;
1143 4
    }
1144
1145 4
    /**
1146
     * Get the collection this Document is mapped to.
1147
     *
1148
     * @return string $collection The collection name.
1149
     */
1150
    public function getCollection()
1151
    {
1152
        return $this->collection;
1153 1
    }
1154
1155 1
    /**
1156 1
     * Sets the collection this Document is mapped to.
1157
     *
1158
     * @param array|string $name
1159
     *
1160
     * @throws \InvalidArgumentException
1161
     */
1162
    public function setCollection($name)
1163 4
    {
1164
        if (is_array($name)) {
1165 4
            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 1
            $this->collection = $name;
1174
        }
1175 1
    }
1176 1
1177
    /**
1178
     * Get whether or not the documents collection is capped.
1179
     *
1180
     * @return boolean
1181
     */
1182
    public function getCollectionCapped()
1183 4
    {
1184
        return $this->collectionCapped;
1185 4
    }
1186
1187
    /**
1188
     * Set whether or not the documents collection is capped.
1189
     *
1190
     * @param boolean $bool
1191
     */
1192
    public function setCollectionCapped($bool)
1193 1
    {
1194
        $this->collectionCapped = $bool;
1195 1
    }
1196 1
1197
    /**
1198
     * Get the collection size
1199
     *
1200
     * @return integer
1201
     */
1202
    public function getCollectionSize()
1203
    {
1204
        return $this->collectionSize;
1205
    }
1206
1207
    /**
1208
     * Set the collection size.
1209
     *
1210
     * @param integer $size
1211
     */
1212
    public function setCollectionSize($size)
1213 800
    {
1214
        $this->collectionSize = $size;
1215 800
    }
1216
1217
    /**
1218
     * Get the collection max.
1219
     *
1220
     * @return integer
1221
     */
1222
    public function getCollectionMax()
1223 373
    {
1224
        return $this->collectionMax;
1225 373
    }
1226
1227
    /**
1228
     * Set the collection max.
1229
     *
1230
     * @param integer $max
1231
     */
1232
    public function setCollectionMax($max)
1233 374
    {
1234
        $this->collectionMax = $max;
1235 374
    }
1236 374
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
    public function isFile()
1253 1
    {
1254
        return $this->file ? true : false;
1255 1
    }
1256 1
1257
    /**
1258
     * Returns the file field name.
1259
     *
1260
     * @return string $file The file field name.
1261
     */
1262
    public function getFile()
1263
    {
1264
        return $this->file;
1265
    }
1266
1267 979
    /**
1268
     * Set the field name that stores the grid file.
1269 979
     *
1270 10
     * @param string $file
1271
     */
1272 979
    public function setFile($file)
1273
    {
1274
        $this->file = $file;
1275 979
    }
1276 969
1277
    /**
1278 979
     * Returns the distance field name.
1279 1
     *
1280
     * @return string $distance The distance field name.
1281 978
     */
1282
    public function getDistance()
1283
    {
1284 978
        return $this->distance;
1285 1
    }
1286
1287 977
    /**
1288 627
     * Set the field name that stores the distance.
1289
     *
1290 977
     * @param string $distance
1291 65
     */
1292 63
    public function setDistance($distance)
1293
    {
1294 65
        $this->distance = $distance;
1295
    }
1296 977
1297 65
    /**
1298 65
     * Map a field.
1299 1
     *
1300
     * @param array $mapping The mapping information.
1301
     *
1302
     * @return array
1303 976
     *
1304 129
     * @throws MappingException
1305 129
     */
1306 129
    public function mapField(array $mapping)
1307
    {
1308
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1309
            $mapping['fieldName'] = $mapping['name'];
1310
        }
1311 976
        if ( ! isset($mapping['fieldName'])) {
1312 1
            throw MappingException::missingFieldName($this->name);
1313
        }
1314
        if ( ! isset($mapping['name'])) {
1315 975
            $mapping['name'] = $mapping['fieldName'];
1316
        }
1317 975
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1318 654
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1319
        }
1320
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1321 975
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1322 611
        }
1323 970
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1324 413
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1325
        }
1326 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 975
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1328 975
        }
1329 975
        if (isset($mapping['collectionClass'])) {
1330 975 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 975
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1332
            }
1333 975
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1334 63
        }
1335
        if ( ! empty($mapping['collectionClass'])) {
1336 975
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1337 1
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1338
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1339 975
            }
1340 63
        }
1341 63
1342
        if (isset($mapping['discriminatorMap'])) {
1343 975
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1344 7 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
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1346 975
                }
1347 947
            }
1348 947
        }
1349 947
1350 928
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1351
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1352 947
        }
1353 947
1354 947
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1355 873
1356 873
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1357
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1358 157
        }
1359 56
1360 101
        if (isset($mapping['embedded'])) {
1361 86
            unset($mapping['cascade']);
1362
        } elseif (isset($mapping['cascade'])) {
1363
            $mapping['cascade'] = $cascades;
1364 947
        }
1365
1366
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1367 975
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1368 53
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1369
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1370
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1371
1372 975
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1373 297
            $mapping['file'] = true;
1374 297
        }
1375
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1376
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1377 975
        }
1378 594 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
            $this->file = $mapping['fieldName'];
1380
            $mapping['name'] = 'file';
1381 975
        }
1382 975 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 975
            $this->distance = $mapping['fieldName'];
1384 975
        }
1385
        if (isset($mapping['id']) && $mapping['id'] === true) {
1386 3
            $mapping['name'] = '_id';
1387
            $this->identifier = $mapping['fieldName'];
1388 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 972
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1390 972
            }
1391 4
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1392
            switch ($this->generatorType) {
1393
                case self::GENERATOR_TYPE_AUTO:
1394 968
                    $mapping['type'] = 'id';
1395 1
                    break;
1396
                default:
1397
                    if ( ! empty($this->generatorOptions['type'])) {
1398 967
                        $mapping['type'] = $this->generatorOptions['type'];
1399 527
                    } elseif (empty($mapping['type'])) {
1400
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1401 967
                    }
1402 464
            }
1403
            unset($this->generatorOptions['type']);
1404 967
        }
1405 472
1406
        if ( ! isset($mapping['nullable'])) {
1407 967
            $mapping['nullable'] = false;
1408 509
        }
1409
1410
        // Synchronize the "simple" and "storeAs" mapping information for backwards compatibility
1411 967
        if (isset($mapping['simple']) && ($mapping['simple'] === true || $mapping['simple'] === 'true')) {
1412 134
            $mapping['storeAs'] = ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1413
            @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
        if (isset($mapping['storeAs'])) {
1417
            $mapping['simple'] = $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1418
        }
1419
1420 967
        if (isset($mapping['reference'])
1421 102
            && isset($mapping['storeAs'])
1422 102
            && $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID
1423
            && ! isset($mapping['targetDocument'])
1424 967
        ) {
1425 27
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1426 27
        }
1427
1428 967
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1429 967
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1430 967
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1431 599
        }
1432 92
1433 92
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1434
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1435 599
        }
1436 299
1437 299 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
            $mapping['association'] = self::REFERENCE_ONE;
1439 599
        }
1440 67 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 67
            $mapping['association'] = self::REFERENCE_MANY;
1442
        }
1443 599 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 574
            $mapping['association'] = self::EMBED_ONE;
1445
        }
1446 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
            $mapping['association'] = self::EMBED_MANY;
1448 967
        }
1449
1450
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1451
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1452 967
        }
1453
1454 966
        /*
1455 966
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1456 752
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1457
        }
1458
        */
1459 966
        if (isset($mapping['version'])) {
1460
            $mapping['notSaved'] = true;
1461
            $this->setVersionMapping($mapping);
1462
        }
1463
        if (isset($mapping['lock'])) {
1464
            $mapping['notSaved'] = true;
1465
            $this->setLockMapping($mapping);
1466
        }
1467 967
        $mapping['isOwningSide'] = true;
1468
        $mapping['isInverseSide'] = false;
1469 967
        if (isset($mapping['reference'])) {
1470 949 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
                $mapping['isOwningSide'] = true;
1472
                $mapping['isInverseSide'] = false;
1473
            }
1474 929 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 928
                $mapping['isInverseSide'] = true;
1476 928
                $mapping['isOwningSide'] = false;
1477 340
            }
1478 340 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 340
                $mapping['isInverseSide'] = true;
1480
                $mapping['isOwningSide'] = false;
1481 927
            }
1482 622
            if (!isset($mapping['orphanRemoval'])) {
1483
                $mapping['orphanRemoval'] = false;
1484 622
            }
1485 622
        }
1486 622
1487 622
        if (!empty($mapping['prime']) && ($mapping['association'] !== self::REFERENCE_MANY || !$mapping['isInverseSide'])) {
1488 622
            throw MappingException::referencePrimersOnlySupportedForInverseReferenceMany($this->name, $mapping['fieldName']);
1489 622
        }
1490
1491 622
        $this->applyStorageStrategy($mapping);
1492
1493
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1494 915
        if (isset($mapping['association'])) {
1495 915
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1496
        }
1497
1498 929
        return $mapping;
1499 918
    }
1500
1501
    /**
1502 929
     * Validates the storage strategy of a mapping for consistency
1503
     * @param array $mapping
1504
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1505
     */
1506 929
    private function applyStorageStrategy(array &$mapping)
1507 929
    {
1508 1
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1509
            return;
1510 928
        }
1511
1512
        switch (true) {
1513
            case $mapping['type'] == 'int':
1514
            case $mapping['type'] == 'float':
1515
            case $mapping['type'] == 'increment':
1516
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1517
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1518
                break;
1519
1520
            case $mapping['type'] == 'many':
1521
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1522
                $allowedStrategies = [
1523
                    self::STORAGE_STRATEGY_PUSH_ALL,
1524
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1525
                    self::STORAGE_STRATEGY_SET,
1526
                    self::STORAGE_STRATEGY_SET_ARRAY,
1527
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1528
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1529 6
                ];
1530
                break;
1531 6
1532 6
            default:
1533 6
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1534 5
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1535
        }
1536
1537
        if (! isset($mapping['strategy'])) {
1538
            $mapping['strategy'] = $defaultStrategy;
1539
        }
1540
1541 5
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1542
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1543 5
        }
1544 5
1545 5
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1546 5
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1547
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1548
        }
1549
    }
1550
1551
    /**
1552
     * Map a MongoGridFSFile.
1553 8
     *
1554
     * @param array $mapping The mapping information.
1555 8
     */
1556 8
    public function mapFile(array $mapping)
1557 8
    {
1558 8
        $mapping['file'] = true;
1559
        $mapping['type'] = 'file';
1560
        $this->mapField($mapping);
1561
    }
1562
1563
    /**
1564
     * Map a single embedded document.
1565 8
     *
1566
     * @param array $mapping The mapping information.
1567 8
     */
1568 8
    public function mapOneEmbedded(array $mapping)
1569 8
    {
1570 8
        $mapping['embedded'] = true;
1571
        $mapping['type'] = 'one';
1572
        $this->mapField($mapping);
1573
    }
1574
1575
    /**
1576
     * Map a collection of embedded documents.
1577
     *
1578
     * @param array $mapping The mapping information.
1579 129
     */
1580
    public function mapManyEmbedded(array $mapping)
1581 129
    {
1582
        $mapping['embedded'] = true;
1583 129
        $mapping['type'] = 'many';
1584 77
        $this->mapField($mapping);
1585
    }
1586 129
1587
    /**
1588
     * Map a single document reference.
1589
     *
1590
     * @param array $mapping The mapping information.
1591
     */
1592
    public function mapOneReference(array $mapping)
1593
    {
1594
        $mapping['reference'] = true;
1595
        $mapping['type'] = 'one';
1596
        $this->mapField($mapping);
1597
    }
1598
1599 78
    /**
1600
     * Map a collection of document references.
1601 78
     *
1602 78
     * @param array $mapping The mapping information.
1603
     */
1604
    public function mapManyReference(array $mapping)
1605
    {
1606
        $mapping['reference'] = true;
1607
        $mapping['type'] = 'many';
1608
        $this->mapField($mapping);
1609
    }
1610 17
1611
    /**
1612 17
     * 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
    public function addInheritedFieldMapping(array $fieldMapping)
1619
    {
1620
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1621 5
1622
        if (isset($fieldMapping['association'])) {
1623 5
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1624
        }
1625
    }
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 7
     *
1632
     * @param array $mapping
1633 7
     *
1634
     * @return void
1635
     *
1636
     * @throws MappingException
1637
     */
1638
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1639
    {
1640
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1641
    }
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
    public function hasReference($fieldName)
1650
    {
1651
        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
    public function hasEmbed($fieldName)
1661
    {
1662
        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
    public function hasAssociation($fieldName)
1671
    {
1672
        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 868
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1716
     */
1717 868
    public function isCollectionValuedReference($fieldName)
1718 868
    {
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 665
     *
1727
     * @param string $fieldName
1728 665
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1729 665
     */
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 737
     * and if yes, checks whether it is a collection-valued association (to-many).
1739
     *
1740 737
     * @param string $fieldName
1741 737
     * @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 591
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1753
     */
1754 591
    public function setIdGenerator($generator)
1755 591
    {
1756 591
        $this->idGenerator = $generator;
1757
    }
1758
1759
    /**
1760
     * Casts the identifier to its portable PHP type.
1761
     *
1762
     * @param mixed $id
1763
     * @return mixed $id
1764 682
     */
1765
    public function getPHPIdentifierValue($id)
1766 682
    {
1767
        $idType = $this->fieldMappings[$this->identifier]['type'];
1768
        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
    public function getDatabaseIdentifierValue($id)
1778
    {
1779
        $idType = $this->fieldMappings[$this->identifier]['type'];
1780
        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 36
     * @param object $document
1789
     * @param mixed $id
1790 36
     */
1791
    public function setIdentifierValue($document, $id)
1792
    {
1793
        $id = $this->getPHPIdentifierValue($id);
1794
        $this->reflFields[$this->identifier]->setValue($document, $id);
1795
    }
1796
1797
    /**
1798
     * Gets the document identifier as a PHP type.
1799
     *
1800 11
     * @param object $document
1801
     * @return mixed $id
1802 11
     */
1803
    public function getIdentifierValue($document)
1804
    {
1805 1
        return $this->reflFields[$this->identifier]->getValue($document);
1806
    }
1807
1808 11
    /**
1809 11
     * {@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 31
1820
    /**
1821 31
     * Get the document identifier object as a database type.
1822 1
     *
1823
     * @param object $document
1824
     *
1825 31
     * @return \MongoId $id The MongoID object.
1826
     */
1827
    public function getIdentifierObject($document)
1828
    {
1829
        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 201
     * @param mixed $value
1838
     */
1839 201
    public function setFieldValue($document, $field, $value)
1840 6
    {
1841
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1842 199
            //property changes to an uninitialized proxy will not be tracked or persisted,
1843
            //so the proxy needs to be loaded first.
1844
            $document->__load();
1845
        }
1846
1847
        $this->reflFields[$field]->setValue($document, $value);
1848
    }
1849
1850 634
    /**
1851
     * Gets the specified field's value off the given document.
1852 634
     *
1853 634
     * @param object $document
1854
     * @param string $field
1855
     *
1856
     * @return mixed
1857
     */
1858
    public function getFieldValue($document, $field)
1859
    {
1860
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1861
            $document->__load();
1862
        }
1863
1864
        return $this->reflFields[$field]->getValue($document);
1865
    }
1866
1867 9
    /**
1868
     * Gets the mapping of a field.
1869 9
     *
1870 9
     * @param string $fieldName  The field name.
1871 9
     *
1872
     * @return array  The field mapping.
1873
     *
1874
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1875
     */
1876
    public function getFieldMapping($fieldName)
1877
    {
1878
        if ( ! isset($this->fieldMappings[$fieldName])) {
1879
            throw MappingException::mappingNotFound($this->name, $fieldName);
1880
        }
1881
        return $this->fieldMappings[$fieldName];
1882
    }
1883
1884
    /**
1885 1
     * Gets mappings of fields holding embedded document(s).
1886
     *
1887 1
     * @return array of field mappings
1888 1
     */
1889 1
    public function getEmbeddedFieldsMappings()
1890
    {
1891
        return array_filter(
1892
            $this->associationMappings,
1893
            function($assoc) { return ! empty($assoc['embedded']); }
1894
        );
1895
    }
1896
1897
    /**
1898
     * Gets the field mapping by its DB name.
1899 539
     * E.g. it returns identifier's mapping when called with _id.
1900
     *
1901 539
     * @param string $dbFieldName
1902
     *
1903
     * @return array
1904
     * @throws MappingException
1905
     */
1906
    public function getFieldMappingByDbFieldName($dbFieldName)
1907
    {
1908
        foreach ($this->fieldMappings as $mapping) {
1909 379
            if ($mapping['name'] == $dbFieldName) {
1910
                return $mapping;
1911 379
            }
1912 379
        }
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
    public function isNullable($fieldName)
1925
    {
1926
        $mapping = $this->getFieldMapping($fieldName);
1927 641
        if ($mapping !== false) {
1928
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1929 641
        }
1930
        return false;
1931
    }
1932
1933
    /**
1934
     * Checks whether the document has a discriminator field and value configured.
1935
     *
1936
     * @return boolean
1937 372
     */
1938
    public function hasDiscriminator()
1939 372
    {
1940
        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
    public function setIdGeneratorType($generatorType)
1949
    {
1950
        $this->generatorType = $generatorType;
1951
    }
1952
1953
    /**
1954
     * Sets the Id generator options.
1955
     *
1956
     * @param array $generatorOptions Generator options.
1957 2
     */
1958
    public function setIdGeneratorOptions($generatorOptions)
1959 2
    {
1960 2
        $this->generatorOptions = $generatorOptions;
1961 1
    }
1962
1963 2
    /**
1964
     * @return boolean
1965
     */
1966 2
    public function isInheritanceTypeNone()
1967
    {
1968
        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 926
     */
1976
    public function isInheritanceTypeSingleCollection()
1977 926
    {
1978
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1979 926
    }
1980 113
1981
    /**
1982 926
     * 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
    public function setSubclasses(array $subclasses)
1997
    {
1998
        foreach ($subclasses as $subclass) {
1999
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
2000
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
2001
            } else {
2002
                $this->subClasses[] = $subclass;
2003
            }
2004
        }
2005
    }
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
    public function setParentClasses(array $classNames)
2015
    {
2016
        $this->parentClasses = $classNames;
2017
2018
        if (count($classNames) > 0) {
2019
            $this->rootDocumentName = array_pop($classNames);
2020
        }
2021
    }
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 102
2033
    /**
2034 102
     * Checks whether the class will use a collection to generate incremented identifiers.
2035 1
     *
2036
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
2037
     */
2038 101
    public function isIdGeneratorIncrement()
2039 101
    {
2040 101
        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 373
     */
2048
    public function isIdGeneratorUuid()
2049 373
    {
2050 373
        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 373
    public function isIdGeneratorNone()
2059
    {
2060 373
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
2061 373
    }
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 27
    public function setVersionMapping(array &$mapping)
2072
    {
2073 27
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
2074 1
            throw LockException::invalidVersionFieldType($mapping['type']);
2075
        }
2076
2077 26
        $this->isVersioned  = true;
2078 26
        $this->versionField = $mapping['fieldName'];
2079 26
    }
2080
2081
    /**
2082
     * Sets whether this class is to be versioned for optimistic locking.
2083
     *
2084
     * @param boolean $bool
2085
     */
2086
    public function setVersioned($bool)
2087
    {
2088
        $this->isVersioned = $bool;
2089
    }
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
    public function setVersionField($versionField)
2098
    {
2099
        $this->versionField = $versionField;
2100
    }
2101
2102
    /**
2103
     * Sets the version field mapping used for versioning. Sets the default
2104
     * value to use depending on the column type.
2105 9
     *
2106
     * @param array $mapping   The version field mapping array
2107 9
     *
2108 9
     * @throws \Doctrine\ODM\MongoDB\LockException
2109
     */
2110
    public function setLockMapping(array &$mapping)
2111
    {
2112
        if ($mapping['type'] !== 'int') {
2113
            throw LockException::invalidLockFieldType($mapping['type']);
2114
        }
2115
2116
        $this->isLockable = true;
2117
        $this->lockField = $mapping['fieldName'];
2118
    }
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 22
2130
    /**
2131 22
     * Sets the name of the field that is to be used for storing whether a document
2132 22
     * is currently locked or not.
2133
     *
2134
     * @param string $lockField
2135
     */
2136
    public function setLockField($lockField)
2137
    {
2138 6
        $this->lockField = $lockField;
2139
    }
2140 6
2141 3
    /**
2142
     * Marks this class as read only, no change tracking is applied to it.
2143
     */
2144 3
    public function markReadOnly()
2145
    {
2146
        $this->isReadOnly = true;
2147
    }
2148
2149
    /**
2150
     * {@inheritDoc}
2151
     */
2152 2
    public function getFieldNames()
2153
    {
2154 2
        return array_keys($this->fieldMappings);
2155
    }
2156
2157
    /**
2158 2
     * {@inheritDoc}
2159
     */
2160
    public function getAssociationNames()
2161
    {
2162 2
        return array_keys($this->associationMappings);
2163
    }
2164
2165
    /**
2166
     * {@inheritDoc}
2167
     */
2168
    public function getTypeOfField($fieldName)
2169
    {
2170
        return isset($this->fieldMappings[$fieldName]) ?
2171
            $this->fieldMappings[$fieldName]['type'] : null;
2172
    }
2173
2174
    /**
2175
     * {@inheritDoc}
2176
     */
2177
    public function getAssociationTargetClass($assocName)
2178
    {
2179
        if ( ! isset($this->associationMappings[$assocName])) {
2180
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2181
        }
2182
2183
        return $this->associationMappings[$assocName]['targetDocument'];
2184
    }
2185
2186
    /**
2187
     * Retrieve the collectionClass associated with an association
2188
     *
2189
     * @param string $assocName
2190
     */
2191
    public function getAssociationCollectionClass($assocName)
2192
    {
2193
        if ( ! isset($this->associationMappings[$assocName])) {
2194
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2195
        }
2196
2197
        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
        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