Completed
Push — master ( 315dca...845f07 )
by Andreas
12s
created

ClassMetadataInfo::getReferenceFieldName()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

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