Completed
Pull Request — master (#1623)
by Andreas
09:05
created

ClassMetadataInfo::hasIndexes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 0
crap 6
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
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
461
     * metadata of the class with the given name.
462
     *
463
     * @param string $documentName The name of the document class the new instance is used for.
464
     */
465 994
    public function __construct($documentName)
466
    {
467 994
        $this->name = $documentName;
468 994
        $this->rootDocumentName = $documentName;
469 994
    }
470
471
    /**
472
     * Helper method to get reference id of ref* type references
473
     * @param mixed  $reference
474
     * @param string $storeAs
475
     * @return mixed
476
     * @internal
477
     */
478 117
    public static function getReferenceId($reference, $storeAs)
479
    {
480 117
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID ? $reference : $reference[ClassMetadataInfo::getReferencePrefix($storeAs) . 'id'];
481
    }
482
483
    /**
484
     * Returns the reference prefix used for a reference
485
     * @param string $storeAs
486
     * @return string
487
     */
488 196
    private static function getReferencePrefix($storeAs)
489
    {
490 196
        if (!in_array($storeAs, [ClassMetadataInfo::REFERENCE_STORE_AS_REF, ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF, ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB])) {
491
            throw new \LogicException('Can only get a reference prefix for DBRef and reference arrays');
492
        }
493
494 196
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_REF ? '' : '$';
495
    }
496
497
    /**
498
     * Returns a fully qualified field name for a given reference
499
     * @param string $storeAs
500
     * @param string $pathPrefix The field path prefix
501
     * @return string
502
     * @internal
503
     */
504 142
    public static function getReferenceFieldName($storeAs, $pathPrefix = '')
505
    {
506 142
        if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID) {
507 103
            return $pathPrefix;
508
        }
509
510 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...
511
    }
512
513
    /**
514
     * {@inheritDoc}
515
     */
516 922
    public function getReflectionClass()
517
    {
518 922
        if ( ! $this->reflClass) {
519 2
            $this->reflClass = new \ReflectionClass($this->name);
520
        }
521
522 922
        return $this->reflClass;
523
    }
524
525
    /**
526
     * {@inheritDoc}
527
     */
528 336
    public function isIdentifier($fieldName)
529
    {
530 336
        return $this->identifier === $fieldName;
531
    }
532
533
    /**
534
     * INTERNAL:
535
     * Sets the mapped identifier field of this class.
536
     *
537
     * @param string $identifier
538
     */
539 373
    public function setIdentifier($identifier)
540
    {
541 373
        $this->identifier = $identifier;
542 373
    }
543
544
    /**
545
     * {@inheritDoc}
546
     *
547
     * Since MongoDB only allows exactly one identifier field
548
     * this will always return an array with only one value
549
     */
550 40
    public function getIdentifier()
551
    {
552 40
        return array($this->identifier);
553
    }
554
555
    /**
556
     * {@inheritDoc}
557
     *
558
     * Since MongoDB only allows exactly one identifier field
559
     * this will always return an array with only one value
560
     */
561 98
    public function getIdentifierFieldNames()
562
    {
563 98
        return array($this->identifier);
564
    }
565
566
    /**
567
     * {@inheritDoc}
568
     */
569 567
    public function hasField($fieldName)
570
    {
571 567
        return isset($this->fieldMappings[$fieldName]);
572
    }
573
574
    /**
575
     * Sets the inheritance type used by the class and it's subclasses.
576
     *
577
     * @param integer $type
578
     */
579 389
    public function setInheritanceType($type)
580
    {
581 389
        $this->inheritanceType = $type;
582 389
    }
583
584
    /**
585
     * Checks whether a mapped field is inherited from an entity superclass.
586
     *
587
     * @param  string $fieldName
588
     *
589
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
590
     */
591 922
    public function isInheritedField($fieldName)
592
    {
593 922
        return isset($this->fieldMappings[$fieldName]['inherited']);
594
    }
595
596
    /**
597
     * Registers a custom repository class for the document class.
598
     *
599
     * @param string $repositoryClassName The class name of the custom repository.
600
     */
601 321
    public function setCustomRepositoryClass($repositoryClassName)
602
    {
603 321
        if ($this->isEmbeddedDocument || $this->isQueryResultDocument) {
604
            return;
605
        }
606
607 321 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...
608 4
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
609
        }
610
611 321
        $this->customRepositoryClassName = $repositoryClassName;
612 321
    }
613
614
    /**
615
     * Dispatches the lifecycle event of the given document by invoking all
616
     * registered callbacks.
617
     *
618
     * @param string $event     Lifecycle event
619
     * @param object $document  Document on which the event occurred
620
     * @param array  $arguments Arguments to pass to all callbacks
621
     * @throws \InvalidArgumentException if document class is not this class or
622
     *                                   a Proxy of this class
623
     */
624 671
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
625
    {
626 671
        if ( ! $document instanceof $this->name) {
627 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
628
        }
629
630 670
        if (empty($this->lifecycleCallbacks[$event])) {
631 656
            return;
632
        }
633
634 199
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
635 199
            if ($arguments !== null) {
636 198
                call_user_func_array(array($document, $callback), $arguments);
637
            } else {
638 199
                $document->$callback();
639
            }
640
        }
641 199
    }
642
643
    /**
644
     * Checks whether the class has callbacks registered for a lifecycle event.
645
     *
646
     * @param string $event Lifecycle event
647
     *
648
     * @return boolean
649
     */
650
    public function hasLifecycleCallbacks($event)
651
    {
652
        return ! empty($this->lifecycleCallbacks[$event]);
653
    }
654
655
    /**
656
     * Gets the registered lifecycle callbacks for an event.
657
     *
658
     * @param string $event
659
     * @return array
660
     */
661
    public function getLifecycleCallbacks($event)
662
    {
663
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
664
    }
665
666
    /**
667
     * Adds a lifecycle callback for documents of this class.
668
     *
669
     * If the callback is already registered, this is a NOOP.
670
     *
671
     * @param string $callback
672
     * @param string $event
673
     */
674 300
    public function addLifecycleCallback($callback, $event)
675
    {
676 300
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
677 1
            return;
678
        }
679
680 300
        $this->lifecycleCallbacks[$event][] = $callback;
681 300
    }
682
683
    /**
684
     * Sets the lifecycle callbacks for documents of this class.
685
     *
686
     * Any previously registered callbacks are overwritten.
687
     *
688
     * @param array $callbacks
689
     */
690 372
    public function setLifecycleCallbacks(array $callbacks)
691
    {
692 372
        $this->lifecycleCallbacks = $callbacks;
693 372
    }
694
695
    /**
696
     * Registers a method for loading document data before field hydration.
697
     *
698
     * Note: A method may be registered multiple times for different fields.
699
     * it will be invoked only once for the first field found.
700
     *
701
     * @param string       $method Method name
702
     * @param array|string $fields Database field name(s)
703
     */
704 15
    public function registerAlsoLoadMethod($method, $fields)
705
    {
706 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
707 15
    }
708
709
    /**
710
     * Sets the AlsoLoad methods for documents of this class.
711
     *
712
     * Any previously registered methods are overwritten.
713
     *
714
     * @param array $methods
715
     */
716 372
    public function setAlsoLoadMethods(array $methods)
717
    {
718 372
        $this->alsoLoadMethods = $methods;
719 372
    }
720
721
    /**
722
     * Sets the discriminator field.
723
     *
724
     * The field name is the the unmapped database field. Discriminator values
725
     * are only used to discern the hydration class and are not mapped to class
726
     * properties.
727
     *
728
     * @param string $discriminatorField
729
     *
730
     * @throws MappingException If the discriminator field conflicts with the
731
     *                          "name" attribute of a mapped field.
732
     */
733 402
    public function setDiscriminatorField($discriminatorField)
734
    {
735 402
        if ($discriminatorField === null) {
736 329
            $this->discriminatorField = null;
737
738 329
            return;
739
        }
740
741
        // Handle array argument with name/fieldName keys for BC
742 130
        if (is_array($discriminatorField)) {
743
            if (isset($discriminatorField['name'])) {
744
                $discriminatorField = $discriminatorField['name'];
745
            } elseif (isset($discriminatorField['fieldName'])) {
746
                $discriminatorField = $discriminatorField['fieldName'];
747
            }
748
        }
749
750 130
        foreach ($this->fieldMappings as $fieldMapping) {
751 4
            if ($discriminatorField == $fieldMapping['name']) {
752 4
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
753
            }
754
        }
755
756 129
        $this->discriminatorField = $discriminatorField;
757 129
    }
758
759
    /**
760
     * Sets the discriminator values used by this class.
761
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
762
     *
763
     * @param array $map
764
     *
765
     * @throws MappingException
766
     */
767 395
    public function setDiscriminatorMap(array $map)
768
    {
769 395
        foreach ($map as $value => $className) {
770 125
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
771 91
                $className = $this->namespace . '\\' . $className;
772
            }
773 125
            $this->discriminatorMap[$value] = $className;
774 125
            if ($this->name == $className) {
775 117
                $this->discriminatorValue = $value;
776
            } else {
777 120
                if ( ! class_exists($className)) {
778
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
779
                }
780 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...
781 125
                    $this->subClasses[] = $className;
782
                }
783
            }
784
        }
785 395
    }
786
787
    /**
788
     * Sets the default discriminator value to be used for this class
789
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
790
     *
791
     * @param string $defaultDiscriminatorValue
792
     *
793
     * @throws MappingException
794
     */
795 379
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
796
    {
797 379
        if ($defaultDiscriminatorValue === null) {
798 372
            $this->defaultDiscriminatorValue = null;
799
800 372
            return;
801
        }
802
803 60
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
804
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
805
        }
806
807 60
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
808 60
    }
809
810
    /**
811
     * Sets the discriminator value for this class.
812
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
813
     * collection.
814
     *
815
     * @param string $value
816
     */
817 3
    public function setDiscriminatorValue($value)
818
    {
819 3
        $this->discriminatorMap[$value] = $this->name;
820 3
        $this->discriminatorValue = $value;
821 3
    }
822
823
    /**
824
     * Sets the slaveOkay option applied to collections for this class.
825
     *
826
     * @param boolean|null $slaveOkay
827
     *
828
     * @deprecated in version 1.2 and will be removed in 2.0.
829
     */
830 3
    public function setSlaveOkay($slaveOkay)
831
    {
832 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...
833 3
            sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.'),
834 3
            E_USER_DEPRECATED
835
        );
836 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...
837 3
    }
838
839
    /**
840
     * Add a index for this Document.
841
     *
842
     * @param array $keys Array of keys for the index.
843
     * @param array $options Array of options for the index.
844
     */
845 235
    public function addIndex($keys, array $options = array())
846
    {
847 235
        $this->indexes[] = array(
848 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...
849 235
                if ($value == 1 || $value == -1) {
850 63
                    return (int) $value;
851
                }
852 227
                if (is_string($value)) {
853 227
                    $lower = strtolower($value);
854 227
                    if ($lower === 'asc') {
855 220
                        return 1;
856 11
                    } elseif ($lower === 'desc') {
857 4
                        return -1;
858
                    }
859
                }
860 7
                return $value;
861 235
            }, $keys),
862 235
            'options' => $options
863
        );
864 235
    }
865
866
    /**
867
     * Set whether or not queries on this document should require indexes.
868
     *
869
     * @param bool $requireIndexes
870
     *
871
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
872
     */
873 913
    public function setRequireIndexes($requireIndexes)
874
    {
875 913
        @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...
876 913
            'requireIndexes was deprecated in version 1.2 and will be removed altogether in 2.0.',
877 913
            E_USER_DEPRECATED
878
        );
879 913
        $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...
880 913
    }
881
882
    /**
883
     * Returns the array of indexes for this Document.
884
     *
885
     * @return array $indexes The array of indexes.
886
     */
887 54
    public function getIndexes()
888
    {
889 54
        return $this->indexes;
890
    }
891
892
    /**
893
     * Checks whether this document has indexes or not.
894
     *
895
     * @return boolean
896
     */
897
    public function hasIndexes()
898
    {
899
        return $this->indexes ? true : false;
900
    }
901
902
    /**
903
     * Set shard key for this Document.
904
     *
905
     * @param array $keys Array of document keys.
906
     * @param array $options Array of sharding options.
907
     *
908
     * @throws MappingException
909
     */
910 87
    public function setShardKey(array $keys, array $options = array())
911
    {
912 87
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
913 2
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
914
        }
915
916 87
        if ($this->isEmbeddedDocument) {
917 2
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
918
        }
919
920 85
        foreach (array_keys($keys) as $field) {
921 85
            if (! isset($this->fieldMappings[$field])) {
922 78
                continue;
923
            }
924
925 7
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
926 3
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
927
            }
928
929 4
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
930 4
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
931
            }
932
        }
933
934 81
        $this->shardKey = array(
935 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...
936 81
                if ($value == 1 || $value == -1) {
937 6
                    return (int) $value;
938
                }
939 80
                if (is_string($value)) {
940 80
                    $lower = strtolower($value);
941 80
                    if ($lower === 'asc') {
942 79
                        return 1;
943 53
                    } elseif ($lower === 'desc') {
944
                        return -1;
945
                    }
946
                }
947 53
                return $value;
948 81
            }, $keys),
949 81
            'options' => $options
950
        );
951 81
    }
952
953
    /**
954
     * @return array
955
     */
956 28
    public function getShardKey()
957
    {
958 28
        return $this->shardKey;
959
    }
960
961
    /**
962
     * Checks whether this document has shard key or not.
963
     *
964
     * @return bool
965
     */
966 613
    public function isSharded()
967
    {
968 613
        return $this->shardKey ? true : false;
969
    }
970
971
    /**
972
     * Sets the write concern used by this class.
973
     *
974
     * @param string $writeConcern
975
     */
976 386
    public function setWriteConcern($writeConcern)
977
    {
978 386
        $this->writeConcern = $writeConcern;
979 386
    }
980
981
    /**
982
     * @return string
983
     */
984 12
    public function getWriteConcern()
985
    {
986 12
        return $this->writeConcern;
987
    }
988
989
    /**
990
     * Whether there is a write concern configured for this class.
991
     *
992
     * @return bool
993
     */
994 616
    public function hasWriteConcern()
995
    {
996 616
        return $this->writeConcern !== null;
997
    }
998
999
    /**
1000
     * Sets the change tracking policy used by this class.
1001
     *
1002
     * @param integer $policy
1003
     */
1004 377
    public function setChangeTrackingPolicy($policy)
1005
    {
1006 377
        $this->changeTrackingPolicy = $policy;
1007 377
    }
1008
1009
    /**
1010
     * Whether the change tracking policy of this class is "deferred explicit".
1011
     *
1012
     * @return boolean
1013
     */
1014 75
    public function isChangeTrackingDeferredExplicit()
1015
    {
1016 75
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
1017
    }
1018
1019
    /**
1020
     * Whether the change tracking policy of this class is "deferred implicit".
1021
     *
1022
     * @return boolean
1023
     */
1024 636
    public function isChangeTrackingDeferredImplicit()
1025
    {
1026 636
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
1027
    }
1028
1029
    /**
1030
     * Whether the change tracking policy of this class is "notify".
1031
     *
1032
     * @return boolean
1033
     */
1034 351
    public function isChangeTrackingNotify()
1035
    {
1036 351
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
1037
    }
1038
1039
    /**
1040
     * Gets the ReflectionProperties of the mapped class.
1041
     *
1042
     * @return array An array of ReflectionProperty instances.
1043
     */
1044 98
    public function getReflectionProperties()
1045
    {
1046 98
        return $this->reflFields;
1047
    }
1048
1049
    /**
1050
     * Gets a ReflectionProperty for a specific field of the mapped class.
1051
     *
1052
     * @param string $name
1053
     *
1054
     * @return \ReflectionProperty
1055
     */
1056
    public function getReflectionProperty($name)
1057
    {
1058
        return $this->reflFields[$name];
1059
    }
1060
1061
    /**
1062
     * {@inheritDoc}
1063
     */
1064 928
    public function getName()
1065
    {
1066 928
        return $this->name;
1067
    }
1068
1069
    /**
1070
     * The namespace this Document class belongs to.
1071
     *
1072
     * @return string $namespace The namespace name.
1073
     */
1074
    public function getNamespace()
1075
    {
1076
        return $this->namespace;
1077
    }
1078
1079
    /**
1080
     * Returns the database this Document is mapped to.
1081
     *
1082
     * @return string $db The database name.
1083
     */
1084 848
    public function getDatabase()
1085
    {
1086 848
        return $this->db;
1087
    }
1088
1089
    /**
1090
     * Set the database this Document is mapped to.
1091
     *
1092
     * @param string $db The database name
1093
     */
1094 104
    public function setDatabase($db)
1095
    {
1096 104
        $this->db = $db;
1097 104
    }
1098
1099
    /**
1100
     * Get the collection this Document is mapped to.
1101
     *
1102
     * @return string $collection The collection name.
1103
     */
1104 853
    public function getCollection()
1105
    {
1106 853
        return $this->collection;
1107
    }
1108
1109
    /**
1110
     * Sets the collection this Document is mapped to.
1111
     *
1112
     * @param array|string $name
1113
     *
1114
     * @throws \InvalidArgumentException
1115
     */
1116 958
    public function setCollection($name)
1117
    {
1118 958
        if (is_array($name)) {
1119
            if ( ! isset($name['name'])) {
1120
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
1121
            }
1122
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
1123
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
1124
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
1125
            $this->collection = $name['name'];
1126
        } else {
1127 958
            $this->collection = $name;
1128
        }
1129 958
    }
1130
1131
    /**
1132
     * Get whether or not the documents collection is capped.
1133
     *
1134
     * @return boolean
1135
     */
1136 4
    public function getCollectionCapped()
1137
    {
1138 4
        return $this->collectionCapped;
1139
    }
1140
1141
    /**
1142
     * Set whether or not the documents collection is capped.
1143
     *
1144
     * @param boolean $bool
1145
     */
1146 1
    public function setCollectionCapped($bool)
1147
    {
1148 1
        $this->collectionCapped = $bool;
1149 1
    }
1150
1151
    /**
1152
     * Get the collection size
1153
     *
1154
     * @return integer
1155
     */
1156 4
    public function getCollectionSize()
1157
    {
1158 4
        return $this->collectionSize;
1159
    }
1160
1161
    /**
1162
     * Set the collection size.
1163
     *
1164
     * @param integer $size
1165
     */
1166 1
    public function setCollectionSize($size)
1167
    {
1168 1
        $this->collectionSize = $size;
1169 1
    }
1170
1171
    /**
1172
     * Get the collection max.
1173
     *
1174
     * @return integer
1175
     */
1176 4
    public function getCollectionMax()
1177
    {
1178 4
        return $this->collectionMax;
1179
    }
1180
1181
    /**
1182
     * Set the collection max.
1183
     *
1184
     * @param integer $max
1185
     */
1186 1
    public function setCollectionMax($max)
1187
    {
1188 1
        $this->collectionMax = $max;
1189 1
    }
1190
1191
    /**
1192
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1193
     *
1194
     * @return boolean
1195
     */
1196
    public function isMappedToCollection()
1197
    {
1198
        return $this->collection ? true : false;
1199
    }
1200
1201
    /**
1202
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1203
     *
1204
     * @return boolean
1205
     */
1206 794
    public function isFile()
1207
    {
1208 794
        return $this->file ? true : false;
1209
    }
1210
1211
    /**
1212
     * Returns the file field name.
1213
     *
1214
     * @return string $file The file field name.
1215
     */
1216 372
    public function getFile()
1217
    {
1218 372
        return $this->file;
1219
    }
1220
1221
    /**
1222
     * Set the field name that stores the grid file.
1223
     *
1224
     * @param string $file
1225
     */
1226 373
    public function setFile($file)
1227
    {
1228 373
        $this->file = $file;
1229 373
    }
1230
1231
    /**
1232
     * Returns the distance field name.
1233
     *
1234
     * @return string $distance The distance field name.
1235
     */
1236
    public function getDistance()
1237
    {
1238
        return $this->distance;
1239
    }
1240
1241
    /**
1242
     * Set the field name that stores the distance.
1243
     *
1244
     * @param string $distance
1245
     */
1246 1
    public function setDistance($distance)
1247
    {
1248 1
        $this->distance = $distance;
1249 1
    }
1250
1251
    /**
1252
     * Map a field.
1253
     *
1254
     * @param array $mapping The mapping information.
1255
     *
1256
     * @return array
1257
     *
1258
     * @throws MappingException
1259
     */
1260 972
    public function mapField(array $mapping)
1261
    {
1262 972
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1263 10
            $mapping['fieldName'] = $mapping['name'];
1264
        }
1265 972
        if ( ! isset($mapping['fieldName'])) {
1266
            throw MappingException::missingFieldName($this->name);
1267
        }
1268 972
        if ( ! isset($mapping['name'])) {
1269 962
            $mapping['name'] = $mapping['fieldName'];
1270
        }
1271 972
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1272 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1273
        }
1274 971
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1275
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1276
        }
1277 971
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1278 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1279
        }
1280 970 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...
1281 625
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1282
        }
1283 970
        if (isset($mapping['collectionClass'])) {
1284 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...
1285 63
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1286
            }
1287 65
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1288
        }
1289 970
        if ( ! empty($mapping['collectionClass'])) {
1290 65
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1291 65
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1292 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1293
            }
1294
        }
1295
1296 969
        if (isset($mapping['discriminatorMap'])) {
1297 129
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1298 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...
1299 129
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1300
                }
1301
            }
1302
        }
1303
1304 969
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1305 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1306
        }
1307
1308 968
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1309
1310 968
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1311 653
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1312
        }
1313
1314 968
        if (isset($mapping['embedded'])) {
1315 610
            unset($mapping['cascade']);
1316 963
        } elseif (isset($mapping['cascade'])) {
1317 411
            $mapping['cascade'] = $cascades;
1318
        }
1319
1320 968
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1321 968
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1322 968
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1323 968
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1324 968
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1325
1326 968
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1327 63
            $mapping['file'] = true;
1328
        }
1329 968
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1330 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1331
        }
1332 968 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...
1333 63
            $this->file = $mapping['fieldName'];
1334 63
            $mapping['name'] = 'file';
1335
        }
1336 968 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...
1337 7
            $this->distance = $mapping['fieldName'];
1338
        }
1339 968
        if (isset($mapping['id']) && $mapping['id'] === true) {
1340 940
            $mapping['name'] = '_id';
1341 940
            $this->identifier = $mapping['fieldName'];
1342 940 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...
1343 921
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1344
            }
1345 940
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1346 940
            switch ($this->generatorType) {
1347 940
                case self::GENERATOR_TYPE_AUTO:
1348 866
                    $mapping['type'] = 'id';
1349 866
                    break;
1350
                default:
1351 157
                    if ( ! empty($this->generatorOptions['type'])) {
1352 56
                        $mapping['type'] = $this->generatorOptions['type'];
1353 101
                    } elseif (empty($mapping['type'])) {
1354 86
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1355
                    }
1356
            }
1357 940
            unset($this->generatorOptions['type']);
1358
        }
1359
1360 968
        if ( ! isset($mapping['nullable'])) {
1361 53
            $mapping['nullable'] = false;
1362
        }
1363
1364
        // Synchronize the "simple" and "storeAs" mapping information for backwards compatibility
1365 968
        if (isset($mapping['simple']) && ($mapping['simple'] === true || $mapping['simple'] === 'true')) {
1366 296
            $mapping['storeAs'] = ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1367 296
            @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...
1368
        }
1369
        // Provide the correct value for the "simple" field for backwards compatibility
1370 968
        if (isset($mapping['storeAs'])) {
1371 592
            $mapping['simple'] = $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1372
        }
1373
1374 968
        if (isset($mapping['reference'])
1375 968
            && isset($mapping['storeAs'])
1376 968
            && $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID
1377 968
            && ! isset($mapping['targetDocument'])
1378
        ) {
1379 3
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1380
        }
1381
1382 965
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1383 965
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1384 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1385
        }
1386
1387 961
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1388 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1389
        }
1390
1391 960 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...
1392 525
            $mapping['association'] = self::REFERENCE_ONE;
1393
        }
1394 960 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...
1395 463
            $mapping['association'] = self::REFERENCE_MANY;
1396
        }
1397 960 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...
1398 471
            $mapping['association'] = self::EMBED_ONE;
1399
        }
1400 960 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...
1401 508
            $mapping['association'] = self::EMBED_MANY;
1402
        }
1403
1404 960
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1405 133
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1406
        }
1407
1408
        /*
1409
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1410
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1411
        }
1412
        */
1413 960
        if (isset($mapping['version'])) {
1414 102
            $mapping['notSaved'] = true;
1415 102
            $this->setVersionMapping($mapping);
1416
        }
1417 960
        if (isset($mapping['lock'])) {
1418 27
            $mapping['notSaved'] = true;
1419 27
            $this->setLockMapping($mapping);
1420
        }
1421 960
        $mapping['isOwningSide'] = true;
1422 960
        $mapping['isInverseSide'] = false;
1423 960
        if (isset($mapping['reference'])) {
1424 597 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...
1425 92
                $mapping['isOwningSide'] = true;
1426 92
                $mapping['isInverseSide'] = false;
1427
            }
1428 597 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...
1429 298
                $mapping['isInverseSide'] = true;
1430 298
                $mapping['isOwningSide'] = false;
1431
            }
1432 597 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...
1433 67
                $mapping['isInverseSide'] = true;
1434 67
                $mapping['isOwningSide'] = false;
1435
            }
1436 597
            if (!isset($mapping['orphanRemoval'])) {
1437 572
                $mapping['orphanRemoval'] = false;
1438
            }
1439
        }
1440
1441 960
        if (!empty($mapping['prime']) && ($mapping['association'] !== self::REFERENCE_MANY || !$mapping['isInverseSide'])) {
1442
            throw MappingException::referencePrimersOnlySupportedForInverseReferenceMany($this->name, $mapping['fieldName']);
1443
        }
1444
1445 960
        $this->applyStorageStrategy($mapping);
1446
1447 959
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1448 959
        if (isset($mapping['association'])) {
1449 750
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1450
        }
1451
1452 959
        return $mapping;
1453
    }
1454
1455
    /**
1456
     * Validates the storage strategy of a mapping for consistency
1457
     * @param array $mapping
1458
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1459
     */
1460 960
    private function applyStorageStrategy(array &$mapping)
1461
    {
1462 960
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1463 942
            return;
1464
        }
1465
1466
        switch (true) {
1467 922
            case $mapping['type'] == 'int':
1468 921
            case $mapping['type'] == 'float':
1469 921
            case $mapping['type'] == 'increment':
1470 339
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1471 339
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1472 339
                break;
1473
1474 920
            case $mapping['type'] == 'many':
1475 621
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1476
                $allowedStrategies = [
1477 621
                    self::STORAGE_STRATEGY_PUSH_ALL,
1478 621
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1479 621
                    self::STORAGE_STRATEGY_SET,
1480 621
                    self::STORAGE_STRATEGY_SET_ARRAY,
1481 621
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1482 621
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1483
                ];
1484 621
                break;
1485
1486
            default:
1487 908
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1488 908
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1489
        }
1490
1491 922
        if (! isset($mapping['strategy'])) {
1492 911
            $mapping['strategy'] = $defaultStrategy;
1493
        }
1494
1495 922
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1496
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1497
        }
1498
1499 922
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1500 922
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1501 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1502
        }
1503 921
    }
1504
1505
    /**
1506
     * Map a MongoGridFSFile.
1507
     *
1508
     * @param array $mapping The mapping information.
1509
     */
1510
    public function mapFile(array $mapping)
1511
    {
1512
        $mapping['file'] = true;
1513
        $mapping['type'] = 'file';
1514
        $this->mapField($mapping);
1515
    }
1516
1517
    /**
1518
     * Map a single embedded document.
1519
     *
1520
     * @param array $mapping The mapping information.
1521
     */
1522 6
    public function mapOneEmbedded(array $mapping)
1523
    {
1524 6
        $mapping['embedded'] = true;
1525 6
        $mapping['type'] = 'one';
1526 6
        $this->mapField($mapping);
1527 5
    }
1528
1529
    /**
1530
     * Map a collection of embedded documents.
1531
     *
1532
     * @param array $mapping The mapping information.
1533
     */
1534 5
    public function mapManyEmbedded(array $mapping)
1535
    {
1536 5
        $mapping['embedded'] = true;
1537 5
        $mapping['type'] = 'many';
1538 5
        $this->mapField($mapping);
1539 5
    }
1540
1541
    /**
1542
     * Map a single document reference.
1543
     *
1544
     * @param array $mapping The mapping information.
1545
     */
1546 8
    public function mapOneReference(array $mapping)
1547
    {
1548 8
        $mapping['reference'] = true;
1549 8
        $mapping['type'] = 'one';
1550 8
        $this->mapField($mapping);
1551 8
    }
1552
1553
    /**
1554
     * Map a collection of document references.
1555
     *
1556
     * @param array $mapping The mapping information.
1557
     */
1558 8
    public function mapManyReference(array $mapping)
1559
    {
1560 8
        $mapping['reference'] = true;
1561 8
        $mapping['type'] = 'many';
1562 8
        $this->mapField($mapping);
1563 8
    }
1564
1565
    /**
1566
     * INTERNAL:
1567
     * Adds a field mapping without completing/validating it.
1568
     * This is mainly used to add inherited field mappings to derived classes.
1569
     *
1570
     * @param array $fieldMapping
1571
     */
1572 129
    public function addInheritedFieldMapping(array $fieldMapping)
1573
    {
1574 129
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1575
1576 129
        if (isset($fieldMapping['association'])) {
1577 77
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1578
        }
1579 129
    }
1580
1581
    /**
1582
     * INTERNAL:
1583
     * Adds an association mapping without completing/validating it.
1584
     * This is mainly used to add inherited association mappings to derived classes.
1585
     *
1586
     * @param array $mapping
1587
     *
1588
     * @return void
1589
     *
1590
     * @throws MappingException
1591
     */
1592 78
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1593
    {
1594 78
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1595 78
    }
1596
1597
    /**
1598
     * Checks whether the class has a mapped association with the given field name.
1599
     *
1600
     * @param string $fieldName
1601
     * @return boolean
1602
     */
1603 17
    public function hasReference($fieldName)
1604
    {
1605 17
        return isset($this->fieldMappings[$fieldName]['reference']);
1606
    }
1607
1608
    /**
1609
     * Checks whether the class has a mapped embed with the given field name.
1610
     *
1611
     * @param string $fieldName
1612
     * @return boolean
1613
     */
1614 5
    public function hasEmbed($fieldName)
1615
    {
1616 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1617
    }
1618
1619
    /**
1620
     * {@inheritDoc}
1621
     *
1622
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1623
     */
1624 7
    public function hasAssociation($fieldName)
1625
    {
1626 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1627
    }
1628
1629
    /**
1630
     * {@inheritDoc}
1631
     *
1632
     * Checks whether the class has a mapped reference or embed for the specified field and
1633
     * is a single valued association.
1634
     */
1635
    public function isSingleValuedAssociation($fieldName)
1636
    {
1637
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1638
    }
1639
1640
    /**
1641
     * {@inheritDoc}
1642
     *
1643
     * Checks whether the class has a mapped reference or embed for the specified field and
1644
     * is a collection valued association.
1645
     */
1646
    public function isCollectionValuedAssociation($fieldName)
1647
    {
1648
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1649
    }
1650
1651
    /**
1652
     * Checks whether the class has a mapped association for the specified field
1653
     * and if yes, checks whether it is a single-valued association (to-one).
1654
     *
1655
     * @param string $fieldName
1656
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1657
     */
1658
    public function isSingleValuedReference($fieldName)
1659
    {
1660
        return isset($this->fieldMappings[$fieldName]['association']) &&
1661
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1662
    }
1663
1664
    /**
1665
     * Checks whether the class has a mapped association for the specified field
1666
     * and if yes, checks whether it is a collection-valued association (to-many).
1667
     *
1668
     * @param string $fieldName
1669
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1670
     */
1671
    public function isCollectionValuedReference($fieldName)
1672
    {
1673
        return isset($this->fieldMappings[$fieldName]['association']) &&
1674
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1675
    }
1676
1677
    /**
1678
     * Checks whether the class has a mapped embedded document for the specified field
1679
     * and if yes, checks whether it is a single-valued association (to-one).
1680
     *
1681
     * @param string $fieldName
1682
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1683
     */
1684
    public function isSingleValuedEmbed($fieldName)
1685
    {
1686
        return isset($this->fieldMappings[$fieldName]['association']) &&
1687
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1688
    }
1689
1690
    /**
1691
     * Checks whether the class has a mapped embedded document for the specified field
1692
     * and if yes, checks whether it is a collection-valued association (to-many).
1693
     *
1694
     * @param string $fieldName
1695
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1696
     */
1697
    public function isCollectionValuedEmbed($fieldName)
1698
    {
1699
        return isset($this->fieldMappings[$fieldName]['association']) &&
1700
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1701
    }
1702
1703
    /**
1704
     * Sets the ID generator used to generate IDs for instances of this class.
1705
     *
1706
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1707
     */
1708 861
    public function setIdGenerator($generator)
1709
    {
1710 861
        $this->idGenerator = $generator;
1711 861
    }
1712
1713
    /**
1714
     * Casts the identifier to its portable PHP type.
1715
     *
1716
     * @param mixed $id
1717
     * @return mixed $id
1718
     */
1719 657
    public function getPHPIdentifierValue($id)
1720
    {
1721 657
        $idType = $this->fieldMappings[$this->identifier]['type'];
1722 657
        return Type::getType($idType)->convertToPHPValue($id);
1723
    }
1724
1725
    /**
1726
     * Casts the identifier to its database type.
1727
     *
1728
     * @param mixed $id
1729
     * @return mixed $id
1730
     */
1731 730
    public function getDatabaseIdentifierValue($id)
1732
    {
1733 730
        $idType = $this->fieldMappings[$this->identifier]['type'];
1734 730
        return Type::getType($idType)->convertToDatabaseValue($id);
1735
    }
1736
1737
    /**
1738
     * Sets the document identifier of a document.
1739
     *
1740
     * The value will be converted to a PHP type before being set.
1741
     *
1742
     * @param object $document
1743
     * @param mixed $id
1744
     */
1745 585
    public function setIdentifierValue($document, $id)
1746
    {
1747 585
        $id = $this->getPHPIdentifierValue($id);
1748 585
        $this->reflFields[$this->identifier]->setValue($document, $id);
1749 585
    }
1750
1751
    /**
1752
     * Gets the document identifier as a PHP type.
1753
     *
1754
     * @param object $document
1755
     * @return mixed $id
1756
     */
1757 675
    public function getIdentifierValue($document)
1758
    {
1759 675
        return $this->reflFields[$this->identifier]->getValue($document);
1760
    }
1761
1762
    /**
1763
     * {@inheritDoc}
1764
     *
1765
     * Since MongoDB only allows exactly one identifier field this is a proxy
1766
     * to {@see getIdentifierValue()} and returns an array with the identifier
1767
     * field as a key.
1768
     */
1769
    public function getIdentifierValues($object)
1770
    {
1771
        return array($this->identifier => $this->getIdentifierValue($object));
1772
    }
1773
1774
    /**
1775
     * Get the document identifier object as a database type.
1776
     *
1777
     * @param object $document
1778
     *
1779
     * @return \MongoId $id The MongoID object.
1780
     */
1781 36
    public function getIdentifierObject($document)
1782
    {
1783 36
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1784
    }
1785
1786
    /**
1787
     * Sets the specified field to the specified value on the given document.
1788
     *
1789
     * @param object $document
1790
     * @param string $field
1791
     * @param mixed $value
1792
     */
1793 11
    public function setFieldValue($document, $field, $value)
1794
    {
1795 11
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1796
            //property changes to an uninitialized proxy will not be tracked or persisted,
1797
            //so the proxy needs to be loaded first.
1798 1
            $document->__load();
1799
        }
1800
1801 11
        $this->reflFields[$field]->setValue($document, $value);
1802 11
    }
1803
1804
    /**
1805
     * Gets the specified field's value off the given document.
1806
     *
1807
     * @param object $document
1808
     * @param string $field
1809
     *
1810
     * @return mixed
1811
     */
1812 31
    public function getFieldValue($document, $field)
1813
    {
1814 31
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1815 1
            $document->__load();
1816
        }
1817
1818 31
        return $this->reflFields[$field]->getValue($document);
1819
    }
1820
1821
    /**
1822
     * Gets the mapping of a field.
1823
     *
1824
     * @param string $fieldName  The field name.
1825
     *
1826
     * @return array  The field mapping.
1827
     *
1828
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1829
     */
1830 201
    public function getFieldMapping($fieldName)
1831
    {
1832 201
        if ( ! isset($this->fieldMappings[$fieldName])) {
1833 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1834
        }
1835 199
        return $this->fieldMappings[$fieldName];
1836
    }
1837
1838
    /**
1839
     * Gets mappings of fields holding embedded document(s).
1840
     *
1841
     * @return array of field mappings
1842
     */
1843 625
    public function getEmbeddedFieldsMappings()
1844
    {
1845 625
        return array_filter(
1846 625
            $this->associationMappings,
1847
            function($assoc) { return ! empty($assoc['embedded']); }
1848
        );
1849
    }
1850
1851
    /**
1852
     * Gets the field mapping by its DB name.
1853
     * E.g. it returns identifier's mapping when called with _id.
1854
     *
1855
     * @param string $dbFieldName
1856
     *
1857
     * @return array
1858
     * @throws MappingException
1859
     */
1860 9
    public function getFieldMappingByDbFieldName($dbFieldName)
1861
    {
1862 9
        foreach ($this->fieldMappings as $mapping) {
1863 9
            if ($mapping['name'] == $dbFieldName) {
1864 9
                return $mapping;
1865
            }
1866
        }
1867
1868
        throw MappingException::mappingNotFoundByDbName($this->name, $dbFieldName);
1869
    }
1870
1871
    /**
1872
     * Check if the field is not null.
1873
     *
1874
     * @param string $fieldName  The field name
1875
     *
1876
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1877
     */
1878 1
    public function isNullable($fieldName)
1879
    {
1880 1
        $mapping = $this->getFieldMapping($fieldName);
1881 1
        if ($mapping !== false) {
1882 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1883
        }
1884
        return false;
1885
    }
1886
1887
    /**
1888
     * Checks whether the document has a discriminator field and value configured.
1889
     *
1890
     * @return boolean
1891
     */
1892 533
    public function hasDiscriminator()
1893
    {
1894 533
        return isset($this->discriminatorField, $this->discriminatorValue);
1895
    }
1896
1897
    /**
1898
     * Sets the type of Id generator to use for the mapped class.
1899
     *
1900
     * @param string $generatorType Generator type.
1901
     */
1902 378
    public function setIdGeneratorType($generatorType)
1903
    {
1904 378
        $this->generatorType = $generatorType;
1905 378
    }
1906
1907
    /**
1908
     * Sets the Id generator options.
1909
     *
1910
     * @param array $generatorOptions Generator options.
1911
     */
1912
    public function setIdGeneratorOptions($generatorOptions)
1913
    {
1914
        $this->generatorOptions = $generatorOptions;
1915
    }
1916
1917
    /**
1918
     * @return boolean
1919
     */
1920 634
    public function isInheritanceTypeNone()
1921
    {
1922 634
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1923
    }
1924
1925
    /**
1926
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1927
     *
1928
     * @return boolean
1929
     */
1930 371
    public function isInheritanceTypeSingleCollection()
1931
    {
1932 371
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1933
    }
1934
1935
    /**
1936
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1937
     *
1938
     * @return boolean
1939
     */
1940
    public function isInheritanceTypeCollectionPerClass()
1941
    {
1942
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1943
    }
1944
1945
    /**
1946
     * Sets the mapped subclasses of this class.
1947
     *
1948
     * @param string[] $subclasses The names of all mapped subclasses.
1949
     */
1950 2
    public function setSubclasses(array $subclasses)
1951
    {
1952 2
        foreach ($subclasses as $subclass) {
1953 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1954 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1955
            } else {
1956 2
                $this->subClasses[] = $subclass;
1957
            }
1958
        }
1959 2
    }
1960
1961
    /**
1962
     * Sets the parent class names.
1963
     * Assumes that the class names in the passed array are in the order:
1964
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1965
     *
1966
     * @param string[] $classNames
1967
     */
1968 919
    public function setParentClasses(array $classNames)
1969
    {
1970 919
        $this->parentClasses = $classNames;
1971
1972 919
        if (count($classNames) > 0) {
1973 113
            $this->rootDocumentName = array_pop($classNames);
1974
        }
1975 919
    }
1976
1977
    /**
1978
     * Checks whether the class will generate a new \MongoId instance for us.
1979
     *
1980
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1981
     */
1982
    public function isIdGeneratorAuto()
1983
    {
1984
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1985
    }
1986
1987
    /**
1988
     * Checks whether the class will use a collection to generate incremented identifiers.
1989
     *
1990
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1991
     */
1992
    public function isIdGeneratorIncrement()
1993
    {
1994
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1995
    }
1996
1997
    /**
1998
     * Checks whether the class will generate a uuid id.
1999
     *
2000
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
2001
     */
2002
    public function isIdGeneratorUuid()
2003
    {
2004
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
2005
    }
2006
2007
    /**
2008
     * Checks whether the class uses no id generator.
2009
     *
2010
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
2011
     */
2012
    public function isIdGeneratorNone()
2013
    {
2014
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
2015
    }
2016
2017
    /**
2018
     * Sets the version field mapping used for versioning. Sets the default
2019
     * value to use depending on the column type.
2020
     *
2021
     * @param array $mapping   The version field mapping array
2022
     *
2023
     * @throws LockException
2024
     */
2025 102
    public function setVersionMapping(array &$mapping)
2026
    {
2027 102
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
2028 1
            throw LockException::invalidVersionFieldType($mapping['type']);
2029
        }
2030
2031 101
        $this->isVersioned  = true;
2032 101
        $this->versionField = $mapping['fieldName'];
2033 101
    }
2034
2035
    /**
2036
     * Sets whether this class is to be versioned for optimistic locking.
2037
     *
2038
     * @param boolean $bool
2039
     */
2040 372
    public function setVersioned($bool)
2041
    {
2042 372
        $this->isVersioned = $bool;
2043 372
    }
2044
2045
    /**
2046
     * Sets the name of the field that is to be used for versioning if this class is
2047
     * versioned for optimistic locking.
2048
     *
2049
     * @param string $versionField
2050
     */
2051 372
    public function setVersionField($versionField)
2052
    {
2053 372
        $this->versionField = $versionField;
2054 372
    }
2055
2056
    /**
2057
     * Sets the version field mapping used for versioning. Sets the default
2058
     * value to use depending on the column type.
2059
     *
2060
     * @param array $mapping   The version field mapping array
2061
     *
2062
     * @throws \Doctrine\ODM\MongoDB\LockException
2063
     */
2064 27
    public function setLockMapping(array &$mapping)
2065
    {
2066 27
        if ($mapping['type'] !== 'int') {
2067 1
            throw LockException::invalidLockFieldType($mapping['type']);
2068
        }
2069
2070 26
        $this->isLockable = true;
2071 26
        $this->lockField = $mapping['fieldName'];
2072 26
    }
2073
2074
    /**
2075
     * Sets whether this class is to allow pessimistic locking.
2076
     *
2077
     * @param boolean $bool
2078
     */
2079
    public function setLockable($bool)
2080
    {
2081
        $this->isLockable = $bool;
2082
    }
2083
2084
    /**
2085
     * Sets the name of the field that is to be used for storing whether a document
2086
     * is currently locked or not.
2087
     *
2088
     * @param string $lockField
2089
     */
2090
    public function setLockField($lockField)
2091
    {
2092
        $this->lockField = $lockField;
2093
    }
2094
2095
    /**
2096
     * {@inheritDoc}
2097
     */
2098
    public function getFieldNames()
2099
    {
2100
        return array_keys($this->fieldMappings);
2101
    }
2102
2103
    /**
2104
     * {@inheritDoc}
2105
     */
2106
    public function getAssociationNames()
2107
    {
2108
        return array_keys($this->associationMappings);
2109
    }
2110
2111
    /**
2112
     * {@inheritDoc}
2113
     */
2114 22
    public function getTypeOfField($fieldName)
2115
    {
2116 22
        return isset($this->fieldMappings[$fieldName]) ?
2117 22
            $this->fieldMappings[$fieldName]['type'] : null;
2118
    }
2119
2120
    /**
2121
     * {@inheritDoc}
2122
     */
2123 6
    public function getAssociationTargetClass($assocName)
2124
    {
2125 6
        if ( ! isset($this->associationMappings[$assocName])) {
2126 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2127
        }
2128
2129 3
        return $this->associationMappings[$assocName]['targetDocument'];
2130
    }
2131
2132
    /**
2133
     * Retrieve the collectionClass associated with an association
2134
     *
2135
     * @param string $assocName
2136
     */
2137 2
    public function getAssociationCollectionClass($assocName)
2138
    {
2139 2
        if ( ! isset($this->associationMappings[$assocName])) {
2140
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
2141
        }
2142
2143 2
        if ( ! array_key_exists('collectionClass', $this->associationMappings[$assocName])) {
2144
            throw new InvalidArgumentException("collectionClass can only be applied to 'embedMany' and 'referenceMany' associations.");
2145
        }
2146
2147 2
        return $this->associationMappings[$assocName]['collectionClass'];
2148
    }
2149
2150
    /**
2151
     * {@inheritDoc}
2152
     */
2153
    public function isAssociationInverseSide($fieldName)
2154
    {
2155
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2156
    }
2157
2158
    /**
2159
     * {@inheritDoc}
2160
     */
2161
    public function getAssociationMappedByTargetField($fieldName)
2162
    {
2163
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
2164
    }
2165
}
2166