Completed
Pull Request — master (#1352)
by Andreas
12:19
created

ClassMetadataInfo::mapField()   F

Complexity

Conditions 85
Paths > 20000

Size

Total Lines 184
Code Lines 116

Duplication

Lines 34
Ratio 18.48 %

Code Coverage

Tests 144
CRAP Score 85.1422

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 34
loc 184
ccs 144
cts 148
cp 0.973
rs 2
cc 85
eloc 116
nc 2166847534
nop 1
crap 85.1422

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Mapping\ClassMetadata;
23
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
24
use Doctrine\ODM\MongoDB\LockException;
25
use Doctrine\ODM\MongoDB\Mapping\MappingException;
26
use Doctrine\ODM\MongoDB\Proxy\Proxy;
27
use Doctrine\ODM\MongoDB\Types\Type;
28
use InvalidArgumentException;
29
30
/**
31
 * A <tt>ClassMetadata</tt> instance holds all the object-document mapping metadata
32
 * of a document and it's references.
33
 *
34
 * Once populated, ClassMetadata instances are usually cached in a serialized form.
35
 *
36
 * <b>IMPORTANT NOTE:</b>
37
 *
38
 * The fields of this class are only public for 2 reasons:
39
 * 1) To allow fast READ access.
40
 * 2) To drastically reduce the size of a serialized instance (private/protected members
41
 *    get the whole class name, namespace inclusive, prepended to every property in
42
 *    the serialized representation).
43
 *
44
 * @since       1.0
45
 */
46
class ClassMetadataInfo implements \Doctrine\Common\Persistence\Mapping\ClassMetadata
47
{
48
    /* The Id generator types. */
49
    /**
50
     * AUTO means Doctrine will automatically create a new \MongoId instance for us.
51
     */
52
    const GENERATOR_TYPE_AUTO = 1;
53
54
    /**
55
     * INCREMENT means a separate collection is used for maintaining and incrementing id generation.
56
     * Offers full portability.
57
     */
58
    const GENERATOR_TYPE_INCREMENT = 2;
59
60
    /**
61
     * UUID means Doctrine will generate a uuid for us.
62
     */
63
    const GENERATOR_TYPE_UUID = 3;
64
65
    /**
66
     * ALNUM means Doctrine will generate Alpha-numeric string identifiers, using the INCREMENT
67
     * generator to ensure identifier uniqueness
68
     */
69
    const GENERATOR_TYPE_ALNUM = 4;
70
71
    /**
72
     * CUSTOM means Doctrine expect a class parameter. It will then try to initiate that class
73
     * and pass other options to the generator. It will throw an Exception if the class
74
     * does not exist or if an option was passed for that there is not setter in the new
75
     * generator class.
76
     *
77
     * The class  will have to be a subtype of AbstractIdGenerator.
78
     */
79
    const GENERATOR_TYPE_CUSTOM = 5;
80
81
    /**
82
     * NONE means Doctrine will not generate any id for us and you are responsible for manually
83
     * assigning an id.
84
     */
85
    const GENERATOR_TYPE_NONE = 6;
86
87
    /**
88
     * Default discriminator field name.
89
     *
90
     * This is used for associations value for associations where a that do not define a "targetDocument" or
91
     * "discriminatorField" option in their mapping.
92
     */
93
    const DEFAULT_DISCRIMINATOR_FIELD = '_doctrine_class_name';
94
95
    const REFERENCE_ONE = 1;
96
    const REFERENCE_MANY = 2;
97
    const EMBED_ONE = 3;
98
    const EMBED_MANY = 4;
99
    const MANY = 'many';
100
    const ONE = 'one';
101
102
    /* The inheritance mapping types */
103
    /**
104
     * NONE means the class does not participate in an inheritance hierarchy
105
     * and therefore does not need an inheritance mapping type.
106
     */
107
    const INHERITANCE_TYPE_NONE = 1;
108
109
    /**
110
     * SINGLE_COLLECTION means the class will be persisted according to the rules of
111
     * <tt>Single Collection Inheritance</tt>.
112
     */
113
    const INHERITANCE_TYPE_SINGLE_COLLECTION = 2;
114
115
    /**
116
     * COLLECTION_PER_CLASS means the class will be persisted according to the rules
117
     * of <tt>Concrete Collection Inheritance</tt>.
118
     */
119
    const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3;
120
121
    /**
122
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
123
     * by doing a property-by-property comparison with the original data. This will
124
     * be done for all entities that are in MANAGED state at commit-time.
125
     *
126
     * This is the default change tracking policy.
127
     */
128
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
129
130
    /**
131
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
132
     * by doing a property-by-property comparison with the original data. This will
133
     * be done only for entities that were explicitly saved (through persist() or a cascade).
134
     */
135
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
136
137
    /**
138
     * NOTIFY means that Doctrine relies on the entities sending out notifications
139
     * when their properties change. Such entity classes must implement
140
     * the <tt>NotifyPropertyChanged</tt> interface.
141
     */
142
    const CHANGETRACKING_NOTIFY = 3;
143
144
    /**
145
     * SET means that fields will be written to the database using a $set operator
146
     */
147
    const STORAGE_STRATEGY_SET = 'set';
148
149
    /**
150
     * INCREMENT means that fields will be written to the database by calculating
151
     * the difference and using the $inc operator
152
     */
153
    const STORAGE_STRATEGY_INCREMENT = 'increment';
154
155
    const STORAGE_STRATEGY_PUSH_ALL = 'pushAll';
156
    const STORAGE_STRATEGY_ADD_TO_SET = 'addToSet';
157
    const STORAGE_STRATEGY_ATOMIC_SET = 'atomicSet';
158
    const STORAGE_STRATEGY_ATOMIC_SET_ARRAY = 'atomicSetArray';
159
    const STORAGE_STRATEGY_SET_ARRAY = 'setArray';
160
161
    /**
162
     * READ-ONLY: The name of the mongo database the document is mapped to.
163
     */
164
    public $db;
165
166
    /**
167
     * READ-ONLY: The name of the mongo collection the document is mapped to.
168
     */
169
    public $collection;
170
171
    /**
172
     * READ-ONLY: If the collection should be a fixed size.
173
     */
174
    public $collectionCapped;
175
176
    /**
177
     * READ-ONLY: If the collection is fixed size, its size in bytes.
178
     */
179
    public $collectionSize;
180
181
    /**
182
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
183
     */
184
    public $collectionMax;
185
186
    /**
187
     * READ-ONLY: The field name of the document identifier.
188
     */
189
    public $identifier;
190
191
    /**
192
     * READ-ONLY: The field that stores a file reference and indicates the
193
     * document is a file and should be stored on the MongoGridFS.
194
     */
195
    public $file;
196
197
    /**
198
     * READ-ONLY: The field that stores the calculated distance when performing geo spatial
199
     * queries.
200
     */
201
    public $distance;
202
203
    /**
204
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
205
     */
206
    public $slaveOkay;
207
208
    /**
209
     * READ-ONLY: The array of indexes for the document collection.
210
     */
211
    public $indexes = array();
212
213
    /**
214
     * READ-ONLY: Whether or not queries on this document should require indexes.
215
     */
216
    public $requireIndexes = false;
217
218
    /**
219
     * READ-ONLY: The name of the document class.
220
     */
221
    public $name;
222
223
    /**
224
     * READ-ONLY: The namespace the document class is contained in.
225
     *
226
     * @var string
227
     * @todo Not really needed. Usage could be localized.
228
     */
229
    public $namespace;
230
231
    /**
232
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
233
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
234
     * as {@link $documentName}.
235
     *
236
     * @var string
237
     */
238
    public $rootDocumentName;
239
240
    /**
241
     * The name of the custom repository class used for the document class.
242
     * (Optional).
243
     *
244
     * @var string
245
     */
246
    public $customRepositoryClassName;
247
248
    /**
249
     * READ-ONLY: The names of the parent classes (ancestors).
250
     *
251
     * @var array
252
     */
253
    public $parentClasses = array();
254
255
    /**
256
     * READ-ONLY: The names of all subclasses (descendants).
257
     *
258
     * @var array
259
     */
260
    public $subClasses = array();
261
262
    /**
263
     * The ReflectionProperty instances of the mapped class.
264
     *
265
     * @var \ReflectionProperty[]
266
     */
267
    public $reflFields = array();
268
269
    /**
270
     * READ-ONLY: The inheritance mapping type used by the class.
271
     *
272
     * @var integer
273
     */
274
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
275
276
    /**
277
     * READ-ONLY: The Id generator type used by the class.
278
     *
279
     * @var string
280
     */
281
    public $generatorType = self::GENERATOR_TYPE_AUTO;
282
283
    /**
284
     * READ-ONLY: The Id generator options.
285
     *
286
     * @var array
287
     */
288
    public $generatorOptions = array();
289
290
    /**
291
     * READ-ONLY: The ID generator used for generating IDs for this class.
292
     *
293
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
294
     */
295
    public $idGenerator;
296
297
    /**
298
     * READ-ONLY: The field mappings of the class.
299
     * Keys are field names and values are mapping definitions.
300
     *
301
     * The mapping definition array has the following values:
302
     *
303
     * - <b>fieldName</b> (string)
304
     * The name of the field in the Document.
305
     *
306
     * - <b>id</b> (boolean, optional)
307
     * Marks the field as the primary key of the document. Multiple fields of an
308
     * document can have the id attribute, forming a composite key.
309
     *
310
     * @var array
311
     */
312
    public $fieldMappings = array();
313
314
    /**
315
     * READ-ONLY: The association mappings of the class.
316
     * Keys are field names and values are mapping definitions.
317
     *
318
     * @var array
319
     */
320
    public $associationMappings = array();
321
322
    /**
323
     * READ-ONLY: Array of fields to also load with a given method.
324
     *
325
     * @var array
326
     */
327
    public $alsoLoadMethods = array();
328
329
    /**
330
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
331
     *
332
     * @var array
333
     */
334
    public $lifecycleCallbacks = array();
335
336
    /**
337
     * READ-ONLY: The discriminator value of this class.
338
     *
339
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
340
     * where a discriminator field is used.</b>
341
     *
342
     * @var mixed
343
     * @see discriminatorField
344
     */
345
    public $discriminatorValue;
346
347
    /**
348
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
349
     *
350
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
351
     * where a discriminator field is used.</b>
352
     *
353
     * @var mixed
354
     * @see discriminatorField
355
     */
356
    public $discriminatorMap = array();
357
358
    /**
359
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
360
     * inheritance mapping.
361
     *
362
     * @var string
363
     */
364
    public $discriminatorField;
365
366
    /**
367
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
368
     *
369
     * @var string
370
     * @see discriminatorField
371
     */
372
    public $defaultDiscriminatorValue;
373
374
    /**
375
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
376
     *
377
     * @var boolean
378
     */
379
    public $isMappedSuperclass = false;
380
381
    /**
382
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
383
     *
384
     * @var boolean
385
     */
386
    public $isEmbeddedDocument = false;
387
388
    /**
389
     * READ-ONLY: The policy used for change-tracking on entities of this class.
390
     *
391
     * @var integer
392
     */
393
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
394
395
    /**
396
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
397
     * with optimistic locking.
398
     *
399
     * @var boolean $isVersioned
400
     */
401
    public $isVersioned;
402
403
    /**
404
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
405
     *
406
     * @var mixed $versionField
407
     */
408
    public $versionField;
409
410
    /**
411
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
412
     * locking.
413
     *
414
     * @var boolean $isLockable
415
     */
416
    public $isLockable;
417
418
    /**
419
     * READ-ONLY: The name of the field which is used for locking a document.
420
     *
421
     * @var mixed $lockField
422
     */
423
    public $lockField;
424
425
    /**
426
     * The ReflectionClass instance of the mapped class.
427
     *
428
     * @var \ReflectionClass
429
     */
430
    public $reflClass;
431
432
    /**
433
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
434
     * metadata of the class with the given name.
435
     *
436
     * @param string $documentName The name of the document class the new instance is used for.
437
     */
438 852
    public function __construct($documentName)
439
    {
440 852
        $this->name = $documentName;
441 852
        $this->rootDocumentName = $documentName;
442 852
    }
443
444
    /**
445
     * {@inheritDoc}
446
     */
447 803
    public function getReflectionClass()
448
    {
449 803
        if ( ! $this->reflClass) {
450 2
            $this->reflClass = new \ReflectionClass($this->name);
451 2
        }
452
453 803
        return $this->reflClass;
454
    }
455
456
    /**
457
     * {@inheritDoc}
458
     */
459 292
    public function isIdentifier($fieldName)
460
    {
461 292
        return $this->identifier === $fieldName;
462
    }
463
464
    /**
465
     * INTERNAL:
466
     * Sets the mapped identifier field of this class.
467
     *
468
     * @param string $identifier
469
     */
470 331
    public function setIdentifier($identifier)
471
    {
472 331
        $this->identifier = $identifier;
473 331
    }
474
475
    /**
476
     * {@inheritDoc}
477
     *
478
     * Since MongoDB only allows exactly one identifier field
479
     * this will always return an array with only one value
480
     */
481 25
    public function getIdentifier()
482
    {
483 25
        return array($this->identifier);
484
    }
485
486
    /**
487
     * {@inheritDoc}
488
     *
489
     * Since MongoDB only allows exactly one identifier field
490
     * this will always return an array with only one value
491
     */
492 89
    public function getIdentifierFieldNames()
493
    {
494 89
        return array($this->identifier);
495
    }
496
497
    /**
498
     * {@inheritDoc}
499
     */
500 511
    public function hasField($fieldName)
501
    {
502 511
        return isset($this->fieldMappings[$fieldName]);
503
    }
504
505
    /**
506
     * Sets the inheritance type used by the class and it's subclasses.
507
     *
508
     * @param integer $type
509
     */
510 342
    public function setInheritanceType($type)
511
    {
512 342
        $this->inheritanceType = $type;
513 342
    }
514
515
    /**
516
     * Checks whether a mapped field is inherited from an entity superclass.
517
     *
518
     * @param  string $fieldName
519
     *
520
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
521
     */
522 803
    public function isInheritedField($fieldName)
523
    {
524 803
        return isset($this->fieldMappings[$fieldName]['inherited']);
525
    }
526
527
    /**
528
     * Registers a custom repository class for the document class.
529
     *
530
     * @param string $repositoryClassName The class name of the custom repository.
531
     */
532 286 View Code Duplication
    public function setCustomRepositoryClass($repositoryClassName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
533
    {
534 286
        if ($this->isEmbeddedDocument) {
535
            return;
536
        }
537
        
538 286
        if ($repositoryClassName && strpos($repositoryClassName, '\\') === false && strlen($this->namespace)) {
539 3
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
540 3
        }
541
542 286
        $this->customRepositoryClassName = $repositoryClassName;
543 286
    }
544
545
    /**
546
     * Dispatches the lifecycle event of the given document by invoking all
547
     * registered callbacks.
548
     *
549
     * @param string $event     Lifecycle event
550
     * @param object $document  Document on which the event occurred
551
     * @param array  $arguments Arguments to pass to all callbacks
552
     * @throws \InvalidArgumentException if document class is not this class or
553
     *                                   a Proxy of this class
554
     */
555 588
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
556
    {
557 588
        if ( ! $document instanceof $this->name) {
558 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
559
        }
560
561 587
        if (empty($this->lifecycleCallbacks[$event])) {
562 574
            return;
563
        }
564
565 177
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
566 177
            if ($arguments !== null) {
567 176
                call_user_func_array(array($document, $callback), $arguments);
568 176
            } else {
569 2
                $document->$callback();
570
            }
571 177
        }
572 177
    }
573
574
    /**
575
     * Checks whether the class has callbacks registered for a lifecycle event.
576
     *
577
     * @param string $event Lifecycle event
578
     *
579
     * @return boolean
580
     */
581
    public function hasLifecycleCallbacks($event)
582
    {
583
        return ! empty($this->lifecycleCallbacks[$event]);
584
    }
585
586
    /**
587
     * Gets the registered lifecycle callbacks for an event.
588
     *
589
     * @param string $event
590
     * @return array
591
     */
592
    public function getLifecycleCallbacks($event)
593
    {
594
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
595
    }
596
597
    /**
598
     * Adds a lifecycle callback for documents of this class.
599
     *
600
     * If the callback is already registered, this is a NOOP.
601
     *
602
     * @param string $callback
603
     * @param string $event
604
     */
605 267
    public function addLifecycleCallback($callback, $event)
606
    {
607 267
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
608 1
            return;
609
        }
610
611 267
        $this->lifecycleCallbacks[$event][] = $callback;
612 267
    }
613
614
    /**
615
     * Sets the lifecycle callbacks for documents of this class.
616
     *
617
     * Any previously registered callbacks are overwritten.
618
     *
619
     * @param array $callbacks
620
     */
621 330
    public function setLifecycleCallbacks(array $callbacks)
622
    {
623 330
        $this->lifecycleCallbacks = $callbacks;
624 330
    }
625
626
    /**
627
     * Registers a method for loading document data before field hydration.
628
     *
629
     * Note: A method may be registered multiple times for different fields.
630
     * it will be invoked only once for the first field found.
631
     *
632
     * @param string       $method Method name
633
     * @param array|string $fields Database field name(s)
634
     */
635 15
    public function registerAlsoLoadMethod($method, $fields)
636
    {
637 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
638 15
    }
639
640
    /**
641
     * Sets the AlsoLoad methods for documents of this class.
642
     *
643
     * Any previously registered methods are overwritten.
644
     *
645
     * @param array $methods
646
     */
647 330
    public function setAlsoLoadMethods(array $methods)
648
    {
649 330
        $this->alsoLoadMethods = $methods;
650 330
    }
651
652
    /**
653
     * Sets the discriminator field.
654
     *
655
     * The field name is the the unmapped database field. Discriminator values
656
     * are only used to discern the hydration class and are not mapped to class
657
     * properties.
658
     *
659
     * @param string $discriminatorField
660
     *
661
     * @throws MappingException If the discriminator field conflicts with the
662
     *                          "name" attribute of a mapped field.
663
     */
664 351
    public function setDiscriminatorField($discriminatorField)
665
    {
666 351
        if ($discriminatorField === null) {
667 291
            $this->discriminatorField = null;
668
669 291
            return;
670
        }
671
672
        // Handle array argument with name/fieldName keys for BC
673 115
        if (is_array($discriminatorField)) {
674
            if (isset($discriminatorField['name'])) {
675
                $discriminatorField = $discriminatorField['name'];
676
            } elseif (isset($discriminatorField['fieldName'])) {
677
                $discriminatorField = $discriminatorField['fieldName'];
678
            }
679
        }
680
681 115
        foreach ($this->fieldMappings as $fieldMapping) {
682 4
            if ($discriminatorField == $fieldMapping['name']) {
683 1
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
684
            }
685 114
        }
686
687 114
        $this->discriminatorField = $discriminatorField;
688 114
    }
689
690
    /**
691
     * Sets the discriminator values used by this class.
692
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
693
     *
694
     * @param array $map
695
     *
696
     * @throws MappingException
697
     */
698 347
    public function setDiscriminatorMap(array $map)
699
    {
700 347
        foreach ($map as $value => $className) {
701 113
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
702 81
                $className = $this->namespace . '\\' . $className;
703 81
            }
704 113
            $this->discriminatorMap[$value] = $className;
705 113
            if ($this->name == $className) {
706 105
                $this->discriminatorValue = $value;
707 105
            } else {
708 105
                if ( ! class_exists($className)) {
709
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
710
                }
711 105
                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...
712 91
                    $this->subClasses[] = $className;
713 91
                }
714
            }
715 347
        }
716 347
    }
717
718
    /**
719
     * Sets the default discriminator value to be used for this class
720
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
721
     *
722
     * @param string $defaultDiscriminatorValue
723
     *
724
     * @throws MappingException
725
     */
726 336
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
727
    {
728 336
        if ($defaultDiscriminatorValue === null) {
729 330
            $this->defaultDiscriminatorValue = null;
730
731 330
            return;
732
        }
733
734 53
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
735
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
736
        }
737
738 53
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
739 53
    }
740
741
    /**
742
     * Sets the discriminator value for this class.
743
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
744
     * collection.
745
     *
746
     * @param string $value
747
     */
748
    public function setDiscriminatorValue($value)
749
    {
750
        $this->discriminatorMap[$value] = $this->name;
751
        $this->discriminatorValue = $value;
752
    }
753
754
    /**
755
     * Sets the slaveOkay option applied to collections for this class.
756
     *
757
     * @param boolean|null $slaveOkay
758
     */
759 3
    public function setSlaveOkay($slaveOkay)
760
    {
761 3
        $this->slaveOkay = $slaveOkay === null ? null : (boolean) $slaveOkay;
762 3
    }
763
764
    /**
765
     * Add a index for this Document.
766
     *
767
     * @param array $keys Array of keys for the index.
768
     * @param array $options Array of options for the index.
769
     */
770 219
    public function addIndex($keys, array $options = array())
771
    {
772 219
        $this->indexes[] = array(
773
            'keys' => array_map(function($value) {
774 219
                if ($value == 1 || $value == -1) {
775 55
                    return (int) $value;
776
                }
777 211
                if (is_string($value)) {
778 211
                    $lower = strtolower($value);
779 211
                    if ($lower === 'asc') {
780 204
                        return 1;
781 10
                    } elseif ($lower === 'desc') {
782 3
                        return -1;
783
                    }
784 7
                }
785 7
                return $value;
786 219
            }, $keys),
787
            'options' => $options
788 219
        );
789 219
    }
790
791
    /**
792
     * Set whether or not queries on this document should require indexes.
793
     *
794
     * @param bool $requireIndexes
795
     */
796 795
    public function setRequireIndexes($requireIndexes)
797
    {
798 795
        $this->requireIndexes = $requireIndexes;
799 795
    }
800
801
    /**
802
     * Returns the array of indexes for this Document.
803
     *
804
     * @return array $indexes The array of indexes.
805
     */
806 53
    public function getIndexes()
807
    {
808 53
        return $this->indexes;
809
    }
810
811
    /**
812
     * Checks whether this document has indexes or not.
813
     *
814
     * @return boolean
815
     */
816
    public function hasIndexes()
817
    {
818
        return $this->indexes ? true : false;
819
    }
820
821
    /**
822
     * Sets the change tracking policy used by this class.
823
     *
824
     * @param integer $policy
825
     */
826 334
    public function setChangeTrackingPolicy($policy)
827
    {
828 334
        $this->changeTrackingPolicy = $policy;
829 334
    }
830
831
    /**
832
     * Whether the change tracking policy of this class is "deferred explicit".
833
     *
834
     * @return boolean
835
     */
836 57
    public function isChangeTrackingDeferredExplicit()
837
    {
838 57
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
839
    }
840
841
    /**
842
     * Whether the change tracking policy of this class is "deferred implicit".
843
     *
844
     * @return boolean
845
     */
846 563
    public function isChangeTrackingDeferredImplicit()
847
    {
848 563
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
849
    }
850
851
    /**
852
     * Whether the change tracking policy of this class is "notify".
853
     *
854
     * @return boolean
855
     */
856 327
    public function isChangeTrackingNotify()
857
    {
858 327
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
859
    }
860
861
    /**
862
     * Gets the ReflectionProperties of the mapped class.
863
     *
864
     * @return array An array of ReflectionProperty instances.
865
     */
866 89
    public function getReflectionProperties()
867
    {
868 89
        return $this->reflFields;
869
    }
870
871
    /**
872
     * Gets a ReflectionProperty for a specific field of the mapped class.
873
     *
874
     * @param string $name
875
     *
876
     * @return \ReflectionProperty
877
     */
878
    public function getReflectionProperty($name)
879
    {
880
        return $this->reflFields[$name];
881
    }
882
883
    /**
884
     * {@inheritDoc}
885
     */
886 800
    public function getName()
887
    {
888 800
        return $this->name;
889
    }
890
891
    /**
892
     * The namespace this Document class belongs to.
893
     *
894
     * @return string $namespace The namespace name.
895
     */
896
    public function getNamespace()
897
    {
898
        return $this->namespace;
899
    }
900
901
    /**
902
     * Returns the database this Document is mapped to.
903
     *
904
     * @return string $db The database name.
905
     */
906 741
    public function getDatabase()
907
    {
908 741
        return $this->db;
909
    }
910
911
    /**
912
     * Set the database this Document is mapped to.
913
     *
914
     * @param string $db The database name
915
     */
916 93
    public function setDatabase($db)
917
    {
918 93
        $this->db = $db;
919 93
    }
920
921
    /**
922
     * Get the collection this Document is mapped to.
923
     *
924
     * @return string $collection The collection name.
925
     */
926 745
    public function getCollection()
927
    {
928 745
        return $this->collection;
929
    }
930
931
    /**
932
     * Sets the collection this Document is mapped to.
933
     *
934
     * @param array|string $name
935
     *
936
     * @throws \InvalidArgumentException
937
     */
938 833
    public function setCollection($name)
939
    {
940 833
        if (is_array($name)) {
941
            if ( ! isset($name['name'])) {
942
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
943
            }
944
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
945
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
946
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
947
            $this->collection = $name['name'];
948
        } else {
949 833
            $this->collection = $name;
950
        }
951 833
    }
952
953
    /**
954
     * Get whether or not the documents collection is capped.
955
     *
956
     * @return boolean
957
     */
958 4
    public function getCollectionCapped()
959
    {
960 4
        return $this->collectionCapped;
961
    }
962
963
    /**
964
     * Set whether or not the documents collection is capped.
965
     *
966
     * @param boolean $bool
967
     */
968 1
    public function setCollectionCapped($bool)
969
    {
970 1
        $this->collectionCapped = $bool;
971 1
    }
972
973
    /**
974
     * Get the collection size
975
     *
976
     * @return integer
977
     */
978 4
    public function getCollectionSize()
979
    {
980 4
        return $this->collectionSize;
981
    }
982
983
    /**
984
     * Set the collection size.
985
     *
986
     * @param integer $size
987
     */
988 1
    public function setCollectionSize($size)
989
    {
990 1
        $this->collectionSize = $size;
991 1
    }
992
993
    /**
994
     * Get the collection max.
995
     *
996
     * @return integer
997
     */
998 4
    public function getCollectionMax()
999
    {
1000 4
        return $this->collectionMax;
1001
    }
1002
1003
    /**
1004
     * Set the collection max.
1005
     *
1006
     * @param integer $max
1007
     */
1008 1
    public function setCollectionMax($max)
1009
    {
1010 1
        $this->collectionMax = $max;
1011 1
    }
1012
1013
    /**
1014
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1015
     *
1016
     * @return boolean
1017
     */
1018
    public function isMappedToCollection()
1019
    {
1020
        return $this->collection ? true : false;
1021
    }
1022
1023
    /**
1024
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1025
     *
1026
     * @return boolean
1027
     */
1028 694
    public function isFile()
1029
    {
1030 694
        return $this->file ? true : false;
1031
    }
1032
1033
    /**
1034
     * Returns the file field name.
1035
     *
1036
     * @return string $file The file field name.
1037
     */
1038 330
    public function getFile()
1039
    {
1040 330
        return $this->file;
1041
    }
1042
1043
    /**
1044
     * Set the field name that stores the grid file.
1045
     *
1046
     * @param string $file
1047
     */
1048 331
    public function setFile($file)
1049
    {
1050 331
        $this->file = $file;
1051 331
    }
1052
1053
    /**
1054
     * Returns the distance field name.
1055
     *
1056
     * @return string $distance The distance field name.
1057
     */
1058
    public function getDistance()
1059
    {
1060
        return $this->distance;
1061
    }
1062
1063
    /**
1064
     * Set the field name that stores the distance.
1065
     *
1066
     * @param string $distance
1067
     */
1068 1
    public function setDistance($distance)
1069
    {
1070 1
        $this->distance = $distance;
1071 1
    }
1072
1073
    /**
1074
     * Map a field.
1075
     *
1076
     * @param array $mapping The mapping information.
1077
     *
1078
     * @return array
1079
     *
1080
     * @throws MappingException
1081
     */
1082 838
    public function mapField(array $mapping)
1083
    {
1084 838
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1085 8
            $mapping['fieldName'] = $mapping['name'];
1086 8
        }
1087 838
        if ( ! isset($mapping['fieldName'])) {
1088
            throw MappingException::missingFieldName($this->name);
1089
        }
1090 838
        if ( ! isset($mapping['name'])) {
1091 830
            $mapping['name'] = $mapping['fieldName'];
1092 830
        }
1093 838
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1094 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1095
        }
1096 837
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1097
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1098 53
        }
1099 837
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1100 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1101
        }
1102 836
        if (isset($mapping['targetDocument']) && strpos($mapping['targetDocument'], '\\') === false && strlen($this->namespace)) {
1103 533
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1104 533
        }
1105
1106 836
        if (isset($mapping['discriminatorMap'])) {
1107 103
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1108 103
                if (strpos($class, '\\') === false && strlen($this->namespace)) {
1109 68
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1110 68
                }
1111 103
            }
1112 103
        }
1113
1114 836
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1115 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1116
        }
1117
1118 835
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1119
1120 835
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1121 559
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1122 559
        }
1123
1124 835
        if (isset($mapping['embedded'])) {
1125 525
            unset($mapping['cascade']);
1126 835
        } elseif (isset($mapping['cascade'])) {
1127 365
            $mapping['cascade'] = $cascades;
1128 365
        }
1129
1130 835
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1131 835
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1132 835
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1133 835
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1134 835
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1135
        
1136 835
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1137 56
            $mapping['file'] = true;
1138 56
        }
1139 835
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1140
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1141
        }
1142 835 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...
1143 56
            $this->file = $mapping['fieldName'];
1144 56
            $mapping['name'] = 'file';
1145 56
        }
1146 835 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...
1147 7
            $this->distance = $mapping['fieldName'];
1148 7
        }
1149 835
        if (isset($mapping['id']) && $mapping['id'] === true) {
1150 816
            $mapping['name'] = '_id';
1151 816
            $this->identifier = $mapping['fieldName'];
1152 816 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...
1153 801
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1154 801
            }
1155 816
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1156 816
            switch ($this->generatorType) {
1157 816
                case self::GENERATOR_TYPE_AUTO:
1158 749
                    $mapping['type'] = 'id';
1159 749
                    break;
1160 149
                default:
1161 149
                    if ( ! empty($this->generatorOptions['type'])) {
1162 52
                        $mapping['type'] = $this->generatorOptions['type'];
1163 149
                    } elseif (empty($mapping['type'])) {
1164 74
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1165 74
                    }
1166 816
            }
1167 816
            unset($this->generatorOptions['type']);
1168 835
        } elseif (!isset($mapping['embedded']) && !isset($mapping['reference']) && isset($mapping['type'])) {
1169 763
            switch ($mapping['type']) {
1170 763
                case 'int':
1171 763
                case 'float':
1172 298
                    $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1173 298
                    break;
1174 762
                default:
1175 762
                    $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1176 763
            }
1177
1178 763
            if (isset($mapping['strategy'])) {
1179 224
                if (! in_array($mapping['strategy'], $allowedStrategies)) {
1180
                    throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1181
                }
1182 224
            } else {
1183 763
                $mapping['strategy'] = self::STORAGE_STRATEGY_SET;
1184
            }
1185 763
        }
1186
1187 835
        if ( ! isset($mapping['nullable'])) {
1188 39
            $mapping['nullable'] = false;
1189 39
        }
1190
1191 835
        if (isset($mapping['reference']) && ! empty($mapping['simple']) && ! isset($mapping['targetDocument'])) {
1192 1
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1193
        }
1194
1195 834
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1196 834
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1197 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1198
        }
1199
        
1200 830
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1201 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1202
        }
1203
1204 829 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...
1205 454
            $mapping['association'] = self::REFERENCE_ONE;
1206 454
        }
1207 829 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...
1208 396
            $mapping['association'] = self::REFERENCE_MANY;
1209 396
        }
1210 829 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...
1211 405
            $mapping['association'] = self::EMBED_ONE;
1212 405
        }
1213 829 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...
1214 456
            $mapping['association'] = self::EMBED_MANY;
1215 456
        }
1216
1217 829
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1218 105
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1219 105
        }
1220
1221
        /*
1222
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1223
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1224
        }
1225
        */
1226 829
        if (isset($mapping['version'])) {
1227 93
            $mapping['notSaved'] = true;
1228 93
            $this->setVersionMapping($mapping);
1229 92
        }
1230 829
        if (isset($mapping['lock'])) {
1231 26
            $mapping['notSaved'] = true;
1232 26
            $this->setLockMapping($mapping);
1233 25
        }
1234 829
        $mapping['isOwningSide'] = true;
1235 829
        $mapping['isInverseSide'] = false;
1236 829
        if (isset($mapping['reference'])) {
1237 510 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...
1238 214
                $mapping['isOwningSide'] = true;
1239 214
                $mapping['isInverseSide'] = false;
1240 214
            }
1241 510 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...
1242 253
                $mapping['isInverseSide'] = true;
1243 253
                $mapping['isOwningSide'] = false;
1244 253
            }
1245 510 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...
1246 194
                $mapping['isInverseSide'] = true;
1247 194
                $mapping['isOwningSide'] = false;
1248 194
            }
1249 510
            if (!isset($mapping['orphanRemoval'])) {
1250 490
                $mapping['orphanRemoval'] = false;
1251 490
            }
1252 510
        }
1253
1254 829
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1255 829
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1256 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1257
        }
1258
1259 828
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1260 828
        if (isset($mapping['association'])) {
1261 649
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1262 649
        }
1263
1264 828
        return $mapping;
1265
    }
1266
1267
    /**
1268
     * Map a MongoGridFSFile.
1269
     *
1270
     * @param array $mapping The mapping information.
1271
     */
1272
    public function mapFile(array $mapping)
1273
    {
1274
        $mapping['file'] = true;
1275
        $mapping['type'] = 'file';
1276
        $this->mapField($mapping);
1277
    }
1278
1279
    /**
1280
     * Map a single embedded document.
1281
     *
1282
     * @param array $mapping The mapping information.
1283
     */
1284 6
    public function mapOneEmbedded(array $mapping)
1285
    {
1286 6
        $mapping['embedded'] = true;
1287 6
        $mapping['type'] = 'one';
1288 6
        $this->mapField($mapping);
1289 5
    }
1290
1291
    /**
1292
     * Map a collection of embedded documents.
1293
     *
1294
     * @param array $mapping The mapping information.
1295
     */
1296 3
    public function mapManyEmbedded(array $mapping)
1297
    {
1298 3
        $mapping['embedded'] = true;
1299 3
        $mapping['type'] = 'many';
1300 3
        $this->mapField($mapping);
1301 3
    }
1302
1303
    /**
1304
     * Map a single document reference.
1305
     *
1306
     * @param array $mapping The mapping information.
1307
     */
1308 8
    public function mapOneReference(array $mapping)
1309
    {
1310 8
        $mapping['reference'] = true;
1311 8
        $mapping['type'] = 'one';
1312 8
        $this->mapField($mapping);
1313 8
    }
1314
1315
    /**
1316
     * Map a collection of document references.
1317
     *
1318
     * @param array $mapping The mapping information.
1319
     */
1320 8
    public function mapManyReference(array $mapping)
1321
    {
1322 8
        $mapping['reference'] = true;
1323 8
        $mapping['type'] = 'many';
1324 8
        $this->mapField($mapping);
1325 8
    }
1326
1327
    /**
1328
     * INTERNAL:
1329
     * Adds a field mapping without completing/validating it.
1330
     * This is mainly used to add inherited field mappings to derived classes.
1331
     *
1332
     * @param array $fieldMapping
1333
     */
1334 115
    public function addInheritedFieldMapping(array $fieldMapping)
1335
    {
1336 115
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1337
1338 115
        if (isset($fieldMapping['association'])) {
1339 76
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1340 76
        }
1341 115
    }
1342
1343
    /**
1344
     * INTERNAL:
1345
     * Adds an association mapping without completing/validating it.
1346
     * This is mainly used to add inherited association mappings to derived classes.
1347
     *
1348
     * @param array $mapping
1349
     *
1350
     * @return void
1351
     *
1352
     * @throws MappingException
1353
     */
1354 77
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1355
    {
1356 77
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1357 77
    }
1358
1359
    /**
1360
     * Checks whether the class has a mapped association with the given field name.
1361
     *
1362
     * @param string $fieldName
1363
     * @return boolean
1364
     */
1365 7
    public function hasReference($fieldName)
1366
    {
1367 7
        return isset($this->fieldMappings[$fieldName]['reference']);
1368
    }
1369
1370
    /**
1371
     * Checks whether the class has a mapped embed with the given field name.
1372
     *
1373
     * @param string $fieldName
1374
     * @return boolean
1375
     */
1376 5
    public function hasEmbed($fieldName)
1377
    {
1378 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1379
    }
1380
1381
    /**
1382
     * {@inheritDoc}
1383
     *
1384
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1385
     */
1386 7
    public function hasAssociation($fieldName)
1387
    {
1388 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1389
    }
1390
1391
    /**
1392
     * {@inheritDoc}
1393
     *
1394
     * Checks whether the class has a mapped reference or embed for the specified field and
1395
     * is a single valued association.
1396
     */
1397
    public function isSingleValuedAssociation($fieldName)
1398
    {
1399
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1400
    }
1401
1402
    /**
1403
     * {@inheritDoc}
1404
     *
1405
     * Checks whether the class has a mapped reference or embed for the specified field and
1406
     * is a collection valued association.
1407
     */
1408
    public function isCollectionValuedAssociation($fieldName)
1409
    {
1410
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1411
    }
1412
1413
    /**
1414
     * Checks whether the class has a mapped association for the specified field
1415
     * and if yes, checks whether it is a single-valued association (to-one).
1416
     *
1417
     * @param string $fieldName
1418
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1419
     */
1420
    public function isSingleValuedReference($fieldName)
1421
    {
1422
        return isset($this->fieldMappings[$fieldName]['association']) &&
1423
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1424
    }
1425
1426
    /**
1427
     * Checks whether the class has a mapped association for the specified field
1428
     * and if yes, checks whether it is a collection-valued association (to-many).
1429
     *
1430
     * @param string $fieldName
1431
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1432
     */
1433
    public function isCollectionValuedReference($fieldName)
1434
    {
1435
        return isset($this->fieldMappings[$fieldName]['association']) &&
1436
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1437
    }
1438
1439
    /**
1440
     * Checks whether the class has a mapped embedded document for the specified field
1441
     * and if yes, checks whether it is a single-valued association (to-one).
1442
     *
1443
     * @param string $fieldName
1444
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1445
     */
1446
    public function isSingleValuedEmbed($fieldName)
1447
    {
1448
        return isset($this->fieldMappings[$fieldName]['association']) &&
1449
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1450
    }
1451
1452
    /**
1453
     * Checks whether the class has a mapped embedded document for the specified field
1454
     * and if yes, checks whether it is a collection-valued association (to-many).
1455
     *
1456
     * @param string $fieldName
1457
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1458
     */
1459
    public function isCollectionValuedEmbed($fieldName)
1460
    {
1461
        return isset($this->fieldMappings[$fieldName]['association']) &&
1462
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1463
    }
1464
1465
    /**
1466
     * Sets the ID generator used to generate IDs for instances of this class.
1467
     *
1468
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1469
     */
1470 743
    public function setIdGenerator($generator)
1471
    {
1472 743
        $this->idGenerator = $generator;
1473 743
    }
1474
1475
    /**
1476
     * Casts the identifier to its portable PHP type.
1477
     *
1478
     * @param mixed $id
1479
     * @return mixed $id
1480
     */
1481 580
    public function getPHPIdentifierValue($id)
1482
    {
1483 580
        $idType = $this->fieldMappings[$this->identifier]['type'];
1484 580
        return Type::getType($idType)->convertToPHPValue($id);
1485
    }
1486
1487
    /**
1488
     * Casts the identifier to its database type.
1489
     *
1490
     * @param mixed $id
1491
     * @return mixed $id
1492
     */
1493 642
    public function getDatabaseIdentifierValue($id)
1494
    {
1495 642
        $idType = $this->fieldMappings[$this->identifier]['type'];
1496 642
        return Type::getType($idType)->convertToDatabaseValue($id);
1497
    }
1498
1499
    /**
1500
     * Sets the document identifier of a document.
1501
     *
1502
     * The value will be converted to a PHP type before being set.
1503
     *
1504
     * @param object $document
1505
     * @param mixed $id
1506
     */
1507 513
    public function setIdentifierValue($document, $id)
1508
    {
1509 513
        $id = $this->getPHPIdentifierValue($id);
1510 513
        $this->reflFields[$this->identifier]->setValue($document, $id);
1511 513
    }
1512
1513
    /**
1514
     * Gets the document identifier as a PHP type.
1515
     *
1516
     * @param object $document
1517
     * @return mixed $id
1518
     */
1519 593
    public function getIdentifierValue($document)
1520
    {
1521 593
        return $this->reflFields[$this->identifier]->getValue($document);
1522
    }
1523
1524
    /**
1525
     * {@inheritDoc}
1526
     *
1527
     * Since MongoDB only allows exactly one identifier field this is a proxy
1528
     * to {@see getIdentifierValue()} and returns an array with the identifier
1529
     * field as a key.
1530
     */
1531
    public function getIdentifierValues($object)
1532
    {
1533
        return array($this->identifier => $this->getIdentifierValue($object));
1534
    }
1535
1536
    /**
1537
     * Get the document identifier object as a database type.
1538
     *
1539
     * @param object $document
1540
     *
1541
     * @return \MongoId $id The MongoID object.
1542
     */
1543 32
    public function getIdentifierObject($document)
1544
    {
1545 32
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1546
    }
1547
1548
    /**
1549
     * Sets the specified field to the specified value on the given document.
1550
     *
1551
     * @param object $document
1552
     * @param string $field
1553
     * @param mixed $value
1554
     */
1555 7
    public function setFieldValue($document, $field, $value)
1556
    {
1557 7
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1558
            //property changes to an uninitialized proxy will not be tracked or persisted,
1559
            //so the proxy needs to be loaded first.
1560 1
            $document->__load();
1561 1
        }
1562
        
1563 7
        $this->reflFields[$field]->setValue($document, $value);
1564 7
    }
1565
1566
    /**
1567
     * Gets the specified field's value off the given document.
1568
     *
1569
     * @param object $document
1570
     * @param string $field
1571
     *
1572
     * @return mixed
1573
     */
1574 25
    public function getFieldValue($document, $field)
1575
    {
1576 25
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1577 1
            $document->__load();
1578 1
        }
1579
        
1580 25
        return $this->reflFields[$field]->getValue($document);
1581
    }
1582
1583
    /**
1584
     * Gets the mapping of a field.
1585
     *
1586
     * @param string $fieldName  The field name.
1587
     *
1588
     * @return array  The field mapping.
1589
     *
1590
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1591
     *
1592
     * @throws MappingException
1593
     */
1594 90
    public function getFieldMapping($fieldName)
1595
    {
1596 90
        if ( ! isset($this->fieldMappings[$fieldName])) {
1597 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1598
        }
1599 88
        return $this->fieldMappings[$fieldName];
1600
    }
1601
1602
    /**
1603
     * Gets mappings of fields holding embedded document(s).
1604
     *
1605
     * @return array of field mappings
1606
     */
1607 555
    public function getEmbeddedFieldsMappings()
1608
    {
1609 555
        return array_filter(
1610 555
            $this->associationMappings,
1611
            function($assoc) { return ! empty($assoc['embedded']); }
1612 555
        );
1613
    }
1614
1615
    /**
1616
     * Check if the field is not null.
1617
     *
1618
     * @param string $fieldName  The field name
1619
     *
1620
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1621
     */
1622 1
    public function isNullable($fieldName)
1623
    {
1624 1
        $mapping = $this->getFieldMapping($fieldName);
1625 1
        if ($mapping !== false) {
1626 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1627
        }
1628
        return false;
1629
    }
1630
1631
    /**
1632
     * Checks whether the document has a discriminator field and value configured.
1633
     *
1634
     * @return boolean
1635
     */
1636 474
    public function hasDiscriminator()
1637
    {
1638 474
        return isset($this->discriminatorField, $this->discriminatorValue);
1639
    }
1640
1641
    /**
1642
     * Sets the type of Id generator to use for the mapped class.
1643
     */
1644 336
    public function setIdGeneratorType($generatorType)
1645
    {
1646 336
        $this->generatorType = $generatorType;
1647 336
    }
1648
1649
    /**
1650
     * Sets the Id generator options.
1651
     */
1652
    public function setIdGeneratorOptions($generatorOptions)
1653
    {
1654
        $this->generatorOptions = $generatorOptions;
1655
    }
1656
1657
    /**
1658
     * @return boolean
1659
     */
1660 561
    public function isInheritanceTypeNone()
1661
    {
1662 561
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1663
    }
1664
1665
    /**
1666
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1667
     *
1668
     * @return boolean
1669
     */
1670 330
    public function isInheritanceTypeSingleCollection()
1671
    {
1672 330
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1673
    }
1674
1675
    /**
1676
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1677
     *
1678
     * @return boolean
1679
     */
1680
    public function isInheritanceTypeCollectionPerClass()
1681
    {
1682
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1683
    }
1684
1685
    /**
1686
     * Sets the mapped subclasses of this class.
1687
     *
1688
     * @param string[] $subclasses The names of all mapped subclasses.
1689
     */
1690 2 View Code Duplication
    public function setSubclasses(array $subclasses)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
1691
    {
1692 2
        foreach ($subclasses as $subclass) {
1693 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1694 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1695 1
            } else {
1696 1
                $this->subClasses[] = $subclass;
1697
            }
1698 2
        }
1699 2
    }
1700
1701
    /**
1702
     * Sets the parent class names.
1703
     * Assumes that the class names in the passed array are in the order:
1704
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1705
     *
1706
     * @param string[] $classNames
1707
     */
1708 798
    public function setParentClasses(array $classNames)
1709
    {
1710 798
        $this->parentClasses = $classNames;
1711
1712 798
        if (count($classNames) > 0) {
1713 101
            $this->rootDocumentName = array_pop($classNames);
1714 101
        }
1715 798
    }
1716
1717
    /**
1718
     * Checks whether the class will generate a new \MongoId instance for us.
1719
     *
1720
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1721
     */
1722
    public function isIdGeneratorAuto()
1723
    {
1724
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1725
    }
1726
1727
    /**
1728
     * Checks whether the class will use a collection to generate incremented identifiers.
1729
     *
1730
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1731
     */
1732
    public function isIdGeneratorIncrement()
1733
    {
1734
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1735
    }
1736
1737
    /**
1738
     * Checks whether the class will generate a uuid id.
1739
     *
1740
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1741
     */
1742
    public function isIdGeneratorUuid()
1743
    {
1744
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1745
    }
1746
1747
    /**
1748
     * Checks whether the class uses no id generator.
1749
     *
1750
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1751
     */
1752
    public function isIdGeneratorNone()
1753
    {
1754
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1755
    }
1756
1757
    /**
1758
     * Sets the version field mapping used for versioning. Sets the default
1759
     * value to use depending on the column type.
1760
     *
1761
     * @param array $mapping   The version field mapping array
1762
     * 
1763
     * @throws LockException
1764
     */
1765 93
    public function setVersionMapping(array &$mapping)
1766
    {
1767 93
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
1768 1
            throw LockException::invalidVersionFieldType($mapping['type']);
1769
        }
1770
1771 92
        $this->isVersioned  = true;
1772 92
        $this->versionField = $mapping['fieldName'];
1773 92
    }
1774
1775
    /**
1776
     * Sets whether this class is to be versioned for optimistic locking.
1777
     *
1778
     * @param boolean $bool
1779
     */
1780 330
    public function setVersioned($bool)
1781
    {
1782 330
        $this->isVersioned = $bool;
1783 330
    }
1784
1785
    /**
1786
     * Sets the name of the field that is to be used for versioning if this class is
1787
     * versioned for optimistic locking.
1788
     *
1789
     * @param string $versionField
1790
     */
1791 330
    public function setVersionField($versionField)
1792
    {
1793 330
        $this->versionField = $versionField;
1794 330
    }
1795
1796
    /**
1797
     * Sets the version field mapping used for versioning. Sets the default
1798
     * value to use depending on the column type.
1799
     *
1800
     * @param array $mapping   The version field mapping array
1801
     *
1802
     * @throws \Doctrine\ODM\MongoDB\LockException
1803
     */
1804 26
    public function setLockMapping(array &$mapping)
1805
    {
1806 26
        if ($mapping['type'] !== 'int') {
1807 1
            throw LockException::invalidLockFieldType($mapping['type']);
1808
        }
1809
1810 25
        $this->isLockable = true;
1811 25
        $this->lockField = $mapping['fieldName'];
1812 25
    }
1813
1814
    /**
1815
     * Sets whether this class is to allow pessimistic locking.
1816
     *
1817
     * @param boolean $bool
1818
     */
1819
    public function setLockable($bool)
1820
    {
1821
        $this->isLockable = $bool;
1822
    }
1823
1824
    /**
1825
     * Sets the name of the field that is to be used for storing whether a document
1826
     * is currently locked or not.
1827
     *
1828
     * @param string $lockField
1829
     */
1830
    public function setLockField($lockField)
1831
    {
1832
        $this->lockField = $lockField;
1833
    }
1834
1835
    /**
1836
     * {@inheritDoc}
1837
     */
1838
    public function getFieldNames()
1839
    {
1840
        return array_keys($this->fieldMappings);
1841
    }
1842
1843
    /**
1844
     * {@inheritDoc}
1845
     */
1846
    public function getAssociationNames()
1847
    {
1848
        return array_keys($this->associationMappings);
1849
    }
1850
1851
    /**
1852
     * {@inheritDoc}
1853
     */
1854 21
    public function getTypeOfField($fieldName)
1855
    {
1856 21
        return isset($this->fieldMappings[$fieldName]) ?
1857 21
            $this->fieldMappings[$fieldName]['type'] : null;
1858
    }
1859
1860
    /**
1861
     * {@inheritDoc}
1862
     */
1863 6
    public function getAssociationTargetClass($assocName)
1864
    {
1865 6
        if ( ! isset($this->associationMappings[$assocName])) {
1866 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
1867
        }
1868
1869 3
        return $this->associationMappings[$assocName]['targetDocument'];
1870
    }
1871
1872
    /**
1873
     * {@inheritDoc}
1874
     */
1875
    public function isAssociationInverseSide($fieldName)
1876
    {
1877
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
1878
    }
1879
1880
    /**
1881
     * {@inheritDoc}
1882
     */
1883
    public function getAssociationMappedByTargetField($fieldName)
1884
    {
1885
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
1886
    }
1887
}
1888