Completed
Push — master ( a5ea00...87aba0 )
by Maciej
17s
created

Doctrine/ODM/MongoDB/Mapping/ClassMetadataInfo.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
107
    /* The inheritance mapping types */
108
    /**
109
     * NONE means the class does not participate in an inheritance hierarchy
110
     * and therefore does not need an inheritance mapping type.
111
     */
112
    const INHERITANCE_TYPE_NONE = 1;
113
114
    /**
115
     * SINGLE_COLLECTION means the class will be persisted according to the rules of
116
     * <tt>Single Collection Inheritance</tt>.
117
     */
118
    const INHERITANCE_TYPE_SINGLE_COLLECTION = 2;
119
120
    /**
121
     * COLLECTION_PER_CLASS means the class will be persisted according to the rules
122
     * of <tt>Concrete Collection Inheritance</tt>.
123
     */
124
    const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3;
125
126
    /**
127
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
128
     * by doing a property-by-property comparison with the original data. This will
129
     * be done for all entities that are in MANAGED state at commit-time.
130
     *
131
     * This is the default change tracking policy.
132
     */
133
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
134
135
    /**
136
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
137
     * by doing a property-by-property comparison with the original data. This will
138
     * be done only for entities that were explicitly saved (through persist() or a cascade).
139
     */
140
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
141
142
    /**
143
     * NOTIFY means that Doctrine relies on the entities sending out notifications
144
     * when their properties change. Such entity classes must implement
145
     * the <tt>NotifyPropertyChanged</tt> interface.
146
     */
147
    const CHANGETRACKING_NOTIFY = 3;
148
149
    /**
150
     * SET means that fields will be written to the database using a $set operator
151
     */
152
    const STORAGE_STRATEGY_SET = 'set';
153
154
    /**
155
     * INCREMENT means that fields will be written to the database by calculating
156
     * the difference and using the $inc operator
157
     */
158
    const STORAGE_STRATEGY_INCREMENT = 'increment';
159
160
    const STORAGE_STRATEGY_PUSH_ALL = 'pushAll';
161
    const STORAGE_STRATEGY_ADD_TO_SET = 'addToSet';
162
    const STORAGE_STRATEGY_ATOMIC_SET = 'atomicSet';
163
    const STORAGE_STRATEGY_ATOMIC_SET_ARRAY = 'atomicSetArray';
164
    const STORAGE_STRATEGY_SET_ARRAY = 'setArray';
165
166
    /**
167
     * READ-ONLY: The name of the mongo database the document is mapped to.
168
     */
169
    public $db;
170
171
    /**
172
     * READ-ONLY: The name of the mongo collection the document is mapped to.
173
     */
174
    public $collection;
175
176
    /**
177
     * READ-ONLY: If the collection should be a fixed size.
178
     */
179
    public $collectionCapped;
180
181
    /**
182
     * READ-ONLY: If the collection is fixed size, its size in bytes.
183
     */
184
    public $collectionSize;
185
186
    /**
187
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
188
     */
189
    public $collectionMax;
190
191
    /**
192
     * READ-ONLY: Describes the level of acknowledgement requested from MongoDB for write operations.
193
     */
194
    public $writeConcern;
195
196
    /**
197
     * READ-ONLY: The field name of the document identifier.
198
     */
199
    public $identifier;
200
201
    /**
202
     * READ-ONLY: The field that stores a file reference and indicates the
203
     * document is a file and should be stored on the MongoGridFS.
204
     */
205
    public $file;
206
207
    /**
208
     * READ-ONLY: The field that stores the calculated distance when performing geo spatial
209
     * queries.
210
     */
211
    public $distance;
212
213
    /**
214
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
215
     *
216
     * @deprecated in version 1.2 and will be removed in 2.0.
217
     */
218
    public $slaveOkay;
219
220
    /**
221
     * READ-ONLY: The array of indexes for the document collection.
222
     */
223
    public $indexes = array();
224
225
    /**
226
     * READ-ONLY: Keys and options describing shard key. Only for sharded collections.
227
     */
228
    public $shardKey;
229
230
    /**
231
     * READ-ONLY: Whether or not queries on this document should require indexes.
232
     *
233
     * @deprecated property was deprecated in 1.2 and will be removed in 2.0
234
     */
235
    public $requireIndexes = false;
236
237
    /**
238
     * READ-ONLY: The name of the document class.
239
     */
240
    public $name;
241
242
    /**
243
     * READ-ONLY: The namespace the document class is contained in.
244
     *
245
     * @var string
246
     * @todo Not really needed. Usage could be localized.
247
     */
248
    public $namespace;
249
250
    /**
251
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
252
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
253
     * as {@link $documentName}.
254
     *
255
     * @var string
256
     */
257
    public $rootDocumentName;
258
259
    /**
260
     * The name of the custom repository class used for the document class.
261
     * (Optional).
262
     *
263
     * @var string
264
     */
265
    public $customRepositoryClassName;
266
267
    /**
268
     * READ-ONLY: The names of the parent classes (ancestors).
269
     *
270
     * @var array
271
     */
272
    public $parentClasses = array();
273
274
    /**
275
     * READ-ONLY: The names of all subclasses (descendants).
276
     *
277
     * @var array
278
     */
279
    public $subClasses = array();
280
281
    /**
282
     * The ReflectionProperty instances of the mapped class.
283
     *
284
     * @var \ReflectionProperty[]
285
     */
286
    public $reflFields = array();
287
288
    /**
289
     * READ-ONLY: The inheritance mapping type used by the class.
290
     *
291
     * @var integer
292
     */
293
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
294
295
    /**
296
     * READ-ONLY: The Id generator type used by the class.
297
     *
298
     * @var string
299
     */
300
    public $generatorType = self::GENERATOR_TYPE_AUTO;
301
302
    /**
303
     * READ-ONLY: The Id generator options.
304
     *
305
     * @var array
306
     */
307
    public $generatorOptions = array();
308
309
    /**
310
     * READ-ONLY: The ID generator used for generating IDs for this class.
311
     *
312
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
313
     */
314
    public $idGenerator;
315
316
    /**
317
     * READ-ONLY: The field mappings of the class.
318
     * Keys are field names and values are mapping definitions.
319
     *
320
     * The mapping definition array has the following values:
321
     *
322
     * - <b>fieldName</b> (string)
323
     * The name of the field in the Document.
324
     *
325
     * - <b>id</b> (boolean, optional)
326
     * Marks the field as the primary key of the document. Multiple fields of an
327
     * document can have the id attribute, forming a composite key.
328
     *
329
     * @var array
330
     */
331
    public $fieldMappings = array();
332
333
    /**
334
     * READ-ONLY: The association mappings of the class.
335
     * Keys are field names and values are mapping definitions.
336
     *
337
     * @var array
338
     */
339
    public $associationMappings = array();
340
341
    /**
342
     * READ-ONLY: Array of fields to also load with a given method.
343
     *
344
     * @var array
345
     */
346
    public $alsoLoadMethods = array();
347
348
    /**
349
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
350
     *
351
     * @var array
352
     */
353
    public $lifecycleCallbacks = array();
354
355
    /**
356
     * READ-ONLY: The discriminator value of this class.
357
     *
358
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
359
     * where a discriminator field is used.</b>
360
     *
361
     * @var mixed
362
     * @see discriminatorField
363
     */
364
    public $discriminatorValue;
365
366
    /**
367
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
368
     *
369
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
370
     * where a discriminator field is used.</b>
371
     *
372
     * @var mixed
373
     * @see discriminatorField
374
     */
375
    public $discriminatorMap = array();
376
377
    /**
378
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
379
     * inheritance mapping.
380
     *
381
     * @var string
382
     */
383
    public $discriminatorField;
384
385
    /**
386
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
387
     *
388
     * @var string
389
     * @see discriminatorField
390
     */
391
    public $defaultDiscriminatorValue;
392
393
    /**
394
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
395
     *
396
     * @var boolean
397
     */
398
    public $isMappedSuperclass = false;
399
400
    /**
401
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
402
     *
403
     * @var boolean
404
     */
405
    public $isEmbeddedDocument = false;
406
407
    /**
408
     * READ-ONLY: Whether this class describes the mapping of an aggregation result document.
409
     *
410
     * @var boolean
411
     */
412
    public $isQueryResultDocument = false;
413
414
    /**
415
     * READ-ONLY: The policy used for change-tracking on entities of this class.
416
     *
417
     * @var integer
418
     */
419
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
420
421
    /**
422
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
423
     * with optimistic locking.
424
     *
425
     * @var boolean $isVersioned
426
     */
427
    public $isVersioned;
428
429
    /**
430
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
431
     *
432
     * @var mixed $versionField
433
     */
434
    public $versionField;
435
436
    /**
437
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
438
     * locking.
439
     *
440
     * @var boolean $isLockable
441
     */
442
    public $isLockable;
443
444
    /**
445
     * READ-ONLY: The name of the field which is used for locking a document.
446
     *
447
     * @var mixed $lockField
448
     */
449
    public $lockField;
450
451
    /**
452
     * The ReflectionClass instance of the mapped class.
453
     *
454
     * @var \ReflectionClass
455
     */
456
    public $reflClass;
457
458
    /**
459
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
460
     * metadata of the class with the given name.
461
     *
462
     * @param string $documentName The name of the document class the new instance is used for.
463
     */
464 975
    public function __construct($documentName)
465
    {
466 975
        $this->name = $documentName;
467 975
        $this->rootDocumentName = $documentName;
468 975
    }
469
470
    /**
471
     * {@inheritDoc}
472
     */
473 903
    public function getReflectionClass()
474
    {
475 903
        if ( ! $this->reflClass) {
476 2
            $this->reflClass = new \ReflectionClass($this->name);
477
        }
478
479 903
        return $this->reflClass;
480
    }
481
482
    /**
483
     * {@inheritDoc}
484
     */
485 330
    public function isIdentifier($fieldName)
486
    {
487 330
        return $this->identifier === $fieldName;
488
    }
489
490
    /**
491
     * INTERNAL:
492
     * Sets the mapped identifier field of this class.
493
     *
494
     * @param string $identifier
495
     */
496 370
    public function setIdentifier($identifier)
497
    {
498 370
        $this->identifier = $identifier;
499 370
    }
500
501
    /**
502
     * {@inheritDoc}
503
     *
504
     * Since MongoDB only allows exactly one identifier field
505
     * this will always return an array with only one value
506
     */
507 40
    public function getIdentifier()
508
    {
509 40
        return array($this->identifier);
510
    }
511
512
    /**
513
     * {@inheritDoc}
514
     *
515
     * Since MongoDB only allows exactly one identifier field
516
     * this will always return an array with only one value
517
     */
518 98
    public function getIdentifierFieldNames()
519
    {
520 98
        return array($this->identifier);
521
    }
522
523
    /**
524
     * {@inheritDoc}
525
     */
526 561
    public function hasField($fieldName)
527
    {
528 561
        return isset($this->fieldMappings[$fieldName]);
529
    }
530
531
    /**
532
     * Sets the inheritance type used by the class and it's subclasses.
533
     *
534
     * @param integer $type
535
     */
536 386
    public function setInheritanceType($type)
537
    {
538 386
        $this->inheritanceType = $type;
539 386
    }
540
541
    /**
542
     * Checks whether a mapped field is inherited from an entity superclass.
543
     *
544
     * @param  string $fieldName
545
     *
546
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
547
     */
548 903
    public function isInheritedField($fieldName)
549
    {
550 903
        return isset($this->fieldMappings[$fieldName]['inherited']);
551
    }
552
553
    /**
554
     * Registers a custom repository class for the document class.
555
     *
556
     * @param string $repositoryClassName The class name of the custom repository.
557
     */
558 318
    public function setCustomRepositoryClass($repositoryClassName)
559
    {
560 318
        if ($this->isEmbeddedDocument || $this->isQueryResultDocument) {
561
            return;
562
        }
563
564 318 View Code Duplication
        if ($repositoryClassName && strpos($repositoryClassName, '\\') === false && strlen($this->namespace)) {
565 4
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
566
        }
567
568 318
        $this->customRepositoryClassName = $repositoryClassName;
569 318
    }
570
571
    /**
572
     * Dispatches the lifecycle event of the given document by invoking all
573
     * registered callbacks.
574
     *
575
     * @param string $event     Lifecycle event
576
     * @param object $document  Document on which the event occurred
577
     * @param array  $arguments Arguments to pass to all callbacks
578
     * @throws \InvalidArgumentException if document class is not this class or
579
     *                                   a Proxy of this class
580
     */
581 660
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
582
    {
583 660
        if ( ! $document instanceof $this->name) {
584 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
585
        }
586
587 659
        if (empty($this->lifecycleCallbacks[$event])) {
588 645
            return;
589
        }
590
591 196
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
592 196
            if ($arguments !== null) {
593 195
                call_user_func_array(array($document, $callback), $arguments);
594
            } else {
595 2
                $document->$callback();
596
            }
597
        }
598 196
    }
599
600
    /**
601
     * Checks whether the class has callbacks registered for a lifecycle event.
602
     *
603
     * @param string $event Lifecycle event
604
     *
605
     * @return boolean
606
     */
607
    public function hasLifecycleCallbacks($event)
608
    {
609
        return ! empty($this->lifecycleCallbacks[$event]);
610
    }
611
612
    /**
613
     * Gets the registered lifecycle callbacks for an event.
614
     *
615
     * @param string $event
616
     * @return array
617
     */
618
    public function getLifecycleCallbacks($event)
619
    {
620
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
621
    }
622
623
    /**
624
     * Adds a lifecycle callback for documents of this class.
625
     *
626
     * If the callback is already registered, this is a NOOP.
627
     *
628
     * @param string $callback
629
     * @param string $event
630
     */
631 297
    public function addLifecycleCallback($callback, $event)
632
    {
633 297
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
634 1
            return;
635
        }
636
637 297
        $this->lifecycleCallbacks[$event][] = $callback;
638 297
    }
639
640
    /**
641
     * Sets the lifecycle callbacks for documents of this class.
642
     *
643
     * Any previously registered callbacks are overwritten.
644
     *
645
     * @param array $callbacks
646
     */
647 369
    public function setLifecycleCallbacks(array $callbacks)
648
    {
649 369
        $this->lifecycleCallbacks = $callbacks;
650 369
    }
651
652
    /**
653
     * Registers a method for loading document data before field hydration.
654
     *
655
     * Note: A method may be registered multiple times for different fields.
656
     * it will be invoked only once for the first field found.
657
     *
658
     * @param string       $method Method name
659
     * @param array|string $fields Database field name(s)
660
     */
661 15
    public function registerAlsoLoadMethod($method, $fields)
662
    {
663 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
664 15
    }
665
666
    /**
667
     * Sets the AlsoLoad methods for documents of this class.
668
     *
669
     * Any previously registered methods are overwritten.
670
     *
671
     * @param array $methods
672
     */
673 369
    public function setAlsoLoadMethods(array $methods)
674
    {
675 369
        $this->alsoLoadMethods = $methods;
676 369
    }
677
678
    /**
679
     * Sets the discriminator field.
680
     *
681
     * The field name is the the unmapped database field. Discriminator values
682
     * are only used to discern the hydration class and are not mapped to class
683
     * properties.
684
     *
685
     * @param string $discriminatorField
686
     *
687
     * @throws MappingException If the discriminator field conflicts with the
688
     *                          "name" attribute of a mapped field.
689
     */
690 399
    public function setDiscriminatorField($discriminatorField)
691
    {
692 399
        if ($discriminatorField === null) {
693 326
            $this->discriminatorField = null;
694
695 326
            return;
696
        }
697
698
        // Handle array argument with name/fieldName keys for BC
699 130
        if (is_array($discriminatorField)) {
700
            if (isset($discriminatorField['name'])) {
701
                $discriminatorField = $discriminatorField['name'];
702
            } elseif (isset($discriminatorField['fieldName'])) {
703
                $discriminatorField = $discriminatorField['fieldName'];
704
            }
705
        }
706
707 130
        foreach ($this->fieldMappings as $fieldMapping) {
708 4
            if ($discriminatorField == $fieldMapping['name']) {
709 1
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
710
            }
711
        }
712
713 129
        $this->discriminatorField = $discriminatorField;
714 129
    }
715
716
    /**
717
     * Sets the discriminator values used by this class.
718
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
719
     *
720
     * @param array $map
721
     *
722
     * @throws MappingException
723
     */
724 392
    public function setDiscriminatorMap(array $map)
725
    {
726 392
        foreach ($map as $value => $className) {
727 125
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
728 91
                $className = $this->namespace . '\\' . $className;
729
            }
730 125
            $this->discriminatorMap[$value] = $className;
731 125
            if ($this->name == $className) {
732 117
                $this->discriminatorValue = $value;
733
            } else {
734 120
                if ( ! class_exists($className)) {
735
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
736
                }
737 120
                if (is_subclass_of($className, $this->name)) {
738 106
                    $this->subClasses[] = $className;
739
                }
740
            }
741
        }
742 392
    }
743
744
    /**
745
     * Sets the default discriminator value to be used for this class
746
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
747
     *
748
     * @param string $defaultDiscriminatorValue
749
     *
750
     * @throws MappingException
751
     */
752 376
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
753
    {
754 376
        if ($defaultDiscriminatorValue === null) {
755 369
            $this->defaultDiscriminatorValue = null;
756
757 369
            return;
758
        }
759
760 60
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
761
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
762
        }
763
764 60
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
765 60
    }
766
767
    /**
768
     * Sets the discriminator value for this class.
769
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
770
     * collection.
771
     *
772
     * @param string $value
773
     */
774 3
    public function setDiscriminatorValue($value)
775
    {
776 3
        $this->discriminatorMap[$value] = $this->name;
777 3
        $this->discriminatorValue = $value;
778 3
    }
779
780
    /**
781
     * Sets the slaveOkay option applied to collections for this class.
782
     *
783
     * @param boolean|null $slaveOkay
784
     *
785
     * @deprecated in version 1.2 and will be removed in 2.0.
786
     */
787 3
    public function setSlaveOkay($slaveOkay)
788
    {
789 3
        @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...
790 3
            sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.'),
791 3
            E_USER_DEPRECATED
792
        );
793 3
        $this->slaveOkay = $slaveOkay === null ? null : (boolean) $slaveOkay;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...etadataInfo::$slaveOkay has been deprecated with message: in version 1.2 and will be removed in 2.0.

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

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

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