Completed
Pull Request — master (#1734)
by Maciej
09:56
created

ClassMetadataInfo   F

Complexity

Total Complexity 277

Size/Duplication

Total Lines 2016
Duplicated Lines 3.32 %

Coupling/Cohesion

Components 9
Dependencies 5

Test Coverage

Coverage 84.19%

Importance

Changes 0
Metric Value
wmc 277
lcom 9
cbo 5
dl 67
loc 2016
ccs 442
cts 525
cp 0.8419
rs 0.5217
c 0
b 0
f 0

109 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getReferenceId() 0 4 2
A getReferencePrefix() 0 8 3
A getReferenceFieldName() 0 8 3
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 1
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 setDiscriminatorValue() 0 5 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 setReadPreference() 0 5 1
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
A setCollection() 0 14 3
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
F mapField() 36 171 74
C applyStorageStrategy() 0 43 13
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 markReadOnly() 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
B addIndex() 14 20 6

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
namespace Doctrine\ODM\MongoDB\Mapping;
4
5
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
6
use Doctrine\ODM\MongoDB\LockException;
7
use Doctrine\ODM\MongoDB\Proxy\Proxy;
8
use Doctrine\ODM\MongoDB\Types\Type;
9
use InvalidArgumentException;
10
11
/**
12
 * A <tt>ClassMetadata</tt> instance holds all the object-document mapping metadata
13
 * of a document and it's references.
14
 *
15
 * Once populated, ClassMetadata instances are usually cached in a serialized form.
16
 *
17
 * <b>IMPORTANT NOTE:</b>
18
 *
19
 * The fields of this class are only public for 2 reasons:
20
 * 1) To allow fast READ access.
21
 * 2) To drastically reduce the size of a serialized instance (private/protected members
22
 *    get the whole class name, namespace inclusive, prepended to every property in
23
 *    the serialized representation).
24
 *
25
 * @since       1.0
26
 */
27
class ClassMetadataInfo implements \Doctrine\Common\Persistence\Mapping\ClassMetadata
28
{
29
    /* The Id generator types. */
30
    /**
31
     * AUTO means Doctrine will automatically create a new \MongoDB\BSON\ObjectId instance for us.
32
     */
33
    const GENERATOR_TYPE_AUTO = 1;
34
35
    /**
36
     * INCREMENT means a separate collection is used for maintaining and incrementing id generation.
37
     * Offers full portability.
38
     */
39
    const GENERATOR_TYPE_INCREMENT = 2;
40
41
    /**
42
     * UUID means Doctrine will generate a uuid for us.
43
     */
44
    const GENERATOR_TYPE_UUID = 3;
45
46
    /**
47
     * ALNUM means Doctrine will generate Alpha-numeric string identifiers, using the INCREMENT
48
     * generator to ensure identifier uniqueness
49
     */
50
    const GENERATOR_TYPE_ALNUM = 4;
51
52
    /**
53
     * CUSTOM means Doctrine expect a class parameter. It will then try to initiate that class
54
     * and pass other options to the generator. It will throw an Exception if the class
55
     * does not exist or if an option was passed for that there is not setter in the new
56
     * generator class.
57
     *
58
     * The class  will have to be a subtype of AbstractIdGenerator.
59
     */
60
    const GENERATOR_TYPE_CUSTOM = 5;
61
62
    /**
63
     * NONE means Doctrine will not generate any id for us and you are responsible for manually
64
     * assigning an id.
65
     */
66
    const GENERATOR_TYPE_NONE = 6;
67
68
    /**
69
     * Default discriminator field name.
70
     *
71
     * This is used for associations value for associations where a that do not define a "targetDocument" or
72
     * "discriminatorField" option in their mapping.
73
     */
74
    const DEFAULT_DISCRIMINATOR_FIELD = '_doctrine_class_name';
75
76
    const REFERENCE_ONE = 1;
77
    const REFERENCE_MANY = 2;
78
    const EMBED_ONE = 3;
79
    const EMBED_MANY = 4;
80
    const MANY = 'many';
81
    const ONE = 'one';
82
83
    /**
84
     * The types of storeAs references
85
     */
86
    const REFERENCE_STORE_AS_ID = 'id';
87
    const REFERENCE_STORE_AS_DB_REF = 'dbRef';
88
    const REFERENCE_STORE_AS_DB_REF_WITH_DB = 'dbRefWithDb';
89
    const REFERENCE_STORE_AS_REF = 'ref';
90
91
    /* The inheritance mapping types */
92
    /**
93
     * NONE means the class does not participate in an inheritance hierarchy
94
     * and therefore does not need an inheritance mapping type.
95
     */
96
    const INHERITANCE_TYPE_NONE = 1;
97
98
    /**
99
     * SINGLE_COLLECTION means the class will be persisted according to the rules of
100
     * <tt>Single Collection Inheritance</tt>.
101
     */
102
    const INHERITANCE_TYPE_SINGLE_COLLECTION = 2;
103
104
    /**
105
     * COLLECTION_PER_CLASS means the class will be persisted according to the rules
106
     * of <tt>Concrete Collection Inheritance</tt>.
107
     */
108
    const INHERITANCE_TYPE_COLLECTION_PER_CLASS = 3;
109
110
    /**
111
     * DEFERRED_IMPLICIT means that changes of entities are calculated at commit-time
112
     * by doing a property-by-property comparison with the original data. This will
113
     * be done for all entities that are in MANAGED state at commit-time.
114
     *
115
     * This is the default change tracking policy.
116
     */
117
    const CHANGETRACKING_DEFERRED_IMPLICIT = 1;
118
119
    /**
120
     * DEFERRED_EXPLICIT means that changes of entities are calculated at commit-time
121
     * by doing a property-by-property comparison with the original data. This will
122
     * be done only for entities that were explicitly saved (through persist() or a cascade).
123
     */
124
    const CHANGETRACKING_DEFERRED_EXPLICIT = 2;
125
126
    /**
127
     * NOTIFY means that Doctrine relies on the entities sending out notifications
128
     * when their properties change. Such entity classes must implement
129
     * the <tt>NotifyPropertyChanged</tt> interface.
130
     */
131
    const CHANGETRACKING_NOTIFY = 3;
132
133
    /**
134
     * SET means that fields will be written to the database using a $set operator
135
     */
136
    const STORAGE_STRATEGY_SET = 'set';
137
138
    /**
139
     * INCREMENT means that fields will be written to the database by calculating
140
     * the difference and using the $inc operator
141
     */
142
    const STORAGE_STRATEGY_INCREMENT = 'increment';
143
144
    const STORAGE_STRATEGY_PUSH_ALL = 'pushAll';
145
    const STORAGE_STRATEGY_ADD_TO_SET = 'addToSet';
146
    const STORAGE_STRATEGY_ATOMIC_SET = 'atomicSet';
147
    const STORAGE_STRATEGY_ATOMIC_SET_ARRAY = 'atomicSetArray';
148
    const STORAGE_STRATEGY_SET_ARRAY = 'setArray';
149
150
    /**
151
     * READ-ONLY: The name of the mongo database the document is mapped to.
152
     */
153
    public $db;
154
155
    /**
156
     * READ-ONLY: The name of the mongo collection the document is mapped to.
157
     */
158
    public $collection;
159
160
    /**
161
     * READ-ONLY: If the collection should be a fixed size.
162
     */
163
    public $collectionCapped;
164
165
    /**
166
     * READ-ONLY: If the collection is fixed size, its size in bytes.
167
     */
168
    public $collectionSize;
169
170
    /**
171
     * READ-ONLY: If the collection is fixed size, the maximum number of elements to store in the collection.
172
     */
173
    public $collectionMax;
174
175
    /**
176
     * READ-ONLY Describes how MongoDB clients route read operations to the members of a replica set.
177
     */
178
    public $readPreference;
179
180
    /**
181
     * READ-ONLY Associated with readPreference Allows to specify criteria so that your application can target read
182
     * operations to specific members, based on custom parameters.
183
     */
184
    public $readPreferenceTags;
185
186
    /**
187
     * READ-ONLY: Describes the level of acknowledgement requested from MongoDB for write operations.
188
     */
189
    public $writeConcern;
190
191
    /**
192
     * READ-ONLY: The field name of the document identifier.
193
     */
194
    public $identifier;
195
196
    /**
197
     * READ-ONLY: The array of indexes for the document collection.
198
     */
199
    public $indexes = array();
200
201
    /**
202
     * READ-ONLY: Keys and options describing shard key. Only for sharded collections.
203
     */
204
    public $shardKey;
205
206
    /**
207
     * READ-ONLY: The name of the document class.
208
     */
209
    public $name;
210
211
    /**
212
     * READ-ONLY: The namespace the document class is contained in.
213
     *
214
     * @var string
215
     * @todo Not really needed. Usage could be localized.
216
     */
217
    public $namespace;
218
219
    /**
220
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
221
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
222
     * as {@link $documentName}.
223
     *
224
     * @var string
225
     */
226
    public $rootDocumentName;
227
228
    /**
229
     * The name of the custom repository class used for the document class.
230
     * (Optional).
231
     *
232
     * @var string
233
     */
234
    public $customRepositoryClassName;
235
236
    /**
237
     * READ-ONLY: The names of the parent classes (ancestors).
238
     *
239
     * @var array
240
     */
241
    public $parentClasses = array();
242
243
    /**
244
     * READ-ONLY: The names of all subclasses (descendants).
245
     *
246
     * @var array
247
     */
248
    public $subClasses = array();
249
250
    /**
251
     * The ReflectionProperty instances of the mapped class.
252
     *
253
     * @var \ReflectionProperty[]
254
     */
255
    public $reflFields = array();
256
257
    /**
258
     * READ-ONLY: The inheritance mapping type used by the class.
259
     *
260
     * @var integer
261
     */
262
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
263
264
    /**
265
     * READ-ONLY: The Id generator type used by the class.
266
     *
267
     * @var string
268
     */
269
    public $generatorType = self::GENERATOR_TYPE_AUTO;
270
271
    /**
272
     * READ-ONLY: The Id generator options.
273
     *
274
     * @var array
275
     */
276
    public $generatorOptions = array();
277
278
    /**
279
     * READ-ONLY: The ID generator used for generating IDs for this class.
280
     *
281
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
282
     */
283
    public $idGenerator;
284
285
    /**
286
     * READ-ONLY: The field mappings of the class.
287
     * Keys are field names and values are mapping definitions.
288
     *
289
     * The mapping definition array has the following values:
290
     *
291
     * - <b>fieldName</b> (string)
292
     * The name of the field in the Document.
293
     *
294
     * - <b>id</b> (boolean, optional)
295
     * Marks the field as the primary key of the document. Multiple fields of an
296
     * document can have the id attribute, forming a composite key.
297
     *
298
     * @var array
299
     */
300
    public $fieldMappings = array();
301
302
    /**
303
     * READ-ONLY: The association mappings of the class.
304
     * Keys are field names and values are mapping definitions.
305
     *
306
     * @var array
307
     */
308
    public $associationMappings = array();
309
310
    /**
311
     * READ-ONLY: Array of fields to also load with a given method.
312
     *
313
     * @var array
314
     */
315
    public $alsoLoadMethods = array();
316
317
    /**
318
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
319
     *
320
     * @var array
321
     */
322
    public $lifecycleCallbacks = array();
323
324
    /**
325
     * READ-ONLY: The discriminator value of this class.
326
     *
327
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
328
     * where a discriminator field is used.</b>
329
     *
330
     * @var mixed
331
     * @see discriminatorField
332
     */
333
    public $discriminatorValue;
334
335
    /**
336
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
337
     *
338
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
339
     * where a discriminator field is used.</b>
340
     *
341
     * @var mixed
342
     * @see discriminatorField
343
     */
344
    public $discriminatorMap = array();
345
346
    /**
347
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
348
     * inheritance mapping.
349
     *
350
     * @var string
351
     */
352
    public $discriminatorField;
353
354
    /**
355
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
356
     *
357
     * @var string
358
     * @see discriminatorField
359
     */
360
    public $defaultDiscriminatorValue;
361
362
    /**
363
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
364
     *
365
     * @var boolean
366
     */
367
    public $isMappedSuperclass = false;
368
369
    /**
370
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
371
     *
372
     * @var boolean
373
     */
374
    public $isEmbeddedDocument = false;
375
376
    /**
377
     * READ-ONLY: Whether this class describes the mapping of an aggregation result document.
378
     *
379
     * @var boolean
380
     */
381
    public $isQueryResultDocument = false;
382
383
    /**
384
     * READ-ONLY: The policy used for change-tracking on entities of this class.
385
     *
386
     * @var integer
387
     */
388
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
389
390
    /**
391
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
392
     * with optimistic locking.
393
     *
394
     * @var boolean $isVersioned
395
     */
396
    public $isVersioned;
397
398
    /**
399
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
400
     *
401
     * @var mixed $versionField
402
     */
403
    public $versionField;
404
405
    /**
406
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
407
     * locking.
408
     *
409
     * @var boolean $isLockable
410
     */
411
    public $isLockable;
412
413
    /**
414
     * READ-ONLY: The name of the field which is used for locking a document.
415
     *
416
     * @var mixed $lockField
417
     */
418
    public $lockField;
419
420
    /**
421
     * The ReflectionClass instance of the mapped class.
422
     *
423
     * @var \ReflectionClass
424
     */
425
    public $reflClass;
426
427
    /**
428
     * READ_ONLY: A flag for whether or not this document is read-only.
429
     *
430
     * @var bool
431
     */
432
    public $isReadOnly;
433
434
    /**
435
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
436
     * metadata of the class with the given name.
437
     *
438
     * @param string $documentName The name of the document class the new instance is used for.
439
     */
440 1474
    public function __construct($documentName)
441
    {
442 1474
        $this->name = $documentName;
443 1474
        $this->rootDocumentName = $documentName;
444 1474
    }
445
446
    /**
447
     * Helper method to get reference id of ref* type references
448
     * @param mixed  $reference
449
     * @param string $storeAs
450
     * @return mixed
451
     * @internal
452
     */
453 120
    public static function getReferenceId($reference, $storeAs)
454
    {
455 120
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID ? $reference : $reference[ClassMetadataInfo::getReferencePrefix($storeAs) . 'id'];
456
    }
457
458
    /**
459
     * Returns the reference prefix used for a reference
460
     * @param string $storeAs
461
     * @return string
462
     */
463 185
    private static function getReferencePrefix($storeAs)
464
    {
465 185
        if (!in_array($storeAs, [ClassMetadataInfo::REFERENCE_STORE_AS_REF, ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF, ClassMetadataInfo::REFERENCE_STORE_AS_DB_REF_WITH_DB])) {
466
            throw new \LogicException('Can only get a reference prefix for DBRef and reference arrays');
467
        }
468
469 185
        return $storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_REF ? '' : '$';
470
    }
471
472
    /**
473
     * Returns a fully qualified field name for a given reference
474
     * @param string $storeAs
475
     * @param string $pathPrefix The field path prefix
476
     * @return string
477
     * @internal
478
     */
479 134
    public static function getReferenceFieldName($storeAs, $pathPrefix = '')
480
    {
481 134
        if ($storeAs === ClassMetadataInfo::REFERENCE_STORE_AS_ID) {
482 94
            return $pathPrefix;
483
        }
484
485 122
        return ($pathPrefix ? $pathPrefix . '.' : '') . static::getReferencePrefix($storeAs) . 'id';
0 ignored issues
show
Bug introduced by
Since getReferencePrefix() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of getReferencePrefix() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

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