Completed
Pull Request — master (#1333)
by Maciej
12:47
created

ClassMetadataInfo::getFieldMapping()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

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