Completed
Pull Request — master (#1659)
by Andreas
10:41 queued 04:03
created

getAssociationMappedByTargetField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

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

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

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

}

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

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

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

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

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

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
518
    }
519
520
    /**
521
     * {@inheritDoc}
522
     */
523 929
    public function getReflectionClass()
524
    {
525 929
        if ( ! $this->reflClass) {
526 2
            $this->reflClass = new \ReflectionClass($this->name);
527
        }
528
529 929
        return $this->reflClass;
530
    }
531
532
    /**
533
     * {@inheritDoc}
534
     */
535 337
    public function isIdentifier($fieldName)
536
    {
537 337
        return $this->identifier === $fieldName;
538
    }
539
540
    /**
541
     * INTERNAL:
542
     * Sets the mapped identifier field of this class.
543
     *
544
     * @param string $identifier
545
     */
546 374
    public function setIdentifier($identifier)
547
    {
548 374
        $this->identifier = $identifier;
549 374
    }
550
551
    /**
552
     * {@inheritDoc}
553
     *
554
     * Since MongoDB only allows exactly one identifier field
555
     * this will always return an array with only one value
556
     */
557 40
    public function getIdentifier()
558
    {
559 40
        return array($this->identifier);
560
    }
561
562
    /**
563
     * {@inheritDoc}
564
     *
565
     * Since MongoDB only allows exactly one identifier field
566
     * this will always return an array with only one value
567
     */
568 98
    public function getIdentifierFieldNames()
569
    {
570 98
        return array($this->identifier);
571
    }
572
573
    /**
574
     * {@inheritDoc}
575
     */
576 573
    public function hasField($fieldName)
577
    {
578 573
        return isset($this->fieldMappings[$fieldName]);
579
    }
580
581
    /**
582
     * Sets the inheritance type used by the class and it's subclasses.
583
     *
584
     * @param integer $type
585
     */
586 390
    public function setInheritanceType($type)
587
    {
588 390
        $this->inheritanceType = $type;
589 390
    }
590
591
    /**
592
     * Checks whether a mapped field is inherited from an entity superclass.
593
     *
594
     * @param  string $fieldName
595
     *
596
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
597
     */
598 929
    public function isInheritedField($fieldName)
599
    {
600 929
        return isset($this->fieldMappings[$fieldName]['inherited']);
601
    }
602
603
    /**
604
     * Registers a custom repository class for the document class.
605
     *
606
     * @param string $repositoryClassName The class name of the custom repository.
607
     */
608 322
    public function setCustomRepositoryClass($repositoryClassName)
609
    {
610 322
        if ($this->isEmbeddedDocument || $this->isQueryResultDocument) {
611
            return;
612
        }
613
614 322 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...
615 4
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
616
        }
617
618 322
        $this->customRepositoryClassName = $repositoryClassName;
619 322
    }
620
621
    /**
622
     * Dispatches the lifecycle event of the given document by invoking all
623
     * registered callbacks.
624
     *
625
     * @param string $event     Lifecycle event
626
     * @param object $document  Document on which the event occurred
627
     * @param array  $arguments Arguments to pass to all callbacks
628
     * @throws \InvalidArgumentException if document class is not this class or
629
     *                                   a Proxy of this class
630
     */
631 678
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
632
    {
633 678
        if ( ! $document instanceof $this->name) {
634 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
635
        }
636
637 677
        if (empty($this->lifecycleCallbacks[$event])) {
638 663
            return;
639
        }
640
641 200
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
642 200
            if ($arguments !== null) {
643 199
                call_user_func_array(array($document, $callback), $arguments);
644
            } else {
645 200
                $document->$callback();
646
            }
647
        }
648 200
    }
649
650
    /**
651
     * Checks whether the class has callbacks registered for a lifecycle event.
652
     *
653
     * @param string $event Lifecycle event
654
     *
655
     * @return boolean
656
     */
657
    public function hasLifecycleCallbacks($event)
658
    {
659
        return ! empty($this->lifecycleCallbacks[$event]);
660
    }
661
662
    /**
663
     * Gets the registered lifecycle callbacks for an event.
664
     *
665
     * @param string $event
666
     * @return array
667
     */
668
    public function getLifecycleCallbacks($event)
669
    {
670
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
671
    }
672
673
    /**
674
     * Adds a lifecycle callback for documents of this class.
675
     *
676
     * If the callback is already registered, this is a NOOP.
677
     *
678
     * @param string $callback
679
     * @param string $event
680
     */
681 301
    public function addLifecycleCallback($callback, $event)
682
    {
683 301
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
684 1
            return;
685
        }
686
687 301
        $this->lifecycleCallbacks[$event][] = $callback;
688 301
    }
689
690
    /**
691
     * Sets the lifecycle callbacks for documents of this class.
692
     *
693
     * Any previously registered callbacks are overwritten.
694
     *
695
     * @param array $callbacks
696
     */
697 373
    public function setLifecycleCallbacks(array $callbacks)
698
    {
699 373
        $this->lifecycleCallbacks = $callbacks;
700 373
    }
701
702
    /**
703
     * Registers a method for loading document data before field hydration.
704
     *
705
     * Note: A method may be registered multiple times for different fields.
706
     * it will be invoked only once for the first field found.
707
     *
708
     * @param string       $method Method name
709
     * @param array|string $fields Database field name(s)
710
     */
711 15
    public function registerAlsoLoadMethod($method, $fields)
712
    {
713 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
714 15
    }
715
716
    /**
717
     * Sets the AlsoLoad methods for documents of this class.
718
     *
719
     * Any previously registered methods are overwritten.
720
     *
721
     * @param array $methods
722
     */
723 373
    public function setAlsoLoadMethods(array $methods)
724
    {
725 373
        $this->alsoLoadMethods = $methods;
726 373
    }
727
728
    /**
729
     * Sets the discriminator field.
730
     *
731
     * The field name is the the unmapped database field. Discriminator values
732
     * are only used to discern the hydration class and are not mapped to class
733
     * properties.
734
     *
735
     * @param string $discriminatorField
736
     *
737
     * @throws MappingException If the discriminator field conflicts with the
738
     *                          "name" attribute of a mapped field.
739
     */
740 403
    public function setDiscriminatorField($discriminatorField)
741
    {
742 403
        if ($discriminatorField === null) {
743 330
            $this->discriminatorField = null;
744
745 330
            return;
746
        }
747
748
        // Handle array argument with name/fieldName keys for BC
749 130
        if (is_array($discriminatorField)) {
750
            if (isset($discriminatorField['name'])) {
751
                $discriminatorField = $discriminatorField['name'];
752
            } elseif (isset($discriminatorField['fieldName'])) {
753
                $discriminatorField = $discriminatorField['fieldName'];
754
            }
755
        }
756
757 130
        foreach ($this->fieldMappings as $fieldMapping) {
758 4
            if ($discriminatorField == $fieldMapping['name']) {
759 4
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
760
            }
761
        }
762
763 129
        $this->discriminatorField = $discriminatorField;
764 129
    }
765
766
    /**
767
     * Sets the discriminator values used by this class.
768
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
769
     *
770
     * @param array $map
771
     *
772
     * @throws MappingException
773
     */
774 396
    public function setDiscriminatorMap(array $map)
775
    {
776 396
        foreach ($map as $value => $className) {
777 125
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
778 91
                $className = $this->namespace . '\\' . $className;
779
            }
780 125
            $this->discriminatorMap[$value] = $className;
781 125
            if ($this->name == $className) {
782 117
                $this->discriminatorValue = $value;
783
            } else {
784 120
                if ( ! class_exists($className)) {
785
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
786
                }
787 120
                if (is_subclass_of($className, $this->name)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $this->name can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
788 125
                    $this->subClasses[] = $className;
789
                }
790
            }
791
        }
792 396
    }
793
794
    /**
795
     * Sets the default discriminator value to be used for this class
796
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
797
     *
798
     * @param string $defaultDiscriminatorValue
799
     *
800
     * @throws MappingException
801
     */
802 380
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
803
    {
804 380
        if ($defaultDiscriminatorValue === null) {
805 373
            $this->defaultDiscriminatorValue = null;
806
807 373
            return;
808
        }
809
810 60
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
811
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
812
        }
813
814 60
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
815 60
    }
816
817
    /**
818
     * Sets the discriminator value for this class.
819
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
820
     * collection.
821
     *
822
     * @param string $value
823
     */
824 3
    public function setDiscriminatorValue($value)
825
    {
826 3
        $this->discriminatorMap[$value] = $this->name;
827 3
        $this->discriminatorValue = $value;
828 3
    }
829
830
    /**
831
     * Sets the slaveOkay option applied to collections for this class.
832
     *
833
     * @param boolean|null $slaveOkay
834
     *
835
     * @deprecated in version 1.2 and will be removed in 2.0.
836
     */
837 3
    public function setSlaveOkay($slaveOkay)
838
    {
839 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...
840 3
            sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.', __METHOD__),
841 3
            E_USER_DEPRECATED
842
        );
843 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...
844 3
    }
845
846
    /**
847
     * Add a index for this Document.
848
     *
849
     * @param array $keys Array of keys for the index.
850
     * @param array $options Array of options for the index.
851
     */
852 235
    public function addIndex($keys, array $options = array())
853
    {
854 235
        $this->indexes[] = array(
855 235 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...
856 235
                if ($value == 1 || $value == -1) {
857 63
                    return (int) $value;
858
                }
859 227
                if (is_string($value)) {
860 227
                    $lower = strtolower($value);
861 227
                    if ($lower === 'asc') {
862 220
                        return 1;
863 11
                    } elseif ($lower === 'desc') {
864 4
                        return -1;
865
                    }
866
                }
867 7
                return $value;
868 235
            }, $keys),
869 235
            'options' => $options
870
        );
871 235
    }
872
873
    /**
874
     * Set whether or not queries on this document should require indexes.
875
     *
876
     * @param bool $requireIndexes
877
     *
878
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
879
     */
880 920
    public function setRequireIndexes($requireIndexes)
881
    {
882 920
        if ($requireIndexes) {
883 24
            @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
884 24
                'requireIndexes was deprecated in version 1.2 and will be removed altogether in 2.0.',
885 24
                E_USER_DEPRECATED
886
            );
887
        }
888
889 920
        $this->requireIndexes = $requireIndexes;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...taInfo::$requireIndexes has been deprecated with message: property was deprecated in 1.2 and will be removed in 2.0

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

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

Loading history...
890 920
    }
891
892
    /**
893
     * Returns the array of indexes for this Document.
894
     *
895
     * @return array $indexes The array of indexes.
896
     */
897 54
    public function getIndexes()
898
    {
899 54
        return $this->indexes;
900
    }
901
902
    /**
903
     * Checks whether this document has indexes or not.
904
     *
905
     * @return boolean
906
     */
907
    public function hasIndexes()
908
    {
909
        return $this->indexes ? true : false;
910
    }
911
912
    /**
913
     * Set shard key for this Document.
914
     *
915
     * @param array $keys Array of document keys.
916
     * @param array $options Array of sharding options.
917
     *
918
     * @throws MappingException
919
     */
920 87
    public function setShardKey(array $keys, array $options = array())
921
    {
922 87
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
923 2
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
924
        }
925
926 87
        if ($this->isEmbeddedDocument) {
927 2
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
928
        }
929
930 85
        foreach (array_keys($keys) as $field) {
931 85
            if (! isset($this->fieldMappings[$field])) {
932 78
                continue;
933
            }
934
935 7
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
936 3
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
937
            }
938
939 4
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
940 4
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
941
            }
942
        }
943
944 81
        $this->shardKey = array(
945 81 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...
946 81
                if ($value == 1 || $value == -1) {
947 6
                    return (int) $value;
948
                }
949 80
                if (is_string($value)) {
950 80
                    $lower = strtolower($value);
951 80
                    if ($lower === 'asc') {
952 79
                        return 1;
953 53
                    } elseif ($lower === 'desc') {
954
                        return -1;
955
                    }
956
                }
957 53
                return $value;
958 81
            }, $keys),
959 81
            'options' => $options
960
        );
961 81
    }
962
963
    /**
964
     * @return array
965
     */
966 28
    public function getShardKey()
967
    {
968 28
        return $this->shardKey;
969
    }
970
971
    /**
972
     * Checks whether this document has shard key or not.
973
     *
974
     * @return bool
975
     */
976 616
    public function isSharded()
977
    {
978 616
        return $this->shardKey ? true : false;
979
    }
980
981
    /**
982
     * Sets the write concern used by this class.
983
     *
984
     * @param string $writeConcern
985
     */
986 387
    public function setWriteConcern($writeConcern)
987
    {
988 387
        $this->writeConcern = $writeConcern;
989 387
    }
990
991
    /**
992
     * @return string
993
     */
994 12
    public function getWriteConcern()
995
    {
996 12
        return $this->writeConcern;
997
    }
998
999
    /**
1000
     * Whether there is a write concern configured for this class.
1001
     *
1002
     * @return bool
1003
     */
1004 622
    public function hasWriteConcern()
1005
    {
1006 622
        return $this->writeConcern !== null;
1007
    }
1008
1009
    /**
1010
     * Sets the change tracking policy used by this class.
1011
     *
1012
     * @param integer $policy
1013
     */
1014 378
    public function setChangeTrackingPolicy($policy)
1015
    {
1016 378
        $this->changeTrackingPolicy = $policy;
1017 378
    }
1018
1019
    /**
1020
     * Whether the change tracking policy of this class is "deferred explicit".
1021
     *
1022
     * @return boolean
1023
     */
1024 75
    public function isChangeTrackingDeferredExplicit()
1025
    {
1026 75
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
1027
    }
1028
1029
    /**
1030
     * Whether the change tracking policy of this class is "deferred implicit".
1031
     *
1032
     * @return boolean
1033
     */
1034 643
    public function isChangeTrackingDeferredImplicit()
1035
    {
1036 643
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1037
    }
1038
1039
    /**
1040
     * Whether the change tracking policy of this class is "notify".
1041
     *
1042
     * @return boolean
1043
     */
1044 352
    public function isChangeTrackingNotify()
1045
    {
1046 352
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1047
    }
1048
1049
    /**
1050
     * Gets the ReflectionProperties of the mapped class.
1051
     *
1052
     * @return array An array of ReflectionProperty instances.
1053
     */
1054 98
    public function getReflectionProperties()
1055
    {
1056 98
        return $this->reflFields;
1057
    }
1058
1059
    /**
1060
     * Gets a ReflectionProperty for a specific field of the mapped class.
1061
     *
1062
     * @param string $name
1063
     *
1064
     * @return \ReflectionProperty
1065
     */
1066
    public function getReflectionProperty($name)
1067
    {
1068
        return $this->reflFields[$name];
1069
    }
1070
1071
    /**
1072
     * {@inheritDoc}
1073
     */
1074 935
    public function getName()
1075
    {
1076 935
        return $this->name;
1077
    }
1078
1079
    /**
1080
     * The namespace this Document class belongs to.
1081
     *
1082
     * @return string $namespace The namespace name.
1083
     */
1084
    public function getNamespace()
1085
    {
1086
        return $this->namespace;
1087
    }
1088
1089
    /**
1090
     * Returns the database this Document is mapped to.
1091
     *
1092
     * @return string $db The database name.
1093
     */
1094 854
    public function getDatabase()
1095
    {
1096 854
        return $this->db;
1097
    }
1098
1099
    /**
1100
     * Set the database this Document is mapped to.
1101
     *
1102
     * @param string $db The database name
1103
     */
1104 104
    public function setDatabase($db)
1105
    {
1106 104
        $this->db = $db;
1107 104
    }
1108
1109
    /**
1110
     * Get the collection this Document is mapped to.
1111
     *
1112
     * @return string $collection The collection name.
1113
     */
1114 859
    public function getCollection()
1115
    {
1116 859
        return $this->collection;
1117
    }
1118
1119
    /**
1120
     * Sets the collection this Document is mapped to.
1121
     *
1122
     * @param array|string $name
1123
     *
1124
     * @throws \InvalidArgumentException
1125
     */
1126 965
    public function setCollection($name)
1127
    {
1128 965
        if (is_array($name)) {
1129
            if ( ! isset($name['name'])) {
1130
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
1131
            }
1132
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
1133
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
1134
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
1135
            $this->collection = $name['name'];
1136
        } else {
1137 965
            $this->collection = $name;
1138
        }
1139 965
    }
1140
1141
    /**
1142
     * Get whether or not the documents collection is capped.
1143
     *
1144
     * @return boolean
1145
     */
1146 4
    public function getCollectionCapped()
1147
    {
1148 4
        return $this->collectionCapped;
1149
    }
1150
1151
    /**
1152
     * Set whether or not the documents collection is capped.
1153
     *
1154
     * @param boolean $bool
1155
     */
1156 1
    public function setCollectionCapped($bool)
1157
    {
1158 1
        $this->collectionCapped = $bool;
1159 1
    }
1160
1161
    /**
1162
     * Get the collection size
1163
     *
1164
     * @return integer
1165
     */
1166 4
    public function getCollectionSize()
1167
    {
1168 4
        return $this->collectionSize;
1169
    }
1170
1171
    /**
1172
     * Set the collection size.
1173
     *
1174
     * @param integer $size
1175
     */
1176 1
    public function setCollectionSize($size)
1177
    {
1178 1
        $this->collectionSize = $size;
1179 1
    }
1180
1181
    /**
1182
     * Get the collection max.
1183
     *
1184
     * @return integer
1185
     */
1186 4
    public function getCollectionMax()
1187
    {
1188 4
        return $this->collectionMax;
1189
    }
1190
1191
    /**
1192
     * Set the collection max.
1193
     *
1194
     * @param integer $max
1195
     */
1196 1
    public function setCollectionMax($max)
1197
    {
1198 1
        $this->collectionMax = $max;
1199 1
    }
1200
1201
    /**
1202
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1203
     *
1204
     * @return boolean
1205
     */
1206
    public function isMappedToCollection()
1207
    {
1208
        return $this->collection ? true : false;
1209
    }
1210
1211
    /**
1212
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1213
     *
1214
     * @return boolean
1215
     */
1216 800
    public function isFile()
1217
    {
1218 800
        return $this->file ? true : false;
1219
    }
1220
1221
    /**
1222
     * Returns the file field name.
1223
     *
1224
     * @return string $file The file field name.
1225
     */
1226 373
    public function getFile()
1227
    {
1228 373
        return $this->file;
1229
    }
1230
1231
    /**
1232
     * Set the field name that stores the grid file.
1233
     *
1234
     * @param string $file
1235
     */
1236 374
    public function setFile($file)
1237
    {
1238 374
        $this->file = $file;
1239 374
    }
1240
1241
    /**
1242
     * Returns the distance field name.
1243
     *
1244
     * @return string $distance The distance field name.
1245
     */
1246
    public function getDistance()
1247
    {
1248
        return $this->distance;
1249
    }
1250
1251
    /**
1252
     * Set the field name that stores the distance.
1253
     *
1254
     * @param string $distance
1255
     */
1256 1
    public function setDistance($distance)
1257
    {
1258 1
        $this->distance = $distance;
1259 1
    }
1260
1261
    /**
1262
     * Map a field.
1263
     *
1264
     * @param array $mapping The mapping information.
1265
     *
1266
     * @return array
1267
     *
1268
     * @throws MappingException
1269
     */
1270 979
    public function mapField(array $mapping)
1271
    {
1272 979
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1273 10
            $mapping['fieldName'] = $mapping['name'];
1274
        }
1275 979
        if ( ! isset($mapping['fieldName'])) {
1276
            throw MappingException::missingFieldName($this->name);
1277
        }
1278 979
        if ( ! isset($mapping['name'])) {
1279 969
            $mapping['name'] = $mapping['fieldName'];
1280
        }
1281 979
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1282 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1283
        }
1284 978
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1285
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1286
        }
1287 978
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1288 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1289
        }
1290 977 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...
1291 627
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1292
        }
1293 977
        if (isset($mapping['collectionClass'])) {
1294 65 View Code Duplication
            if (strpos($mapping['collectionClass'], '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1295 63
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1296
            }
1297 65
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1298
        }
1299 977
        if ( ! empty($mapping['collectionClass'])) {
1300 65
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1301 65
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1302 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1303
            }
1304
        }
1305
1306 976
        if (isset($mapping['discriminatorMap'])) {
1307 129
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1308 129 View Code Duplication
                if (strpos($class, '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1309 129
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1310
                }
1311
            }
1312
        }
1313
1314 976
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1315 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1316
        }
1317
1318 975
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1319
1320 975
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1321 654
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1322
        }
1323
1324 975
        if (isset($mapping['embedded'])) {
1325 611
            unset($mapping['cascade']);
1326 970
        } elseif (isset($mapping['cascade'])) {
1327 413
            $mapping['cascade'] = $cascades;
1328
        }
1329
1330 975
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1331 975
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1332 975
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1333 975
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1334 975
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1335
1336 975
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1337 63
            $mapping['file'] = true;
1338
        }
1339 975
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1340 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1341
        }
1342 975 View Code Duplication
        if (isset($mapping['file']) && $mapping['file'] === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1343 63
            $this->file = $mapping['fieldName'];
1344 63
            $mapping['name'] = 'file';
1345
        }
1346 975 View Code Duplication
        if (isset($mapping['distance']) && $mapping['distance'] === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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