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

ClassMetadataInfo   F

Complexity

Total Complexity 297

Size/Duplication

Total Lines 2103
Duplicated Lines 3.52 %

Coupling/Cohesion

Components 9
Dependencies 5

Test Coverage

Coverage 84.66%

Importance

Changes 0
Metric Value
wmc 297
c 0
b 0
f 0
lcom 9
cbo 5
dl 74
loc 2103
ccs 469
cts 554
cp 0.8466
rs 0.5217

114 Methods

Rating   Name   Duplication   Size   Complexity  
A getReflectionClass() 0 8 2
A isIdentifier() 0 4 1
A setIdentifier() 0 4 1
A getIdentifier() 0 4 1
A getIdentifierFieldNames() 0 4 1
A hasField() 0 4 1
A setInheritanceType() 0 4 1
A isInheritedField() 0 4 1
B setCustomRepositoryClass() 3 12 6
B invokeLifecycleCallbacks() 0 18 5
A hasLifecycleCallbacks() 0 4 1
A getLifecycleCallbacks() 0 4 2
A addLifecycleCallback() 0 8 3
A setLifecycleCallbacks() 0 4 1
A registerAlsoLoadMethod() 0 4 2
A setAlsoLoadMethods() 0 4 1
C setDiscriminatorField() 0 25 7
B setDiscriminatorMap() 0 19 7
A setDefaultDiscriminatorValue() 0 14 3
A __construct() 0 5 1
A getReferenceId() 0 4 1
A getReferencePrefix() 0 4 2
A setDiscriminatorValue() 0 5 1
A setSlaveOkay() 0 8 2
B addIndex() 14 20 6
A setRequireIndexes() 0 8 1
A getIndexes() 0 4 1
A hasIndexes() 0 4 2
C setShardKey() 14 42 13
A getShardKey() 0 4 1
A isSharded() 0 4 2
A setWriteConcern() 0 4 1
A getWriteConcern() 0 4 1
A hasWriteConcern() 0 4 1
A setChangeTrackingPolicy() 0 4 1
A isChangeTrackingDeferredExplicit() 0 4 1
A isChangeTrackingDeferredImplicit() 0 4 1
A isChangeTrackingNotify() 0 4 1
A getReflectionProperties() 0 4 1
A getReflectionProperty() 0 4 1
A getName() 0 4 1
A getNamespace() 0 4 1
A getDatabase() 0 4 1
A setDatabase() 0 4 1
A getCollection() 0 4 1
B setCollection() 0 14 6
A getCollectionCapped() 0 4 1
A setCollectionCapped() 0 4 1
A getCollectionSize() 0 4 1
A setCollectionSize() 0 4 1
A getCollectionMax() 0 4 1
A setCollectionMax() 0 4 1
A isMappedToCollection() 0 4 2
A isFile() 0 4 2
A getFile() 0 4 1
A setFile() 0 4 1
A getDistance() 0 4 1
A setDistance() 0 4 1
F mapField() 43 194 86
C applyStorageStrategy() 0 44 14
A mapFile() 0 6 1
A mapOneEmbedded() 0 6 1
A mapManyEmbedded() 0 6 1
A mapOneReference() 0 6 1
A mapManyReference() 0 6 1
A addInheritedFieldMapping() 0 8 2
A addInheritedAssociationMapping() 0 4 1
A hasReference() 0 4 1
A hasEmbed() 0 4 1
A hasAssociation() 0 4 2
A isSingleValuedAssociation() 0 4 2
A isCollectionValuedAssociation() 0 4 2
A isSingleValuedReference() 0 5 2
A isCollectionValuedReference() 0 5 2
A isSingleValuedEmbed() 0 5 2
A isCollectionValuedEmbed() 0 5 2
A setIdGenerator() 0 4 1
A getPHPIdentifierValue() 0 5 1
A getDatabaseIdentifierValue() 0 5 1
A setIdentifierValue() 0 5 1
A getIdentifierValue() 0 4 1
A getIdentifierValues() 0 4 1
A getIdentifierObject() 0 4 1
A setFieldValue() 0 10 3
A getFieldValue() 0 8 4
A getFieldMapping() 0 7 2
A getEmbeddedFieldsMappings() 0 7 1
A getFieldMappingByDbFieldName() 0 10 3
A isNullable() 0 8 3
A hasDiscriminator() 0 4 1
A setIdGeneratorType() 0 4 1
A setIdGeneratorOptions() 0 4 1
A isInheritanceTypeNone() 0 4 1
A isInheritanceTypeSingleCollection() 0 4 1
A isInheritanceTypeCollectionPerClass() 0 4 1
A setSubclasses() 0 10 4
A setParentClasses() 0 8 2
A isIdGeneratorAuto() 0 4 1
A isIdGeneratorIncrement() 0 4 1
A isIdGeneratorUuid() 0 4 1
A isIdGeneratorNone() 0 4 1
A setVersionMapping() 0 9 3
A setVersioned() 0 4 1
A setVersionField() 0 4 1
A setLockMapping() 0 9 2
A setLockable() 0 4 1
A setLockField() 0 4 1
A getFieldNames() 0 4 1
A getAssociationNames() 0 4 1
A getTypeOfField() 0 5 2
A getAssociationTargetClass() 0 8 2
A getAssociationCollectionClass() 0 12 3
A isAssociationInverseSide() 0 4 1
A getAssociationMappedByTargetField() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ClassMetadataInfo often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ClassMetadataInfo, and based on these observations, apply Extract Interface, too.

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

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

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

Loading history...
589 4
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
590
        }
591
592 321
        $this->customRepositoryClassName = $repositoryClassName;
593 321
    }
594
595
    /**
596
     * Dispatches the lifecycle event of the given document by invoking all
597
     * registered callbacks.
598
     *
599
     * @param string $event     Lifecycle event
600
     * @param object $document  Document on which the event occurred
601
     * @param array  $arguments Arguments to pass to all callbacks
602
     * @throws \InvalidArgumentException if document class is not this class or
603
     *                                   a Proxy of this class
604
     */
605 663
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
606
    {
607 663
        if ( ! $document instanceof $this->name) {
608 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
609
        }
610
611 662
        if (empty($this->lifecycleCallbacks[$event])) {
612 648
            return;
613
        }
614
615 199
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
616 199
            if ($arguments !== null) {
617 198
                call_user_func_array(array($document, $callback), $arguments);
618
            } else {
619 199
                $document->$callback();
620
            }
621
        }
622 199
    }
623
624
    /**
625
     * Checks whether the class has callbacks registered for a lifecycle event.
626
     *
627
     * @param string $event Lifecycle event
628
     *
629
     * @return boolean
630
     */
631
    public function hasLifecycleCallbacks($event)
632
    {
633
        return ! empty($this->lifecycleCallbacks[$event]);
634
    }
635
636
    /**
637
     * Gets the registered lifecycle callbacks for an event.
638
     *
639
     * @param string $event
640
     * @return array
641
     */
642
    public function getLifecycleCallbacks($event)
643
    {
644
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
645
    }
646
647
    /**
648
     * Adds a lifecycle callback for documents of this class.
649
     *
650
     * If the callback is already registered, this is a NOOP.
651
     *
652
     * @param string $callback
653
     * @param string $event
654
     */
655 300
    public function addLifecycleCallback($callback, $event)
656
    {
657 300
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
658 1
            return;
659
        }
660
661 300
        $this->lifecycleCallbacks[$event][] = $callback;
662 300
    }
663
664
    /**
665
     * Sets the lifecycle callbacks for documents of this class.
666
     *
667
     * Any previously registered callbacks are overwritten.
668
     *
669
     * @param array $callbacks
670
     */
671 372
    public function setLifecycleCallbacks(array $callbacks)
672
    {
673 372
        $this->lifecycleCallbacks = $callbacks;
674 372
    }
675
676
    /**
677
     * Registers a method for loading document data before field hydration.
678
     *
679
     * Note: A method may be registered multiple times for different fields.
680
     * it will be invoked only once for the first field found.
681
     *
682
     * @param string       $method Method name
683
     * @param array|string $fields Database field name(s)
684
     */
685 15
    public function registerAlsoLoadMethod($method, $fields)
686
    {
687 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
688 15
    }
689
690
    /**
691
     * Sets the AlsoLoad methods for documents of this class.
692
     *
693
     * Any previously registered methods are overwritten.
694
     *
695
     * @param array $methods
696
     */
697 372
    public function setAlsoLoadMethods(array $methods)
698
    {
699 372
        $this->alsoLoadMethods = $methods;
700 372
    }
701
702
    /**
703
     * Sets the discriminator field.
704
     *
705
     * The field name is the the unmapped database field. Discriminator values
706
     * are only used to discern the hydration class and are not mapped to class
707
     * properties.
708
     *
709
     * @param string $discriminatorField
710
     *
711
     * @throws MappingException If the discriminator field conflicts with the
712
     *                          "name" attribute of a mapped field.
713
     */
714 402
    public function setDiscriminatorField($discriminatorField)
715
    {
716 402
        if ($discriminatorField === null) {
717 329
            $this->discriminatorField = null;
718
719 329
            return;
720
        }
721
722
        // Handle array argument with name/fieldName keys for BC
723 130
        if (is_array($discriminatorField)) {
724
            if (isset($discriminatorField['name'])) {
725
                $discriminatorField = $discriminatorField['name'];
726
            } elseif (isset($discriminatorField['fieldName'])) {
727
                $discriminatorField = $discriminatorField['fieldName'];
728
            }
729
        }
730
731 130
        foreach ($this->fieldMappings as $fieldMapping) {
732 4
            if ($discriminatorField == $fieldMapping['name']) {
733 4
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
734
            }
735
        }
736
737 129
        $this->discriminatorField = $discriminatorField;
738 129
    }
739
740
    /**
741
     * Sets the discriminator values used by this class.
742
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
743
     *
744
     * @param array $map
745
     *
746
     * @throws MappingException
747
     */
748 395
    public function setDiscriminatorMap(array $map)
749
    {
750 395
        foreach ($map as $value => $className) {
751 125
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
752 91
                $className = $this->namespace . '\\' . $className;
753
            }
754 125
            $this->discriminatorMap[$value] = $className;
755 125
            if ($this->name == $className) {
756 117
                $this->discriminatorValue = $value;
757
            } else {
758 120
                if ( ! class_exists($className)) {
759
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
760
                }
761 120
                if (is_subclass_of($className, $this->name)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $this->name can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
762 125
                    $this->subClasses[] = $className;
763
                }
764
            }
765
        }
766 395
    }
767
768
    /**
769
     * Sets the default discriminator value to be used for this class
770
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
771
     *
772
     * @param string $defaultDiscriminatorValue
773
     *
774
     * @throws MappingException
775
     */
776 379
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
777
    {
778 379
        if ($defaultDiscriminatorValue === null) {
779 372
            $this->defaultDiscriminatorValue = null;
780
781 372
            return;
782
        }
783
784 60
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
785
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
786
        }
787
788 60
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
789 60
    }
790
791
    /**
792
     * Sets the discriminator value for this class.
793
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
794
     * collection.
795
     *
796
     * @param string $value
797
     */
798 3
    public function setDiscriminatorValue($value)
799
    {
800 3
        $this->discriminatorMap[$value] = $this->name;
801 3
        $this->discriminatorValue = $value;
802 3
    }
803
804
    /**
805
     * Sets the slaveOkay option applied to collections for this class.
806
     *
807
     * @param boolean|null $slaveOkay
808
     *
809
     * @deprecated in version 1.2 and will be removed in 2.0.
810
     */
811 3
    public function setSlaveOkay($slaveOkay)
812
    {
813 3
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
814 3
            sprintf('%s was deprecated in version 1.2 and will be removed in 2.0.'),
815 3
            E_USER_DEPRECATED
816
        );
817 3
        $this->slaveOkay = $slaveOkay === null ? null : (boolean) $slaveOkay;
0 ignored issues
show
Deprecated Code introduced by
The property Doctrine\ODM\MongoDB\Map...etadataInfo::$slaveOkay has been deprecated with message: in version 1.2 and will be removed in 2.0.

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

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

Loading history...
818 3
    }
819
820
    /**
821
     * Add a index for this Document.
822
     *
823
     * @param array $keys Array of keys for the index.
824
     * @param array $options Array of options for the index.
825
     */
826 235
    public function addIndex($keys, array $options = array())
827
    {
828 235
        $this->indexes[] = array(
829 View Code Duplication
            'keys' => array_map(function($value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
830 235
                if ($value == 1 || $value == -1) {
831 63
                    return (int) $value;
832
                }
833 227
                if (is_string($value)) {
834 227
                    $lower = strtolower($value);
835 227
                    if ($lower === 'asc') {
836 220
                        return 1;
837 11
                    } elseif ($lower === 'desc') {
838 4
                        return -1;
839
                    }
840
                }
841 7
                return $value;
842 235
            }, $keys),
843 235
            'options' => $options
844
        );
845 235
    }
846
847
    /**
848
     * Set whether or not queries on this document should require indexes.
849
     *
850
     * @param bool $requireIndexes
851
     *
852
     * @deprecated method was deprecated in 1.2 and will be removed in 2.0
853
     */
854 901
    public function setRequireIndexes($requireIndexes)
855
    {
856 901
        @trigger_error(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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

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

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

Loading history...
861 901
    }
862
863
    /**
864
     * Returns the array of indexes for this Document.
865
     *
866
     * @return array $indexes The array of indexes.
867
     */
868 54
    public function getIndexes()
869
    {
870 54
        return $this->indexes;
871
    }
872
873
    /**
874
     * Checks whether this document has indexes or not.
875
     *
876
     * @return boolean
877
     */
878
    public function hasIndexes()
879
    {
880
        return $this->indexes ? true : false;
881
    }
882
883
    /**
884
     * Set shard key for this Document.
885
     *
886
     * @param array $keys Array of document keys.
887
     * @param array $options Array of sharding options.
888
     *
889
     * @throws MappingException
890
     */
891 77
    public function setShardKey(array $keys, array $options = array())
892
    {
893 77
        if ($this->inheritanceType === self::INHERITANCE_TYPE_SINGLE_COLLECTION && !is_null($this->shardKey)) {
894 2
            throw MappingException::shardKeyInSingleCollInheritanceSubclass($this->getName());
895
        }
896
897 77
        if ($this->isEmbeddedDocument) {
898 2
            throw MappingException::embeddedDocumentCantHaveShardKey($this->getName());
899
        }
900
901 75
        foreach (array_keys($keys) as $field) {
902 75
            if (! isset($this->fieldMappings[$field])) {
903 68
                continue;
904
            }
905
906 7
            if (in_array($this->fieldMappings[$field]['type'], ['many', 'collection'])) {
907 3
                throw MappingException::noMultiKeyShardKeys($this->getName(), $field);
908
            }
909
910 4
            if ($this->fieldMappings[$field]['strategy'] !== static::STORAGE_STRATEGY_SET) {
911 4
                throw MappingException::onlySetStrategyAllowedInShardKey($this->getName(), $field);
912
            }
913
        }
914
915 71
        $this->shardKey = array(
916 View Code Duplication
            'keys' => array_map(function($value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

Loading history...
1262 623
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1263
        }
1264 958
        if (isset($mapping['collectionClass'])) {
1265 65 View Code Duplication
            if (strpos($mapping['collectionClass'], '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1266 63
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1267
            }
1268 65
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1269
        }
1270 958
        if ( ! empty($mapping['collectionClass'])) {
1271 65
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1272 65
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1273 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1274
            }
1275
        }
1276
1277 957
        if (isset($mapping['discriminatorMap'])) {
1278 127
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1279 127 View Code Duplication
                if (strpos($class, '\\') === false && strlen($this->namespace)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1280 127
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1281
                }
1282
            }
1283
        }
1284
1285 957
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1286 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1287
        }
1288
1289 956
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1290
1291 956
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1292 651
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1293
        }
1294
1295 956
        if (isset($mapping['embedded'])) {
1296 608
            unset($mapping['cascade']);
1297 951
        } elseif (isset($mapping['cascade'])) {
1298 411
            $mapping['cascade'] = $cascades;
1299
        }
1300
1301 956
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1302 956
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1303 956
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1304 956
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1305 956
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1306
1307 956
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1308 63
            $mapping['file'] = true;
1309
        }
1310 956
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1311 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1312
        }
1313 956 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...
1314 63
            $this->file = $mapping['fieldName'];
1315 63
            $mapping['name'] = 'file';
1316
        }
1317 956 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...
1318 7
            $this->distance = $mapping['fieldName'];
1319
        }
1320 956
        if (isset($mapping['id']) && $mapping['id'] === true) {
1321 928
            $mapping['name'] = '_id';
1322 928
            $this->identifier = $mapping['fieldName'];
1323 928 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...
1324 909
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1325
            }
1326 928
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1327 928
            switch ($this->generatorType) {
1328 928
                case self::GENERATOR_TYPE_AUTO:
1329 854
                    $mapping['type'] = 'id';
1330 854
                    break;
1331
                default:
1332 157
                    if ( ! empty($this->generatorOptions['type'])) {
1333 56
                        $mapping['type'] = $this->generatorOptions['type'];
1334 101
                    } elseif (empty($mapping['type'])) {
1335 86
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1336
                    }
1337
            }
1338 928
            unset($this->generatorOptions['type']);
1339
        }
1340
1341 956
        if ( ! isset($mapping['nullable'])) {
1342 53
            $mapping['nullable'] = false;
1343
        }
1344
1345
        // Synchronize the "simple" and "storeAs" mapping information for backwards compatibility
1346 956
        if (isset($mapping['simple']) && ($mapping['simple'] === true || $mapping['simple'] === 'true')) {
1347 296
            $mapping['storeAs'] = ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1348 296
            @trigger_error('"simple" attribute of a reference is deprecated - use storeAs="id" instead.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

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

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

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