Completed
Push — master ( ce8c34...f078de )
by Andreas
49:52 queued 47:00
created

isChangeTrackingDeferredImplicit()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Doctrine\ODM\MongoDB\Mapping;
4
5
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
6
use Doctrine\ODM\MongoDB\LockException;
7
use Doctrine\ODM\MongoDB\Proxy\Proxy;
8
use Doctrine\ODM\MongoDB\Types\Type;
9
use InvalidArgumentException;
10
11
/**
12
 * A <tt>ClassMetadata</tt> instance holds all the object-document mapping metadata
13
 * of a document and it's references.
14
 *
15
 * Once populated, ClassMetadata instances are usually cached in a serialized form.
16
 *
17
 * <b>IMPORTANT NOTE:</b>
18
 *
19
 * The fields of this class are only public for 2 reasons:
20
 * 1) To allow fast READ access.
21
 * 2) To drastically reduce the size of a serialized instance (private/protected members
22
 *    get the whole class name, namespace inclusive, prepended to every property in
23
 *    the serialized representation).
24
 *
25
 * @since       1.0
26
 */
27
class ClassMetadataInfo implements \Doctrine\Common\Persistence\Mapping\ClassMetadata
28
{
29
    /* The Id generator types. */
30
    /**
31
     * AUTO means Doctrine will automatically create a new \MongoDB\BSON\ObjectId instance for us.
32
     */
33
    const GENERATOR_TYPE_AUTO = 1;
34
35
    /**
36
     * INCREMENT means a separate collection is used for maintaining and incrementing id generation.
37
     * Offers full portability.
38
     */
39
    const GENERATOR_TYPE_INCREMENT = 2;
40
41
    /**
42
     * UUID means Doctrine will generate a uuid for us.
43
     */
44
    const GENERATOR_TYPE_UUID = 3;
45
46
    /**
47
     * ALNUM means Doctrine will generate Alpha-numeric string identifiers, using the INCREMENT
48
     * generator to ensure identifier uniqueness
49
     */
50
    const GENERATOR_TYPE_ALNUM = 4;
51
52
    /**
53
     * CUSTOM means Doctrine expect a class parameter. It will then try to initiate that class
54
     * and pass other options to the generator. It will throw an Exception if the class
55
     * does not exist or if an option was passed for that there is not setter in the new
56
     * generator class.
57
     *
58
     * The class  will have to be a subtype of AbstractIdGenerator.
59
     */
60
    const GENERATOR_TYPE_CUSTOM = 5;
61
62
    /**
63
     * NONE means Doctrine will not generate any id for us and you are responsible for manually
64
     * assigning an id.
65
     */
66
    const GENERATOR_TYPE_NONE = 6;
67
68
    /**
69
     * Default discriminator field name.
70
     *
71
     * This is used for associations value for associations where a that do not define a "targetDocument" or
72
     * "discriminatorField" option in their mapping.
73
     */
74
    const DEFAULT_DISCRIMINATOR_FIELD = '_doctrine_class_name';
75
76
    const REFERENCE_ONE = 1;
77
    const REFERENCE_MANY = 2;
78
    const EMBED_ONE = 3;
79
    const EMBED_MANY = 4;
80
    const MANY = 'many';
81
    const ONE = 'one';
82
83
    /**
84
     * The types of storeAs references
85
     */
86
    const REFERENCE_STORE_AS_ID = 'id';
87
    const REFERENCE_STORE_AS_DB_REF = 'dbRef';
88
    const REFERENCE_STORE_AS_DB_REF_WITH_DB = 'dbRefWithDb';
89
    const REFERENCE_STORE_AS_REF = 'ref';
90
91
    /* The inheritance mapping types */
92
    /**
93
     * NONE means the class does not participate in an inheritance hierarchy
94
     * and therefore does not need an inheritance mapping type.
95
     */
96
    const INHERITANCE_TYPE_NONE = 1;
97
98
    /**
99
     * SINGLE_COLLECTION means the class will be persisted according to the rules of
100
     * <tt>Single Collection Inheritance</tt>.
101
     */
102
    const INHERITANCE_TYPE_SINGLE_COLLECTION = 2;
103
104
    /**
105
     * COLLECTION_PER_CLASS means the class will be persisted according to the rules
106
     * of <tt>Concrete Collection Inheritance</tt>.
107
     */
108
    const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3;
109
110
    /**
111
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
112
     * by doing a property-by-property comparison with the original data. This will
113
     * be done for all entities that are in MANAGED state at commit-time.
114
     *
115
     * This is the default change tracking policy.
116
     */
117
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
118
119
    /**
120
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
121
     * by doing a property-by-property comparison with the original data. This will
122
     * be done only for entities that were explicitly saved (through persist() or a cascade).
123
     */
124
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
125
126
    /**
127
     * NOTIFY means that Doctrine relies on the entities sending out notifications
128
     * when their properties change. Such entity classes must implement
129
     * the <tt>NotifyPropertyChanged</tt> interface.
130
     */
131
    const CHANGETRACKING_NOTIFY = 3;
132
133
    /**
134
     * SET means that fields will be written to the database using a $set operator
135
     */
136
    const STORAGE_STRATEGY_SET = 'set';
137
138
    /**
139
     * INCREMENT means that fields will be written to the database by calculating
140
     * the difference and using the $inc operator
141
     */
142
    const STORAGE_STRATEGY_INCREMENT = 'increment';
143
144
    const STORAGE_STRATEGY_PUSH_ALL = 'pushAll';
145
    const STORAGE_STRATEGY_ADD_TO_SET = 'addToSet';
146
    const STORAGE_STRATEGY_ATOMIC_SET = 'atomicSet';
147
    const STORAGE_STRATEGY_ATOMIC_SET_ARRAY = 'atomicSetArray';
148
    const STORAGE_STRATEGY_SET_ARRAY = 'setArray';
149
150
    /**
151
     * READ-ONLY: The name of the mongo database the document is mapped to.
152
     */
153
    public $db;
154
155
    /**
156
     * READ-ONLY: The name of the mongo collection the document is mapped to.
157
     */
158
    public $collection;
159
160
    /**
161
     * READ-ONLY: If the collection should be a fixed size.
162
     */
163
    public $collectionCapped;
164
165
    /**
166
     * READ-ONLY: If the collection is fixed size, its size in bytes.
167
     */
168
    public $collectionSize;
169
170
    /**
171
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
172
     */
173
    public $collectionMax;
174
175
    /**
176
     * READ-ONLY Describes how MongoDB clients route read operations to the members of a replica set.
177
     */
178
    public $readPreference;
179
180
    /**
181
     * READ-ONLY Associated with readPreference Allows to specify criteria so that your application can target read
182
     * operations to specific members, based on custom parameters.
183
     */
184
    public $readPreferenceTags;
185
186
    /**
187
     * READ-ONLY: Describes the level of acknowledgement requested from MongoDB for write operations.
188
     */
189
    public $writeConcern;
190
191
    /**
192
     * READ-ONLY: The field name of the document identifier.
193
     */
194
    public $identifier;
195
196
    /**
197
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
198
     *
199
     * @deprecated in version 1.2 and will be removed in 2.0.
200
     */
201
    public $slaveOkay;
202
203
    /**
204
     * READ-ONLY: The array of indexes for the document collection.
205
     */
206
    public $indexes = array();
207
208
    /**
209
     * READ-ONLY: Keys and options describing shard key. Only for sharded collections.
210
     */
211
    public $shardKey;
212
213
    /**
214
     * READ-ONLY: The name of the document class.
215
     */
216
    public $name;
217
218
    /**
219
     * READ-ONLY: The namespace the document class is contained in.
220
     *
221
     * @var string
222
     * @todo Not really needed. Usage could be localized.
223
     */
224
    public $namespace;
225
226
    /**
227
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
228
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
229
     * as {@link $documentName}.
230
     *
231
     * @var string
232
     */
233
    public $rootDocumentName;
234
235
    /**
236
     * The name of the custom repository class used for the document class.
237
     * (Optional).
238
     *
239
     * @var string
240
     */
241
    public $customRepositoryClassName;
242
243
    /**
244
     * READ-ONLY: The names of the parent classes (ancestors).
245
     *
246
     * @var array
247
     */
248
    public $parentClasses = array();
249
250
    /**
251
     * READ-ONLY: The names of all subclasses (descendants).
252
     *
253
     * @var array
254
     */
255
    public $subClasses = array();
256
257
    /**
258
     * The ReflectionProperty instances of the mapped class.
259
     *
260
     * @var \ReflectionProperty[]
261
     */
262
    public $reflFields = array();
263
264
    /**
265
     * READ-ONLY: The inheritance mapping type used by the class.
266
     *
267
     * @var integer
268
     */
269
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
270
271
    /**
272
     * READ-ONLY: The Id generator type used by the class.
273
     *
274
     * @var string
275
     */
276
    public $generatorType = self::GENERATOR_TYPE_AUTO;
277
278
    /**
279
     * READ-ONLY: The Id generator options.
280
     *
281
     * @var array
282
     */
283
    public $generatorOptions = array();
284
285
    /**
286
     * READ-ONLY: The ID generator used for generating IDs for this class.
287
     *
288
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
289
     */
290
    public $idGenerator;
291
292
    /**
293
     * READ-ONLY: The field mappings of the class.
294
     * Keys are field names and values are mapping definitions.
295
     *
296
     * The mapping definition array has the following values:
297
     *
298
     * - <b>fieldName</b> (string)
299
     * The name of the field in the Document.
300
     *
301
     * - <b>id</b> (boolean, optional)
302
     * Marks the field as the primary key of the document. Multiple fields of an
303
     * document can have the id attribute, forming a composite key.
304
     *
305
     * @var array
306
     */
307
    public $fieldMappings = array();
308
309
    /**
310
     * READ-ONLY: The association mappings of the class.
311
     * Keys are field names and values are mapping definitions.
312
     *
313
     * @var array
314
     */
315
    public $associationMappings = array();
316
317
    /**
318
     * READ-ONLY: Array of fields to also load with a given method.
319
     *
320
     * @var array
321
     */
322
    public $alsoLoadMethods = array();
323
324
    /**
325
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
326
     *
327
     * @var array
328
     */
329
    public $lifecycleCallbacks = array();
330
331
    /**
332
     * READ-ONLY: The discriminator value of this class.
333
     *
334
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
335
     * where a discriminator field is used.</b>
336
     *
337
     * @var mixed
338
     * @see discriminatorField
339
     */
340
    public $discriminatorValue;
341
342
    /**
343
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
344
     *
345
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
346
     * where a discriminator field is used.</b>
347
     *
348
     * @var mixed
349
     * @see discriminatorField
350
     */
351
    public $discriminatorMap = array();
352
353
    /**
354
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
355
     * inheritance mapping.
356
     *
357
     * @var string
358
     */
359
    public $discriminatorField;
360
361
    /**
362
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
363
     *
364
     * @var string
365
     * @see discriminatorField
366
     */
367
    public $defaultDiscriminatorValue;
368
369
    /**
370
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
371
     *
372
     * @var boolean
373
     */
374
    public $isMappedSuperclass = false;
375
376
    /**
377
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
378
     *
379
     * @var boolean
380
     */
381
    public $isEmbeddedDocument = false;
382
383
    /**
384
     * READ-ONLY: Whether this class describes the mapping of an aggregation result document.
385
     *
386
     * @var boolean
387
     */
388
    public $isQueryResultDocument = false;
389
390
    /**
391
     * READ-ONLY: The policy used for change-tracking on entities of this class.
392
     *
393
     * @var integer
394
     */
395
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
396
397
    /**
398
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
399
     * with optimistic locking.
400
     *
401
     * @var boolean $isVersioned
402
     */
403
    public $isVersioned;
404
405
    /**
406
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
407
     *
408
     * @var mixed $versionField
409
     */
410
    public $versionField;
411
412
    /**
413
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
414
     * locking.
415
     *
416
     * @var boolean $isLockable
417
     */
418
    public $isLockable;
419
420
    /**
421
     * READ-ONLY: The name of the field which is used for locking a document.
422
     *
423
     * @var mixed $lockField
424
     */
425
    public $lockField;
426
427
    /**
428
     * The ReflectionClass instance of the mapped class.
429
     *
430
     * @var \ReflectionClass
431
     */
432
    public $reflClass;
433
434
    /**
435
     * READ_ONLY: A flag for whether or not this document is read-only.
436
     *
437
     * @var bool
438
     */
439
    public $isReadOnly;
440
441
    /**
442
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
443
     * metadata of the class with the given name.
444
     *
445
     * @param string $documentName The name of the document class the new instance is used for.
446
     */
447 1505
    public function __construct($documentName)
448
    {
449 1505
        $this->name = $documentName;
450 1505
        $this->rootDocumentName = $documentName;
451 1505
    }
452
453
    /**
454
     * Helper method to get reference id of ref* type references
455
     * @param mixed  $reference
456
     * @param string $storeAs
457
     * @return mixed
458
     * @internal
459
     */
460 121
    public static function getReferenceId($reference, $storeAs)
461
    {
462 121
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID ? $reference : $reference[ClassMetadataInfo::getReferencePrefix($storeAs) . 'id'];
463
    }
464
465
    /**
466
     * Returns the reference prefix used for a reference
467
     * @param string $storeAs
468
     * @return string
469
     */
470 186
    private static function getReferencePrefix($storeAs)
471
    {
472 186
        if (!in_array($storeAs, [ClassMetadataInfo::REFERENCE_STORE_AS_REF, ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF, ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB])) {
473
            throw new \LogicException('Can only get a reference prefix for DBRef and reference arrays');
474
        }
475
476 186
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_REF ? '' : '$';
477
    }
478
479
    /**
480
     * Returns a fully qualified field name for a given reference
481
     * @param string $storeAs
482
     * @param string $pathPrefix The field path prefix
483
     * @return string
484
     * @internal
485
     */
486 134
    public static function getReferenceFieldName($storeAs, $pathPrefix = '')
487
    {
488 134
        if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID) {
489 94
            return $pathPrefix;
490
        }
491
492 122
        return ($pathPrefix ? $pathPrefix . '.' : '') . static::getReferencePrefix($storeAs) . 'id';
0 ignored issues
show
Bug introduced by
Since getReferencePrefix() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getReferencePrefix() to at least protected.

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

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

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

}

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

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

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

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

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

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

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
818 1
                sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.', __METHOD__),
819 1
                E_USER_DEPRECATED
820
            );
821
        }
822
823 2
        if ($this->readPreference) {
824
            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...
825
        }
826 2
        $this->slaveOkay = $slaveOkay === null ? null : (boolean) $slaveOkay;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...etadataInfo::$slaveOkay has been deprecated with message: in version 1.2 and will be removed in 2.0.

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

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

Loading history...
827 2
    }
828
829
    /**
830
     * Add a index for this Document.
831
     *
832
     * @param array $keys Array of keys for the index.
833
     * @param array $options Array of options for the index.
834
     */
835 189
    public function addIndex($keys, array $options = array())
836
    {
837 189
        $this->indexes[] = array(
838 189 View Code Duplication
            'keys' => array_map(function($value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
839 189
                if ($value == 1 || $value == -1) {
840 54
                    return (int) $value;
841
                }
842 182
                if (is_string($value)) {
843 182
                    $lower = strtolower($value);
844 182
                    if ($lower === 'asc') {
845 182
                        return 1;
846 1
                    } elseif ($lower === 'desc') {
847 1
                        return -1;
848
                    }
849
                }
850
                return $value;
851 189
            }, $keys),
852 189
            'options' => $options
853
        );
854 189
    }
855
856
    /**
857
     * Returns the array of indexes for this Document.
858
     *
859
     * @return array $indexes The array of indexes.
860
     */
861 25
    public function getIndexes()
862
    {
863 25
        return $this->indexes;
864
    }
865
866
    /**
867
     * Checks whether this document has indexes or not.
868
     *
869
     * @return boolean
870
     */
871
    public function hasIndexes()
872
    {
873
        return $this->indexes ? true : false;
874
    }
875
876
    /**
877
     * Set shard key for this Document.
878
     *
879
     * @param array $keys Array of document keys.
880
     * @param array $options Array of sharding options.
881
     *
882
     * @throws MappingException
883
     */
884 72
    public function setShardKey(array $keys, array $options = array())
885
    {
886 72
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
887 2
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
888
        }
889
890 72
        if ($this->isEmbeddedDocument) {
891 2
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
892
        }
893
894 70
        foreach (array_keys($keys) as $field) {
895 70
            if (! isset($this->fieldMappings[$field])) {
896 63
                continue;
897
            }
898
899 7
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
900 3
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
901
            }
902
903 4
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
904 4
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
905
            }
906
        }
907
908 66
        $this->shardKey = array(
909 66 View Code Duplication
            'keys' => array_map(function($value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
910 66
                if ($value == 1 || $value == -1) {
911 6
                    return (int) $value;
912
                }
913 65
                if (is_string($value)) {
914 65
                    $lower = strtolower($value);
915 65
                    if ($lower === 'asc') {
916 64
                        return 1;
917 46
                    } elseif ($lower === 'desc') {
918
                        return -1;
919
                    }
920
                }
921 46
                return $value;
922 66
            }, $keys),
923 66
            'options' => $options
924
        );
925 66
    }
926
927
    /**
928
     * @return array
929
     */
930 18
    public function getShardKey()
931
    {
932 18
        return $this->shardKey;
933
    }
934
935
    /**
936
     * Checks whether this document has shard key or not.
937
     *
938
     * @return bool
939
     */
940 1109
    public function isSharded()
941
    {
942 1109
        return $this->shardKey ? true : false;
943
    }
944
945
    /**
946
     * Sets the read preference used by this class.
947
     *
948
     * @param string $readPreference
949
     * @param array|null $tags
950
     *
951
     * @throws MappingException
952
     */
953 894
    public function setReadPreference($readPreference, $tags)
954
    {
955 894
        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...
956
            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...
957
        }
958 894
        $this->readPreference = $readPreference;
959 894
        $this->readPreferenceTags = $tags;
960 894
    }
961
962
    /**
963
     * Sets the write concern used by this class.
964
     *
965
     * @param string $writeConcern
966
     */
967 904
    public function setWriteConcern($writeConcern)
968
    {
969 904
        $this->writeConcern = $writeConcern;
970 904
    }
971
972
    /**
973
     * @return string
974
     */
975 12
    public function getWriteConcern()
976
    {
977 12
        return $this->writeConcern;
978
    }
979
980
    /**
981
     * Whether there is a write concern configured for this class.
982
     *
983
     * @return bool
984
     */
985 558
    public function hasWriteConcern()
986
    {
987 558
        return $this->writeConcern !== null;
988
    }
989
990
    /**
991
     * Sets the change tracking policy used by this class.
992
     *
993
     * @param integer $policy
994
     */
995 895
    public function setChangeTrackingPolicy($policy)
996
    {
997 895
        $this->changeTrackingPolicy = $policy;
998 895
    }
999
1000
    /**
1001
     * Whether the change tracking policy of this class is "deferred explicit".
1002
     *
1003
     * @return boolean
1004
     */
1005 69
    public function isChangeTrackingDeferredExplicit()
1006
    {
1007 69
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
1008
    }
1009
1010
    /**
1011
     * Whether the change tracking policy of this class is "deferred implicit".
1012
     *
1013
     * @return boolean
1014
     */
1015 578
    public function isChangeTrackingDeferredImplicit()
1016
    {
1017 578
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1018
    }
1019
1020
    /**
1021
     * Whether the change tracking policy of this class is "notify".
1022
     *
1023
     * @return boolean
1024
     */
1025 314
    public function isChangeTrackingNotify()
1026
    {
1027 314
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1028
    }
1029
1030
    /**
1031
     * Gets the ReflectionProperties of the mapped class.
1032
     *
1033
     * @return array An array of ReflectionProperty instances.
1034
     */
1035 99
    public function getReflectionProperties()
1036
    {
1037 99
        return $this->reflFields;
1038
    }
1039
1040
    /**
1041
     * Gets a ReflectionProperty for a specific field of the mapped class.
1042
     *
1043
     * @param string $name
1044
     *
1045
     * @return \ReflectionProperty
1046
     */
1047
    public function getReflectionProperty($name)
1048
    {
1049
        return $this->reflFields[$name];
1050
    }
1051
1052
    /**
1053
     * {@inheritDoc}
1054
     */
1055 1387
    public function getName()
1056
    {
1057 1387
        return $this->name;
1058
    }
1059
1060
    /**
1061
     * The namespace this Document class belongs to.
1062
     *
1063
     * @return string $namespace The namespace name.
1064
     */
1065
    public function getNamespace()
1066
    {
1067
        return $this->namespace;
1068
    }
1069
1070
    /**
1071
     * Returns the database this Document is mapped to.
1072
     *
1073
     * @return string $db The database name.
1074
     */
1075 1310
    public function getDatabase()
1076
    {
1077 1310
        return $this->db;
1078
    }
1079
1080
    /**
1081
     * Set the database this Document is mapped to.
1082
     *
1083
     * @param string $db The database name
1084
     */
1085 95
    public function setDatabase($db)
1086
    {
1087 95
        $this->db = $db;
1088 95
    }
1089
1090
    /**
1091
     * Get the collection this Document is mapped to.
1092
     *
1093
     * @return string $collection The collection name.
1094
     */
1095 1312
    public function getCollection()
1096
    {
1097 1312
        return $this->collection;
1098
    }
1099
1100
    /**
1101
     * Sets the collection this Document is mapped to.
1102
     *
1103
     * @param array|string $name
1104
     *
1105
     * @throws \InvalidArgumentException
1106
     */
1107 1474
    public function setCollection($name)
1108
    {
1109 1474
        if (is_array($name)) {
1110
            if ( ! isset($name['name'])) {
1111
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
1112
            }
1113
            $this->collectionCapped = $name['capped'] ?? false;
1114
            $this->collectionSize = $name['size'] ?? 0;
1115
            $this->collectionMax = $name['max'] ?? 0;
1116
            $this->collection = $name['name'];
1117
        } else {
1118 1474
            $this->collection = $name;
1119
        }
1120 1474
    }
1121
1122
    /**
1123
     * Get whether or not the documents collection is capped.
1124
     *
1125
     * @return boolean
1126
     */
1127 4
    public function getCollectionCapped()
1128
    {
1129 4
        return $this->collectionCapped;
1130
    }
1131
1132
    /**
1133
     * Set whether or not the documents collection is capped.
1134
     *
1135
     * @param boolean $bool
1136
     */
1137 1
    public function setCollectionCapped($bool)
1138
    {
1139 1
        $this->collectionCapped = $bool;
1140 1
    }
1141
1142
    /**
1143
     * Get the collection size
1144
     *
1145
     * @return integer
1146
     */
1147 4
    public function getCollectionSize()
1148
    {
1149 4
        return $this->collectionSize;
1150
    }
1151
1152
    /**
1153
     * Set the collection size.
1154
     *
1155
     * @param integer $size
1156
     */
1157 1
    public function setCollectionSize($size)
1158
    {
1159 1
        $this->collectionSize = $size;
1160 1
    }
1161
1162
    /**
1163
     * Get the collection max.
1164
     *
1165
     * @return integer
1166
     */
1167 4
    public function getCollectionMax()
1168
    {
1169 4
        return $this->collectionMax;
1170
    }
1171
1172
    /**
1173
     * Set the collection max.
1174
     *
1175
     * @param integer $max
1176
     */
1177 1
    public function setCollectionMax($max)
1178
    {
1179 1
        $this->collectionMax = $max;
1180 1
    }
1181
1182
    /**
1183
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1184
     *
1185
     * @return boolean
1186
     */
1187
    public function isMappedToCollection()
1188
    {
1189
        return $this->collection ? true : false;
1190
    }
1191
1192
    /**
1193
     * Map a field.
1194
     *
1195
     * @param array $mapping The mapping information.
1196
     *
1197
     * @return array
1198
     *
1199
     * @throws MappingException
1200
     */
1201 1422
    public function mapField(array $mapping)
1202
    {
1203 1422
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1204 9
            $mapping['fieldName'] = $mapping['name'];
1205
        }
1206 1422
        if ( ! isset($mapping['fieldName'])) {
1207
            throw MappingException::missingFieldName($this->name);
1208
        }
1209 1422
        if ( ! isset($mapping['name'])) {
1210 1412
            $mapping['name'] = $mapping['fieldName'];
1211
        }
1212 1422
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1213 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1214
        }
1215 1421
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1216
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1217
        }
1218 1421
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1219 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1220
        }
1221 1420 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...
1222 1118
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1223
        }
1224 1420
        if (isset($mapping['collectionClass'])) {
1225 54 View Code Duplication
            if (strpos($mapping['collectionClass'], '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1226 52
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1227
            }
1228 54
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1229
        }
1230 1420
        if ( ! empty($mapping['collectionClass'])) {
1231 54
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1232 54
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1233 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1234
            }
1235
        }
1236
1237 1419
        if (isset($mapping['discriminatorMap'])) {
1238 115
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1239 115 View Code Duplication
                if (strpos($class, '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1240 115
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1241
                }
1242
            }
1243
        }
1244
1245 1419
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1246 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1247
        }
1248
1249 1418
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1250
1251 1418
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1252 1117
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1253
        }
1254
1255 1418
        if (isset($mapping['embedded'])) {
1256 1076
            unset($mapping['cascade']);
1257 1413
        } elseif (isset($mapping['cascade'])) {
1258 924
            $mapping['cascade'] = $cascades;
1259
        }
1260
1261 1418
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1262 1418
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1263 1418
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1264 1418
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1265 1418
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1266
1267 1418
        if (isset($mapping['id']) && $mapping['id'] === true) {
1268 1390
            $mapping['name'] = '_id';
1269 1390
            $this->identifier = $mapping['fieldName'];
1270 1390 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...
1271 1377
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1272
            }
1273 1390
            $this->generatorOptions = $mapping['options'] ?? array();
1274 1390
            switch ($this->generatorType) {
1275 1390
                case self::GENERATOR_TYPE_AUTO:
1276 1316
                    $mapping['type'] = 'id';
1277 1316
                    break;
1278
                default:
1279 147
                    if ( ! empty($this->generatorOptions['type'])) {
1280 56
                        $mapping['type'] = $this->generatorOptions['type'];
1281 91
                    } elseif (empty($mapping['type'])) {
1282 77
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1283
                    }
1284
            }
1285 1390
            unset($this->generatorOptions['type']);
1286
        }
1287
1288 1418
        if ( ! isset($mapping['nullable'])) {
1289 45
            $mapping['nullable'] = false;
1290
        }
1291
1292 1418
        if (isset($mapping['reference'])
1293 1418
            && isset($mapping['storeAs'])
1294 1418
            && $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID
1295 1418
            && ! isset($mapping['targetDocument'])
1296
        ) {
1297 3
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1298
        }
1299
1300 1415
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1301 1415
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1302 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1303
        }
1304
1305 1411
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && isset($mapping['strategy']) && CollectionHelper::isAtomic($mapping['strategy'])) {
1306 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1307
        }
1308
1309 1410 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...
1310 1015
            $mapping['association'] = self::REFERENCE_ONE;
1311
        }
1312 1410 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...
1313 969
            $mapping['association'] = self::REFERENCE_MANY;
1314
        }
1315 1410 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...
1316 953
            $mapping['association'] = self::EMBED_ONE;
1317
        }
1318 1410 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...
1319 987
            $mapping['association'] = self::EMBED_MANY;
1320
        }
1321
1322 1410
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1323 125
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1324
        }
1325
1326
        /*
1327
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1328
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1329
        }
1330
        */
1331 1410
        if (isset($mapping['version'])) {
1332 68
            $mapping['notSaved'] = true;
1333 68
            $this->setVersionMapping($mapping);
1334
        }
1335 1410
        if (isset($mapping['lock'])) {
1336 23
            $mapping['notSaved'] = true;
1337 23
            $this->setLockMapping($mapping);
1338
        }
1339 1410
        $mapping['isOwningSide'] = true;
1340 1410
        $mapping['isInverseSide'] = false;
1341 1410
        if (isset($mapping['reference'])) {
1342 1081 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...
1343 84
                $mapping['isOwningSide'] = true;
1344 84
                $mapping['isInverseSide'] = false;
1345
            }
1346 1081 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...
1347 826
                $mapping['isInverseSide'] = true;
1348 826
                $mapping['isOwningSide'] = false;
1349
            }
1350 1081 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...
1351 58
                $mapping['isInverseSide'] = true;
1352 58
                $mapping['isOwningSide'] = false;
1353
            }
1354 1081
            if (!isset($mapping['orphanRemoval'])) {
1355 1058
                $mapping['orphanRemoval'] = false;
1356
            }
1357
        }
1358
1359 1410
        if (!empty($mapping['prime']) && ($mapping['association'] !== self::REFERENCE_MANY || !$mapping['isInverseSide'])) {
1360
            throw MappingException::referencePrimersOnlySupportedForInverseReferenceMany($this->name, $mapping['fieldName']);
1361
        }
1362
1363 1410
        $this->applyStorageStrategy($mapping);
1364
1365 1409
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1366 1409
        if (isset($mapping['association'])) {
1367 1215
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1368
        }
1369
1370 1409
        return $mapping;
1371
    }
1372
1373
    /**
1374
     * Validates the storage strategy of a mapping for consistency
1375
     * @param array $mapping
1376
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1377
     */
1378 1410
    private function applyStorageStrategy(array &$mapping)
1379
    {
1380 1410
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1381 1392
            return;
1382
        }
1383
1384
        switch (true) {
1385 1372
            case $mapping['type'] == 'int':
1386 1371
            case $mapping['type'] == 'float':
1387 826
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1388 826
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1389 826
                break;
1390
1391 1371
            case $mapping['type'] == 'many':
1392 1094
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1393
                $allowedStrategies = [
1394 1094
                    self::STORAGE_STRATEGY_PUSH_ALL,
1395 1094
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1396 1094
                    self::STORAGE_STRATEGY_SET,
1397 1094
                    self::STORAGE_STRATEGY_SET_ARRAY,
1398 1094
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1399 1094
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1400
                ];
1401 1094
                break;
1402
1403
            default:
1404 1358
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1405 1358
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1406
        }
1407
1408 1372
        if (! isset($mapping['strategy'])) {
1409 1362
            $mapping['strategy'] = $defaultStrategy;
1410
        }
1411
1412 1372
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1413
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1414
        }
1415
1416 1372
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1417 1372
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1418 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1419
        }
1420 1371
    }
1421
1422
    /**
1423
     * Map a single embedded document.
1424
     *
1425
     * @param array $mapping The mapping information.
1426
     */
1427 6
    public function mapOneEmbedded(array $mapping)
1428
    {
1429 6
        $mapping['embedded'] = true;
1430 6
        $mapping['type'] = 'one';
1431 6
        $this->mapField($mapping);
1432 5
    }
1433
1434
    /**
1435
     * Map a collection of embedded documents.
1436
     *
1437
     * @param array $mapping The mapping information.
1438
     */
1439 5
    public function mapManyEmbedded(array $mapping)
1440
    {
1441 5
        $mapping['embedded'] = true;
1442 5
        $mapping['type'] = 'many';
1443 5
        $this->mapField($mapping);
1444 5
    }
1445
1446
    /**
1447
     * Map a single document reference.
1448
     *
1449
     * @param array $mapping The mapping information.
1450
     */
1451 2
    public function mapOneReference(array $mapping)
1452
    {
1453 2
        $mapping['reference'] = true;
1454 2
        $mapping['type'] = 'one';
1455 2
        $this->mapField($mapping);
1456 2
    }
1457
1458
    /**
1459
     * Map a collection of document references.
1460
     *
1461
     * @param array $mapping The mapping information.
1462
     */
1463 2
    public function mapManyReference(array $mapping)
1464
    {
1465 2
        $mapping['reference'] = true;
1466 2
        $mapping['type'] = 'many';
1467 2
        $this->mapField($mapping);
1468 2
    }
1469
1470
    /**
1471
     * INTERNAL:
1472
     * Adds a field mapping without completing/validating it.
1473
     * This is mainly used to add inherited field mappings to derived classes.
1474
     *
1475
     * @param array $fieldMapping
1476
     */
1477 117
    public function addInheritedFieldMapping(array $fieldMapping)
1478
    {
1479 117
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1480
1481 117
        if (isset($fieldMapping['association'])) {
1482 67
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1483
        }
1484 117
    }
1485
1486
    /**
1487
     * INTERNAL:
1488
     * Adds an association mapping without completing/validating it.
1489
     * This is mainly used to add inherited association mappings to derived classes.
1490
     *
1491
     * @param array $mapping
1492
     *
1493
     * @return void
1494
     *
1495
     * @throws MappingException
1496
     */
1497 68
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1498
    {
1499 68
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1500 68
    }
1501
1502
    /**
1503
     * Checks whether the class has a mapped association with the given field name.
1504
     *
1505
     * @param string $fieldName
1506
     * @return boolean
1507
     */
1508 32
    public function hasReference($fieldName)
1509
    {
1510 32
        return isset($this->fieldMappings[$fieldName]['reference']);
1511
    }
1512
1513
    /**
1514
     * Checks whether the class has a mapped embed with the given field name.
1515
     *
1516
     * @param string $fieldName
1517
     * @return boolean
1518
     */
1519 5
    public function hasEmbed($fieldName)
1520
    {
1521 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1522
    }
1523
1524
    /**
1525
     * {@inheritDoc}
1526
     *
1527
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1528
     */
1529 7
    public function hasAssociation($fieldName)
1530
    {
1531 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1532
    }
1533
1534
    /**
1535
     * {@inheritDoc}
1536
     *
1537
     * Checks whether the class has a mapped reference or embed for the specified field and
1538
     * is a single valued association.
1539
     */
1540
    public function isSingleValuedAssociation($fieldName)
1541
    {
1542
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1543
    }
1544
1545
    /**
1546
     * {@inheritDoc}
1547
     *
1548
     * Checks whether the class has a mapped reference or embed for the specified field and
1549
     * is a collection valued association.
1550
     */
1551
    public function isCollectionValuedAssociation($fieldName)
1552
    {
1553
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1554
    }
1555
1556
    /**
1557
     * Checks whether the class has a mapped association for the specified field
1558
     * and if yes, checks whether it is a single-valued association (to-one).
1559
     *
1560
     * @param string $fieldName
1561
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1562
     */
1563
    public function isSingleValuedReference($fieldName)
1564
    {
1565
        return isset($this->fieldMappings[$fieldName]['association']) &&
1566
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1567
    }
1568
1569
    /**
1570
     * Checks whether the class has a mapped association for the specified field
1571
     * and if yes, checks whether it is a collection-valued association (to-many).
1572
     *
1573
     * @param string $fieldName
1574
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1575
     */
1576
    public function isCollectionValuedReference($fieldName)
1577
    {
1578
        return isset($this->fieldMappings[$fieldName]['association']) &&
1579
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1580
    }
1581
1582
    /**
1583
     * Checks whether the class has a mapped embedded document for the specified field
1584
     * and if yes, checks whether it is a single-valued association (to-one).
1585
     *
1586
     * @param string $fieldName
1587
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1588
     */
1589
    public function isSingleValuedEmbed($fieldName)
1590
    {
1591
        return isset($this->fieldMappings[$fieldName]['association']) &&
1592
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1593
    }
1594
1595
    /**
1596
     * Checks whether the class has a mapped embedded document for the specified field
1597
     * and if yes, checks whether it is a collection-valued association (to-many).
1598
     *
1599
     * @param string $fieldName
1600
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1601
     */
1602
    public function isCollectionValuedEmbed($fieldName)
1603
    {
1604
        return isset($this->fieldMappings[$fieldName]['association']) &&
1605
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1606
    }
1607
1608
    /**
1609
     * Sets the ID generator used to generate IDs for instances of this class.
1610
     *
1611
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1612
     */
1613 1321
    public function setIdGenerator($generator)
1614
    {
1615 1321
        $this->idGenerator = $generator;
1616 1321
    }
1617
1618
    /**
1619
     * Casts the identifier to its portable PHP type.
1620
     *
1621
     * @param mixed $id
1622
     * @return mixed $id
1623
     */
1624 600
    public function getPHPIdentifierValue($id)
1625
    {
1626 600
        $idType = $this->fieldMappings[$this->identifier]['type'];
1627 600
        return Type::getType($idType)->convertToPHPValue($id);
1628
    }
1629
1630
    /**
1631
     * Casts the identifier to its database type.
1632
     *
1633
     * @param mixed $id
1634
     * @return mixed $id
1635
     */
1636 666
    public function getDatabaseIdentifierValue($id)
1637
    {
1638 666
        $idType = $this->fieldMappings[$this->identifier]['type'];
1639 666
        return Type::getType($idType)->convertToDatabaseValue($id);
1640
    }
1641
1642
    /**
1643
     * Sets the document identifier of a document.
1644
     *
1645
     * The value will be converted to a PHP type before being set.
1646
     *
1647
     * @param object $document
1648
     * @param mixed $id
1649
     */
1650 528
    public function setIdentifierValue($document, $id)
1651
    {
1652 528
        $id = $this->getPHPIdentifierValue($id);
1653 528
        $this->reflFields[$this->identifier]->setValue($document, $id);
1654 528
    }
1655
1656
    /**
1657
     * Gets the document identifier as a PHP type.
1658
     *
1659
     * @param object $document
1660
     * @return mixed $id
1661
     */
1662 614
    public function getIdentifierValue($document)
1663
    {
1664 614
        return $this->reflFields[$this->identifier]->getValue($document);
1665
    }
1666
1667
    /**
1668
     * {@inheritDoc}
1669
     *
1670
     * Since MongoDB only allows exactly one identifier field this is a proxy
1671
     * to {@see getIdentifierValue()} and returns an array with the identifier
1672
     * field as a key.
1673
     */
1674
    public function getIdentifierValues($object)
1675
    {
1676
        return array($this->identifier => $this->getIdentifierValue($object));
1677
    }
1678
1679
    /**
1680
     * Get the document identifier object as a database type.
1681
     *
1682
     * @param object $document
1683
     *
1684
     * @return \MongoDB\BSON\ObjectId $id The ObjectId
1685
     */
1686 32
    public function getIdentifierObject($document)
1687
    {
1688 32
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1689
    }
1690
1691
    /**
1692
     * Sets the specified field to the specified value on the given document.
1693
     *
1694
     * @param object $document
1695
     * @param string $field
1696
     * @param mixed $value
1697
     */
1698 8
    public function setFieldValue($document, $field, $value)
1699
    {
1700 8
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1701
            //property changes to an uninitialized proxy will not be tracked or persisted,
1702
            //so the proxy needs to be loaded first.
1703 1
            $document->__load();
1704
        }
1705
1706 8
        $this->reflFields[$field]->setValue($document, $value);
1707 8
    }
1708
1709
    /**
1710
     * Gets the specified field's value off the given document.
1711
     *
1712
     * @param object $document
1713
     * @param string $field
1714
     *
1715
     * @return mixed
1716
     */
1717 27
    public function getFieldValue($document, $field)
1718
    {
1719 27
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1720 1
            $document->__load();
1721
        }
1722
1723 27
        return $this->reflFields[$field]->getValue($document);
1724
    }
1725
1726
    /**
1727
     * Gets the mapping of a field.
1728
     *
1729
     * @param string $fieldName  The field name.
1730
     *
1731
     * @return array  The field mapping.
1732
     *
1733
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1734
     */
1735 167
    public function getFieldMapping($fieldName)
1736
    {
1737 167
        if ( ! isset($this->fieldMappings[$fieldName])) {
1738 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1739
        }
1740 165
        return $this->fieldMappings[$fieldName];
1741
    }
1742
1743
    /**
1744
     * Gets mappings of fields holding embedded document(s).
1745
     *
1746
     * @return array of field mappings
1747
     */
1748 569
    public function getEmbeddedFieldsMappings()
1749
    {
1750 569
        return array_filter(
1751 569
            $this->associationMappings,
1752
            function($assoc) { return ! empty($assoc['embedded']); }
1753
        );
1754
    }
1755
1756
    /**
1757
     * Gets the field mapping by its DB name.
1758
     * E.g. it returns identifier's mapping when called with _id.
1759
     *
1760
     * @param string $dbFieldName
1761
     *
1762
     * @return array
1763
     * @throws MappingException
1764
     */
1765 4
    public function getFieldMappingByDbFieldName($dbFieldName)
1766
    {
1767 4
        foreach ($this->fieldMappings as $mapping) {
1768 4
            if ($mapping['name'] == $dbFieldName) {
1769 4
                return $mapping;
1770
            }
1771
        }
1772
1773
        throw MappingException::mappingNotFoundByDbName($this->name, $dbFieldName);
1774
    }
1775
1776
    /**
1777
     * Check if the field is not null.
1778
     *
1779
     * @param string $fieldName  The field name
1780
     *
1781
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1782
     */
1783 1
    public function isNullable($fieldName)
1784
    {
1785 1
        $mapping = $this->getFieldMapping($fieldName);
1786 1
        if ($mapping !== false) {
1787 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1788
        }
1789
        return false;
1790
    }
1791
1792
    /**
1793
     * Checks whether the document has a discriminator field and value configured.
1794
     *
1795
     * @return boolean
1796
     */
1797 498
    public function hasDiscriminator()
1798
    {
1799 498
        return isset($this->discriminatorField, $this->discriminatorValue);
1800
    }
1801
1802
    /**
1803
     * Sets the type of Id generator to use for the mapped class.
1804
     *
1805
     * @param string $generatorType Generator type.
1806
     */
1807 893
    public function setIdGeneratorType($generatorType)
1808
    {
1809 893
        $this->generatorType = $generatorType;
1810 893
    }
1811
1812
    /**
1813
     * Sets the Id generator options.
1814
     *
1815
     * @param array $generatorOptions Generator options.
1816
     */
1817
    public function setIdGeneratorOptions($generatorOptions)
1818
    {
1819
        $this->generatorOptions = $generatorOptions;
1820
    }
1821
1822
    /**
1823
     * @return boolean
1824
     */
1825 576
    public function isInheritanceTypeNone()
1826
    {
1827 576
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1828
    }
1829
1830
    /**
1831
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1832
     *
1833
     * @return boolean
1834
     */
1835 892
    public function isInheritanceTypeSingleCollection()
1836
    {
1837 892
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1838
    }
1839
1840
    /**
1841
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1842
     *
1843
     * @return boolean
1844
     */
1845
    public function isInheritanceTypeCollectionPerClass()
1846
    {
1847
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1848
    }
1849
1850
    /**
1851
     * Sets the mapped subclasses of this class.
1852
     *
1853
     * @param string[] $subclasses The names of all mapped subclasses.
1854
     */
1855 2
    public function setSubclasses(array $subclasses)
1856
    {
1857 2
        foreach ($subclasses as $subclass) {
1858 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1859 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1860
            } else {
1861 2
                $this->subClasses[] = $subclass;
1862
            }
1863
        }
1864 2
    }
1865
1866
    /**
1867
     * Sets the parent class names.
1868
     * Assumes that the class names in the passed array are in the order:
1869
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1870
     *
1871
     * @param string[] $classNames
1872
     */
1873 1378
    public function setParentClasses(array $classNames)
1874
    {
1875 1378
        $this->parentClasses = $classNames;
1876
1877 1378
        if (count($classNames) > 0) {
1878 100
            $this->rootDocumentName = array_pop($classNames);
1879
        }
1880 1378
    }
1881
1882
    /**
1883
     * Checks whether the class will generate a new \MongoDB\BSON\ObjectId instance for us.
1884
     *
1885
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1886
     */
1887
    public function isIdGeneratorAuto()
1888
    {
1889
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1890
    }
1891
1892
    /**
1893
     * Checks whether the class will use a collection to generate incremented identifiers.
1894
     *
1895
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1896
     */
1897
    public function isIdGeneratorIncrement()
1898
    {
1899
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1900
    }
1901
1902
    /**
1903
     * Checks whether the class will generate a uuid id.
1904
     *
1905
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1906
     */
1907
    public function isIdGeneratorUuid()
1908
    {
1909
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1910
    }
1911
1912
    /**
1913
     * Checks whether the class uses no id generator.
1914
     *
1915
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1916
     */
1917
    public function isIdGeneratorNone()
1918
    {
1919
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1920
    }
1921
1922
    /**
1923
     * Sets the version field mapping used for versioning. Sets the default
1924
     * value to use depending on the column type.
1925
     *
1926
     * @param array $mapping   The version field mapping array
1927
     *
1928
     * @throws LockException
1929
     */
1930 68
    public function setVersionMapping(array &$mapping)
1931
    {
1932 68
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
1933 1
            throw LockException::invalidVersionFieldType($mapping['type']);
1934
        }
1935
1936 67
        $this->isVersioned  = true;
1937 67
        $this->versionField = $mapping['fieldName'];
1938 67
    }
1939
1940
    /**
1941
     * Sets whether this class is to be versioned for optimistic locking.
1942
     *
1943
     * @param boolean $bool
1944
     */
1945 893
    public function setVersioned($bool)
1946
    {
1947 893
        $this->isVersioned = $bool;
1948 893
    }
1949
1950
    /**
1951
     * Sets the name of the field that is to be used for versioning if this class is
1952
     * versioned for optimistic locking.
1953
     *
1954
     * @param string $versionField
1955
     */
1956 893
    public function setVersionField($versionField)
1957
    {
1958 893
        $this->versionField = $versionField;
1959 893
    }
1960
1961
    /**
1962
     * Sets the version field mapping used for versioning. Sets the default
1963
     * value to use depending on the column type.
1964
     *
1965
     * @param array $mapping   The version field mapping array
1966
     *
1967
     * @throws \Doctrine\ODM\MongoDB\LockException
1968
     */
1969 23
    public function setLockMapping(array &$mapping)
1970
    {
1971 23
        if ($mapping['type'] !== 'int') {
1972 1
            throw LockException::invalidLockFieldType($mapping['type']);
1973
        }
1974
1975 22
        $this->isLockable = true;
1976 22
        $this->lockField = $mapping['fieldName'];
1977 22
    }
1978
1979
    /**
1980
     * Sets whether this class is to allow pessimistic locking.
1981
     *
1982
     * @param boolean $bool
1983
     */
1984
    public function setLockable($bool)
1985
    {
1986
        $this->isLockable = $bool;
1987
    }
1988
1989
    /**
1990
     * Sets the name of the field that is to be used for storing whether a document
1991
     * is currently locked or not.
1992
     *
1993
     * @param string $lockField
1994
     */
1995
    public function setLockField($lockField)
1996
    {
1997
        $this->lockField = $lockField;
1998
    }
1999
2000
    /**
2001
     * Marks this class as read only, no change tracking is applied to it.
2002
     */
2003 6
    public function markReadOnly()
2004
    {
2005 6
        $this->isReadOnly = true;
2006 6
    }
2007
2008
    /**
2009
     * {@inheritDoc}
2010
     */
2011
    public function getFieldNames()
2012
    {
2013
        return array_keys($this->fieldMappings);
2014
    }
2015
2016
    /**
2017
     * {@inheritDoc}
2018
     */
2019
    public function getAssociationNames()
2020
    {
2021
        return array_keys($this->associationMappings);
2022
    }
2023
2024
    /**
2025
     * {@inheritDoc}
2026
     */
2027 23
    public function getTypeOfField($fieldName)
2028
    {
2029 23
        return isset($this->fieldMappings[$fieldName]) ?
2030 23
            $this->fieldMappings[$fieldName]['type'] : null;
2031
    }
2032
2033
    /**
2034
     * {@inheritDoc}
2035
     */
2036 6
    public function getAssociationTargetClass($assocName)
2037
    {
2038 6
        if ( ! isset($this->associationMappings[$assocName])) {
2039 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2040
        }
2041
2042 3
        return $this->associationMappings[$assocName]['targetDocument'];
2043
    }
2044
2045
    /**
2046
     * Retrieve the collectionClass associated with an association
2047
     *
2048
     * @param string $assocName
2049
     */
2050 2
    public function getAssociationCollectionClass($assocName)
2051
    {
2052 2
        if ( ! isset($this->associationMappings[$assocName])) {
2053
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2054
        }
2055
2056 2
        if ( ! array_key_exists('collectionClass', $this->associationMappings[$assocName])) {
2057
            throw new InvalidArgumentException("collectionClass can only be applied to 'embedMany' and 'referenceMany' associations.");
2058
        }
2059
2060 2
        return $this->associationMappings[$assocName]['collectionClass'];
2061
    }
2062
2063
    /**
2064
     * {@inheritDoc}
2065
     */
2066
    public function isAssociationInverseSide($fieldName)
2067
    {
2068
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2069
    }
2070
2071
    /**
2072
     * {@inheritDoc}
2073
     */
2074
    public function getAssociationMappedByTargetField($fieldName)
2075
    {
2076
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2077
    }
2078
}
2079