Completed
Pull Request — master (#1349)
by
unknown
09:34
created

ClassMetadataInfo   F

Complexity

Total Complexity 264

Size/Duplication

Total Lines 1904
Duplicated Lines 3.73 %

Coupling/Cohesion

Components 7
Dependencies 5

Test Coverage

Coverage 82.89%

Importance

Changes 15
Bugs 2 Features 5
Metric Value
wmc 264
c 15
b 2
f 5
lcom 7
cbo 5
dl 71
loc 1904
ccs 407
cts 491
cp 0.8289
rs 0.5217

104 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
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
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
A setDefaultDiscriminatorValue() 0 14 3
A setDiscriminatorValue() 0 5 1
A setSlaveOkay() 0 4 2
A setRequireIndexes() 0 4 1
A getIndexes() 0 4 1
A hasIndexes() 0 4 2
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 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
A getReflectionClass() 0 8 2
B setCustomRepositoryClass() 12 12 5
B invokeLifecycleCallbacks() 0 18 5
C setDiscriminatorField() 0 25 7
B setDiscriminatorMap() 0 19 7
B addIndex() 0 20 6
B setCollection() 0 14 6
F mapField() 49 184 82
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 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() 10 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 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
    /**#@-*/
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: The field name of the document identifier.
194
     */
195
    public $identifier;
196
197
    /**
198
     * READ-ONLY: The field that stores a file reference and indicates the
199
     * document is a file and should be stored on the MongoGridFS.
200
     */
201
    public $file;
202
203
    /**
204
     * READ-ONLY: The field that stores the calculated distance when performing geo spatial
205
     * queries.
206
     */
207
    public $distance;
208
209
    /**
210
     * READ-ONLY: Whether or not reads for this class are okay to read from a slave.
211
     */
212
    public $slaveOkay;
213
214
    /**
215
     * READ-ONLY: The array of indexes for the document collection.
216
     */
217
    public $indexes = array();
218
219
    /**
220
     * READ-ONLY: Whether or not queries on this document should require indexes.
221
     */
222
    public $requireIndexes = false;
223
224
    /**
225
     * READ-ONLY: The name of the document class.
226
     */
227
    public $name;
228
229
    /**
230
     * READ-ONLY: The namespace the document class is contained in.
231
     *
232
     * @var string
233
     * @todo Not really needed. Usage could be localized.
234
     */
235
    public $namespace;
236
237
    /**
238
     * READ-ONLY: The name of the document class that is at the root of the mapped document inheritance
239
     * hierarchy. If the document is not part of a mapped inheritance hierarchy this is the same
240
     * as {@link $documentName}.
241
     *
242
     * @var string
243
     */
244
    public $rootDocumentName;
245
246
    /**
247
     * The name of the custom repository class used for the document class.
248
     * (Optional).
249
     *
250
     * @var string
251
     */
252
    public $customRepositoryClassName;
253
254
    /**
255
     * READ-ONLY: The names of the parent classes (ancestors).
256
     *
257
     * @var array
258
     */
259
    public $parentClasses = array();
260
261
    /**
262
     * READ-ONLY: The names of all subclasses (descendants).
263
     *
264
     * @var array
265
     */
266
    public $subClasses = array();
267
268
    /**
269
     * The ReflectionProperty instances of the mapped class.
270
     *
271
     * @var \ReflectionProperty[]
272
     */
273
    public $reflFields = array();
274
275
    /**
276
     * READ-ONLY: The inheritance mapping type used by the class.
277
     *
278
     * @var integer
279
     */
280
    public $inheritanceType = self::INHERITANCE_TYPE_NONE;
281
282
    /**
283
     * READ-ONLY: The Id generator type used by the class.
284
     *
285
     * @var string
286
     */
287
    public $generatorType = self::GENERATOR_TYPE_AUTO;
288
289
    /**
290
     * READ-ONLY: The Id generator options.
291
     *
292
     * @var array
293
     */
294
    public $generatorOptions = array();
295
296
    /**
297
     * READ-ONLY: The ID generator used for generating IDs for this class.
298
     *
299
     * @var \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator
300
     */
301
    public $idGenerator;
302
303
    /**
304
     * READ-ONLY: The field mappings of the class.
305
     * Keys are field names and values are mapping definitions.
306
     *
307
     * The mapping definition array has the following values:
308
     *
309
     * - <b>fieldName</b> (string)
310
     * The name of the field in the Document.
311
     *
312
     * - <b>id</b> (boolean, optional)
313
     * Marks the field as the primary key of the document. Multiple fields of an
314
     * document can have the id attribute, forming a composite key.
315
     *
316
     * @var array
317
     */
318
    public $fieldMappings = array();
319
320
    /**
321
     * READ-ONLY: The association mappings of the class.
322
     * Keys are field names and values are mapping definitions.
323
     *
324
     * @var array
325
     */
326
    public $associationMappings = array();
327
328
    /**
329
     * READ-ONLY: Array of fields to also load with a given method.
330
     *
331
     * @var array
332
     */
333
    public $alsoLoadMethods = array();
334
335
    /**
336
     * READ-ONLY: The registered lifecycle callbacks for documents of this class.
337
     *
338
     * @var array
339
     */
340
    public $lifecycleCallbacks = array();
341
342
    /**
343
     * READ-ONLY: The discriminator value of this class.
344
     *
345
     * <b>This does only apply to the JOINED and SINGLE_COLLECTION inheritance mapping strategies
346
     * where a discriminator field is used.</b>
347
     *
348
     * @var mixed
349
     * @see discriminatorField
350
     */
351
    public $discriminatorValue;
352
353
    /**
354
     * READ-ONLY: The discriminator map of all mapped classes in the hierarchy.
355
     *
356
     * <b>This does only apply to the SINGLE_COLLECTION inheritance mapping strategy
357
     * where a discriminator field is used.</b>
358
     *
359
     * @var mixed
360
     * @see discriminatorField
361
     */
362
    public $discriminatorMap = array();
363
364
    /**
365
     * READ-ONLY: The definition of the discriminator field used in SINGLE_COLLECTION
366
     * inheritance mapping.
367
     *
368
     * @var string
369
     */
370
    public $discriminatorField;
371
372
    /**
373
     * READ-ONLY: The default value for discriminatorField in case it's not set in the document
374
     *
375
     * @var string
376
     * @see discriminatorField
377
     */
378
    public $defaultDiscriminatorValue;
379
380
    /**
381
     * READ-ONLY: Whether this class describes the mapping of a mapped superclass.
382
     *
383
     * @var boolean
384
     */
385
    public $isMappedSuperclass = false;
386
387
    /**
388
     * READ-ONLY: Whether this class describes the mapping of a embedded document.
389
     *
390
     * @var boolean
391
     */
392
    public $isEmbeddedDocument = false;
393
394
    /**
395
     * READ-ONLY: The policy used for change-tracking on entities of this class.
396
     *
397
     * @var integer
398
     */
399
    public $changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
400
401
    /**
402
     * READ-ONLY: A flag for whether or not instances of this class are to be versioned
403
     * with optimistic locking.
404
     *
405
     * @var boolean $isVersioned
406
     */
407
    public $isVersioned;
408
409
    /**
410
     * READ-ONLY: The name of the field which is used for versioning in optimistic locking (if any).
411
     *
412
     * @var mixed $versionField
413
     */
414
    public $versionField;
415
416
    /**
417
     * READ-ONLY: A flag for whether or not instances of this class are to allow pessimistic
418
     * locking.
419
     *
420
     * @var boolean $isLockable
421
     */
422
    public $isLockable;
423
424
    /**
425
     * READ-ONLY: The name of the field which is used for locking a document.
426
     *
427
     * @var mixed $lockField
428
     */
429
    public $lockField;
430
431
    /**
432
     * The ReflectionClass instance of the mapped class.
433
     *
434
     * @var \ReflectionClass
435
     */
436
    public $reflClass;
437
438
    /**
439
     * Initializes a new ClassMetadata instance that will hold the object-document mapping
440
     * metadata of the class with the given name.
441
     *
442
     * @param string $documentName The name of the document class the new instance is used for.
443
     */
444 870
    public function __construct($documentName)
445
    {
446 870
        $this->name = $documentName;
447 870
        $this->rootDocumentName = $documentName;
448 870
    }
449
450
    /**
451
     * {@inheritDoc}
452
     */
453 819
    public function getReflectionClass()
454
    {
455 819
        if ( ! $this->reflClass) {
456 2
            $this->reflClass = new \ReflectionClass($this->name);
457
        }
458
459 819
        return $this->reflClass;
460
    }
461
462
    /**
463
     * {@inheritDoc}
464
     */
465 301
    public function isIdentifier($fieldName)
466
    {
467 301
        return $this->identifier === $fieldName;
468
    }
469
470
    /**
471
     * INTERNAL:
472
     * Sets the mapped identifier field of this class.
473
     *
474
     * @param string $identifier
475
     */
476 338
    public function setIdentifier($identifier)
477
    {
478 338
        $this->identifier = $identifier;
479 338
    }
480
481
    /**
482
     * {@inheritDoc}
483
     *
484
     * Since MongoDB only allows exactly one identifier field
485
     * this will always return an array with only one value
486
     */
487 26
    public function getIdentifier()
488
    {
489 26
        return array($this->identifier);
490
    }
491
492
    /**
493
     * {@inheritDoc}
494
     *
495
     * Since MongoDB only allows exactly one identifier field
496
     * this will always return an array with only one value
497
     */
498 93
    public function getIdentifierFieldNames()
499
    {
500 93
        return array($this->identifier);
501
    }
502
503
    /**
504
     * {@inheritDoc}
505
     */
506 525
    public function hasField($fieldName)
507
    {
508 525
        return isset($this->fieldMappings[$fieldName]);
509
    }
510
511
    /**
512
     * Sets the inheritance type used by the class and it's subclasses.
513
     *
514
     * @param integer $type
515
     */
516 349
    public function setInheritanceType($type)
517
    {
518 349
        $this->inheritanceType = $type;
519 349
    }
520
521
    /**
522
     * Checks whether a mapped field is inherited from an entity superclass.
523
     *
524
     * @param  string $fieldName
525
     *
526
     * @return boolean TRUE if the field is inherited, FALSE otherwise.
527
     */
528 819
    public function isInheritedField($fieldName)
529
    {
530 819
        return isset($this->fieldMappings[$fieldName]['inherited']);
531
    }
532
533
    /**
534
     * Registers a custom repository class for the document class.
535
     *
536
     * @param string $repositoryClassName The class name of the custom repository.
537
     */
538 293 View Code Duplication
    public function setCustomRepositoryClass($repositoryClassName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
539
    {
540 293
        if ($this->isEmbeddedDocument) {
541
            return;
542
        }
543
544 293
        if ($repositoryClassName && strpos($repositoryClassName, '\\') === false && strlen($this->namespace)) {
545 3
            $repositoryClassName = $this->namespace . '\\' . $repositoryClassName;
546
        }
547
548 293
        $this->customRepositoryClassName = $repositoryClassName;
549 293
    }
550
551
    /**
552
     * Dispatches the lifecycle event of the given document by invoking all
553
     * registered callbacks.
554
     *
555
     * @param string $event     Lifecycle event
556
     * @param object $document  Document on which the event occurred
557
     * @param array  $arguments Arguments to pass to all callbacks
558
     * @throws \InvalidArgumentException if document class is not this class or
559
     *                                   a Proxy of this class
560
     */
561 602
    public function invokeLifecycleCallbacks($event, $document, array $arguments = null)
562
    {
563 602
        if ( ! $document instanceof $this->name) {
564 1
            throw new \InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document)));
565
        }
566
567 601
        if (empty($this->lifecycleCallbacks[$event])) {
568 588
            return;
569
        }
570
571 184
        foreach ($this->lifecycleCallbacks[$event] as $callback) {
572 184
            if ($arguments !== null) {
573 183
                call_user_func_array(array($document, $callback), $arguments);
574
            } else {
575 184
                $document->$callback();
576
            }
577
        }
578 184
    }
579
580
    /**
581
     * Checks whether the class has callbacks registered for a lifecycle event.
582
     *
583
     * @param string $event Lifecycle event
584
     *
585
     * @return boolean
586
     */
587
    public function hasLifecycleCallbacks($event)
588
    {
589
        return ! empty($this->lifecycleCallbacks[$event]);
590
    }
591
592
    /**
593
     * Gets the registered lifecycle callbacks for an event.
594
     *
595
     * @param string $event
596
     * @return array
597
     */
598
    public function getLifecycleCallbacks($event)
599
    {
600
        return isset($this->lifecycleCallbacks[$event]) ? $this->lifecycleCallbacks[$event] : array();
601
    }
602
603
    /**
604
     * Adds a lifecycle callback for documents of this class.
605
     *
606
     * If the callback is already registered, this is a NOOP.
607
     *
608
     * @param string $callback
609
     * @param string $event
610
     */
611 274
    public function addLifecycleCallback($callback, $event)
612
    {
613 274
        if (isset($this->lifecycleCallbacks[$event]) && in_array($callback, $this->lifecycleCallbacks[$event])) {
614 1
            return;
615
        }
616
617 274
        $this->lifecycleCallbacks[$event][] = $callback;
618 274
    }
619
620
    /**
621
     * Sets the lifecycle callbacks for documents of this class.
622
     *
623
     * Any previously registered callbacks are overwritten.
624
     *
625
     * @param array $callbacks
626
     */
627 337
    public function setLifecycleCallbacks(array $callbacks)
628
    {
629 337
        $this->lifecycleCallbacks = $callbacks;
630 337
    }
631
632
    /**
633
     * Registers a method for loading document data before field hydration.
634
     *
635
     * Note: A method may be registered multiple times for different fields.
636
     * it will be invoked only once for the first field found.
637
     *
638
     * @param string       $method Method name
639
     * @param array|string $fields Database field name(s)
640
     */
641 15
    public function registerAlsoLoadMethod($method, $fields)
642
    {
643 15
        $this->alsoLoadMethods[$method] = is_array($fields) ? $fields : array($fields);
644 15
    }
645
646
    /**
647
     * Sets the AlsoLoad methods for documents of this class.
648
     *
649
     * Any previously registered methods are overwritten.
650
     *
651
     * @param array $methods
652
     */
653 337
    public function setAlsoLoadMethods(array $methods)
654
    {
655 337
        $this->alsoLoadMethods = $methods;
656 337
    }
657
658
    /**
659
     * Sets the discriminator field.
660
     *
661
     * The field name is the the unmapped database field. Discriminator values
662
     * are only used to discern the hydration class and are not mapped to class
663
     * properties.
664
     *
665
     * @param string $discriminatorField
666
     *
667
     * @throws MappingException If the discriminator field conflicts with the
668
     *                          "name" attribute of a mapped field.
669
     */
670 358
    public function setDiscriminatorField($discriminatorField)
671
    {
672 358
        if ($discriminatorField === null) {
673 298
            $this->discriminatorField = null;
674
675 298
            return;
676
        }
677
678
        // Handle array argument with name/fieldName keys for BC
679 115
        if (is_array($discriminatorField)) {
680
            if (isset($discriminatorField['name'])) {
681
                $discriminatorField = $discriminatorField['name'];
682
            } elseif (isset($discriminatorField['fieldName'])) {
683
                $discriminatorField = $discriminatorField['fieldName'];
684
            }
685
        }
686
687 115
        foreach ($this->fieldMappings as $fieldMapping) {
688 4
            if ($discriminatorField == $fieldMapping['name']) {
689 4
                throw MappingException::discriminatorFieldConflict($this->name, $discriminatorField);
690
            }
691
        }
692
693 114
        $this->discriminatorField = $discriminatorField;
694 114
    }
695
696
    /**
697
     * Sets the discriminator values used by this class.
698
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies.
699
     *
700
     * @param array $map
701
     *
702
     * @throws MappingException
703
     */
704 354
    public function setDiscriminatorMap(array $map)
705
    {
706 354
        foreach ($map as $value => $className) {
707 113
            if (strpos($className, '\\') === false && strlen($this->namespace)) {
708 81
                $className = $this->namespace . '\\' . $className;
709
            }
710 113
            $this->discriminatorMap[$value] = $className;
711 113
            if ($this->name == $className) {
712 105
                $this->discriminatorValue = $value;
713
            } else {
714 105
                if ( ! class_exists($className)) {
715
                    throw MappingException::invalidClassInDiscriminatorMap($className, $this->name);
716
                }
717 105
                if (is_subclass_of($className, $this->name)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if $this->name can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
718 113
                    $this->subClasses[] = $className;
719
                }
720
            }
721
        }
722 354
    }
723
724
    /**
725
     * Sets the default discriminator value to be used for this class
726
     * Used for JOINED and SINGLE_TABLE inheritance mapping strategies if the document has no discriminator value
727
     *
728
     * @param string $defaultDiscriminatorValue
729
     *
730
     * @throws MappingException
731
     */
732 343
    public function setDefaultDiscriminatorValue($defaultDiscriminatorValue)
733
    {
734 343
        if ($defaultDiscriminatorValue === null) {
735 337
            $this->defaultDiscriminatorValue = null;
736
737 337
            return;
738
        }
739
740 53
        if (!array_key_exists($defaultDiscriminatorValue, $this->discriminatorMap)) {
741
            throw MappingException::invalidDiscriminatorValue($defaultDiscriminatorValue, $this->name);
742
        }
743
744 53
        $this->defaultDiscriminatorValue = $defaultDiscriminatorValue;
745 53
    }
746
747
    /**
748
     * Sets the discriminator value for this class.
749
     * Used for JOINED/SINGLE_TABLE inheritance and multiple document types in a single
750
     * collection.
751
     *
752
     * @param string $value
753
     */
754
    public function setDiscriminatorValue($value)
755
    {
756
        $this->discriminatorMap[$value] = $this->name;
757
        $this->discriminatorValue = $value;
758
    }
759
760
    /**
761
     * Sets the slaveOkay option applied to collections for this class.
762
     *
763
     * @param boolean|null $slaveOkay
764
     */
765 3
    public function setSlaveOkay($slaveOkay)
766
    {
767 3
        $this->slaveOkay = $slaveOkay === null ? null : (boolean) $slaveOkay;
768 3
    }
769
770
    /**
771
     * Add a index for this Document.
772
     *
773
     * @param array $keys Array of keys for the index.
774
     * @param array $options Array of options for the index.
775
     */
776 220
    public function addIndex($keys, array $options = array())
777
    {
778 220
        $this->indexes[] = array(
779
            'keys' => array_map(function($value) {
780 220
                if ($value == 1 || $value == -1) {
781 55
                    return (int) $value;
782
                }
783 212
                if (is_string($value)) {
784 212
                    $lower = strtolower($value);
785 212
                    if ($lower === 'asc') {
786 205
                        return 1;
787 10
                    } elseif ($lower === 'desc') {
788 3
                        return -1;
789
                    }
790
                }
791 7
                return $value;
792 220
            }, $keys),
793 220
            'options' => $options
794
        );
795 220
    }
796
797
    /**
798
     * Set whether or not queries on this document should require indexes.
799
     *
800
     * @param bool $requireIndexes
801
     */
802 811
    public function setRequireIndexes($requireIndexes)
803
    {
804 811
        $this->requireIndexes = $requireIndexes;
805 811
    }
806
807
    /**
808
     * Returns the array of indexes for this Document.
809
     *
810
     * @return array $indexes The array of indexes.
811
     */
812 53
    public function getIndexes()
813
    {
814 53
        return $this->indexes;
815
    }
816
817
    /**
818
     * Checks whether this document has indexes or not.
819
     *
820
     * @return boolean
821
     */
822
    public function hasIndexes()
823
    {
824
        return $this->indexes ? true : false;
825
    }
826
827
    /**
828
     * Sets the change tracking policy used by this class.
829
     *
830
     * @param integer $policy
831
     */
832 342
    public function setChangeTrackingPolicy($policy)
833
    {
834 342
        $this->changeTrackingPolicy = $policy;
835 342
    }
836
837
    /**
838
     * Whether the change tracking policy of this class is "deferred explicit".
839
     *
840
     * @return boolean
841
     */
842 62
    public function isChangeTrackingDeferredExplicit()
843
    {
844 62
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
845
    }
846
847
    /**
848
     * Whether the change tracking policy of this class is "deferred implicit".
849
     *
850
     * @return boolean
851
     */
852 577
    public function isChangeTrackingDeferredImplicit()
853
    {
854 577
        return $this->changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
855
    }
856
857
    /**
858
     * Whether the change tracking policy of this class is "notify".
859
     *
860
     * @return boolean
861
     */
862 334
    public function isChangeTrackingNotify()
863
    {
864 334
        return $this->changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
865
    }
866
867
    /**
868
     * Gets the ReflectionProperties of the mapped class.
869
     *
870
     * @return array An array of ReflectionProperty instances.
871
     */
872 93
    public function getReflectionProperties()
873
    {
874 93
        return $this->reflFields;
875
    }
876
877
    /**
878
     * Gets a ReflectionProperty for a specific field of the mapped class.
879
     *
880
     * @param string $name
881
     *
882
     * @return \ReflectionProperty
883
     */
884
    public function getReflectionProperty($name)
885
    {
886
        return $this->reflFields[$name];
887
    }
888
889
    /**
890
     * {@inheritDoc}
891
     */
892 816
    public function getName()
893
    {
894 816
        return $this->name;
895
    }
896
897
    /**
898
     * The namespace this Document class belongs to.
899
     *
900
     * @return string $namespace The namespace name.
901
     */
902
    public function getNamespace()
903
    {
904
        return $this->namespace;
905
    }
906
907
    /**
908
     * Returns the database this Document is mapped to.
909
     *
910
     * @return string $db The database name.
911
     */
912 755
    public function getDatabase()
913
    {
914 755
        return $this->db;
915
    }
916
917
    /**
918
     * Set the database this Document is mapped to.
919
     *
920
     * @param string $db The database name
921
     */
922 93
    public function setDatabase($db)
923
    {
924 93
        $this->db = $db;
925 93
    }
926
927
    /**
928
     * Get the collection this Document is mapped to.
929
     *
930
     * @return string $collection The collection name.
931
     */
932 759
    public function getCollection()
933
    {
934 759
        return $this->collection;
935
    }
936
937
    /**
938
     * Sets the collection this Document is mapped to.
939
     *
940
     * @param array|string $name
941
     *
942
     * @throws \InvalidArgumentException
943
     */
944 849
    public function setCollection($name)
945
    {
946 849
        if (is_array($name)) {
947
            if ( ! isset($name['name'])) {
948
                throw new \InvalidArgumentException('A name key is required when passing an array to setCollection()');
949
            }
950
            $this->collectionCapped = isset($name['capped']) ? $name['capped'] : false;
951
            $this->collectionSize = isset($name['size']) ? $name['size'] : 0;
952
            $this->collectionMax = isset($name['max']) ? $name['max'] : 0;
953
            $this->collection = $name['name'];
954
        } else {
955 849
            $this->collection = $name;
956
        }
957 849
    }
958
959
    /**
960
     * Get whether or not the documents collection is capped.
961
     *
962
     * @return boolean
963
     */
964 4
    public function getCollectionCapped()
965
    {
966 4
        return $this->collectionCapped;
967
    }
968
969
    /**
970
     * Set whether or not the documents collection is capped.
971
     *
972
     * @param boolean $bool
973
     */
974 1
    public function setCollectionCapped($bool)
975
    {
976 1
        $this->collectionCapped = $bool;
977 1
    }
978
979
    /**
980
     * Get the collection size
981
     *
982
     * @return integer
983
     */
984 4
    public function getCollectionSize()
985
    {
986 4
        return $this->collectionSize;
987
    }
988
989
    /**
990
     * Set the collection size.
991
     *
992
     * @param integer $size
993
     */
994 1
    public function setCollectionSize($size)
995
    {
996 1
        $this->collectionSize = $size;
997 1
    }
998
999
    /**
1000
     * Get the collection max.
1001
     *
1002
     * @return integer
1003
     */
1004 4
    public function getCollectionMax()
1005
    {
1006 4
        return $this->collectionMax;
1007
    }
1008
1009
    /**
1010
     * Set the collection max.
1011
     *
1012
     * @param integer $max
1013
     */
1014 1
    public function setCollectionMax($max)
1015
    {
1016 1
        $this->collectionMax = $max;
1017 1
    }
1018
1019
    /**
1020
     * Returns TRUE if this Document is mapped to a collection FALSE otherwise.
1021
     *
1022
     * @return boolean
1023
     */
1024
    public function isMappedToCollection()
1025
    {
1026
        return $this->collection ? true : false;
1027
    }
1028
1029
    /**
1030
     * Returns TRUE if this Document is a file to be stored on the MongoGridFS FALSE otherwise.
1031
     *
1032
     * @return boolean
1033
     */
1034 708
    public function isFile()
1035
    {
1036 708
        return $this->file ? true : false;
1037
    }
1038
1039
    /**
1040
     * Returns the file field name.
1041
     *
1042
     * @return string $file The file field name.
1043
     */
1044 337
    public function getFile()
1045
    {
1046 337
        return $this->file;
1047
    }
1048
1049
    /**
1050
     * Set the field name that stores the grid file.
1051
     *
1052
     * @param string $file
1053
     */
1054 338
    public function setFile($file)
1055
    {
1056 338
        $this->file = $file;
1057 338
    }
1058
1059
    /**
1060
     * Returns the distance field name.
1061
     *
1062
     * @return string $distance The distance field name.
1063
     */
1064
    public function getDistance()
1065
    {
1066
        return $this->distance;
1067
    }
1068
1069
    /**
1070
     * Set the field name that stores the distance.
1071
     *
1072
     * @param string $distance
1073
     */
1074 1
    public function setDistance($distance)
1075
    {
1076 1
        $this->distance = $distance;
1077 1
    }
1078
1079
    /**
1080
     * Map a field.
1081
     *
1082
     * @param array $mapping The mapping information.
1083
     *
1084
     * @return array
1085
     *
1086
     * @throws MappingException
1087
     */
1088 856
    public function mapField(array $mapping)
1089
    {
1090 856
        if ( ! isset($mapping['fieldName']) && isset($mapping['name'])) {
1091 8
            $mapping['fieldName'] = $mapping['name'];
1092
        }
1093 856
        if ( ! isset($mapping['fieldName'])) {
1094
            throw MappingException::missingFieldName($this->name);
1095
        }
1096 856
        if ( ! isset($mapping['name'])) {
1097 848
            $mapping['name'] = $mapping['fieldName'];
1098
        }
1099 856
        if ($this->identifier === $mapping['name'] && empty($mapping['id'])) {
1100 1
            throw MappingException::mustNotChangeIdentifierFieldsType($this->name, $mapping['name']);
1101
        }
1102 855
        if (isset($this->fieldMappings[$mapping['fieldName']])) {
1103
            //throw MappingException::duplicateFieldMapping($this->name, $mapping['fieldName']);
1104
        }
1105 855
        if ($this->discriminatorField !== null && $this->discriminatorField == $mapping['name']) {
1106 1
            throw MappingException::discriminatorFieldConflict($this->name, $this->discriminatorField);
1107
        }
1108 854 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...
1109 549
            $mapping['targetDocument'] = $this->namespace . '\\' . $mapping['targetDocument'];
1110
        }
1111 854
        if (isset($mapping['collectionClass'])) {
1112 54 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...
1113 53
                $mapping['collectionClass'] = $this->namespace . '\\' . $mapping['collectionClass'];
1114
            }
1115 54
            $mapping['collectionClass'] = ltrim($mapping['collectionClass'], '\\');
1116
        }
1117 854
        if ( ! empty($mapping['collectionClass'])) {
1118 54
            $rColl = new \ReflectionClass($mapping['collectionClass']);
1119 54
            if ( ! $rColl->implementsInterface('Doctrine\\Common\\Collections\\Collection')) {
1120 1
                throw MappingException::collectionClassDoesNotImplementCommonInterface($this->name, $mapping['fieldName'], $mapping['collectionClass']);
1121
            }
1122
        }
1123
1124 853
        if (isset($mapping['discriminatorMap'])) {
1125 103
            foreach ($mapping['discriminatorMap'] as $key => $class) {
1126 103 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...
1127 103
                    $mapping['discriminatorMap'][$key] = $this->namespace . '\\' . $class;
1128
                }
1129
            }
1130
        }
1131
1132 853
        if (isset($mapping['cascade']) && isset($mapping['embedded'])) {
1133 1
            throw MappingException::cascadeOnEmbeddedNotAllowed($this->name, $mapping['fieldName']);
1134
        }
1135
1136 852
        $cascades = isset($mapping['cascade']) ? array_map('strtolower', (array) $mapping['cascade']) : array();
1137
1138 852
        if (in_array('all', $cascades) || isset($mapping['embedded'])) {
1139 574
            $cascades = array('remove', 'persist', 'refresh', 'merge', 'detach');
1140
        }
1141
1142 852
        if (isset($mapping['embedded'])) {
1143 539
            unset($mapping['cascade']);
1144 849
        } elseif (isset($mapping['cascade'])) {
1145 373
            $mapping['cascade'] = $cascades;
1146
        }
1147
1148 852
        $mapping['isCascadeRemove'] = in_array('remove', $cascades);
1149 852
        $mapping['isCascadePersist'] = in_array('persist', $cascades);
1150 852
        $mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
1151 852
        $mapping['isCascadeMerge'] = in_array('merge', $cascades);
1152 852
        $mapping['isCascadeDetach'] = in_array('detach', $cascades);
1153
1154 852
        if (isset($mapping['type']) && $mapping['type'] === 'file') {
1155 58
            $mapping['file'] = true;
1156
        }
1157 852
        if (isset($mapping['type']) && $mapping['type'] === 'increment') {
1158 1
            $mapping['strategy'] = self::STORAGE_STRATEGY_INCREMENT;
1159
        }
1160 852 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...
1161 58
            $this->file = $mapping['fieldName'];
1162 58
            $mapping['name'] = 'file';
1163
        }
1164 852 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...
1165 7
            $this->distance = $mapping['fieldName'];
1166
        }
1167 852
        if (isset($mapping['id']) && $mapping['id'] === true) {
1168 832
            $mapping['name'] = '_id';
1169 832
            $this->identifier = $mapping['fieldName'];
1170 832 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...
1171 817
                $this->generatorType = constant(ClassMetadata::class . '::GENERATOR_TYPE_' . strtoupper($mapping['strategy']));
1172
            }
1173 832
            $this->generatorOptions = isset($mapping['options']) ? $mapping['options'] : array();
1174 832
            switch ($this->generatorType) {
1175 832
                case self::GENERATOR_TYPE_AUTO:
1176 765
                    $mapping['type'] = 'id';
1177 765
                    break;
1178
                default:
1179 149
                    if ( ! empty($this->generatorOptions['type'])) {
1180 52
                        $mapping['type'] = $this->generatorOptions['type'];
1181 97
                    } elseif (empty($mapping['type'])) {
1182 74
                        $mapping['type'] = $this->generatorType === self::GENERATOR_TYPE_INCREMENT ? 'int_id' : 'custom_id';
1183
                    }
1184
            }
1185 832
            unset($this->generatorOptions['type']);
1186
        }
1187
1188 852
        if ( ! isset($mapping['nullable'])) {
1189 40
            $mapping['nullable'] = false;
1190
        }
1191
1192
        // Synchronize the "simple" and "storeAs" mapping information for backwards compatibility
1193 852 View Code Duplication
        if (isset($mapping['simple']) && $mapping['simple'] === 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...
1194 294
            $mapping['storeAs'] = ClassMetadataInfo::REFERENCE_STORE_AS_ID;
1195
        }
1196 852 View Code Duplication
        if (isset($mapping['storeAs']) && $mapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID) {
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...
1197 294
            $mapping['simple'] = true;
1198
        }
1199
1200 852
        if (isset($mapping['reference']) && ! empty($mapping['simple']) && ! isset($mapping['targetDocument'])) {
1201 1
            throw MappingException::simpleReferenceRequiresTargetDocument($this->name, $mapping['fieldName']);
1202
        }
1203
1204 851
        if (isset($mapping['reference']) && empty($mapping['targetDocument']) && empty($mapping['discriminatorMap']) &&
1205 851
                (isset($mapping['mappedBy']) || isset($mapping['inversedBy']))) {
1206 4
            throw MappingException::owningAndInverseReferencesRequireTargetDocument($this->name, $mapping['fieldName']);
1207
        }
1208
1209 847
        if ($this->isEmbeddedDocument && $mapping['type'] === 'many' && CollectionHelper::isAtomic($mapping['strategy'])) {
1210 1
            throw MappingException::atomicCollectionStrategyNotAllowed($mapping['strategy'], $this->name, $mapping['fieldName']);
1211
        }
1212
1213 846 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...
1214 463
            $mapping['association'] = self::REFERENCE_ONE;
1215
        }
1216 846 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...
1217 412
            $mapping['association'] = self::REFERENCE_MANY;
1218
        }
1219 846 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...
1220 413
            $mapping['association'] = self::EMBED_ONE;
1221
        }
1222 846 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...
1223 470
            $mapping['association'] = self::EMBED_MANY;
1224
        }
1225
1226 846
        if (isset($mapping['association']) && ! isset($mapping['targetDocument']) && ! isset($mapping['discriminatorField'])) {
1227 107
            $mapping['discriminatorField'] = self::DEFAULT_DISCRIMINATOR_FIELD;
1228
        }
1229
1230
        /*
1231
        if (isset($mapping['type']) && ($mapping['type'] === 'one' || $mapping['type'] === 'many')) {
1232
            $mapping['type'] = $mapping['type'] === 'one' ? self::ONE : self::MANY;
1233
        }
1234
        */
1235 846
        if (isset($mapping['version'])) {
1236 93
            $mapping['notSaved'] = true;
1237 93
            $this->setVersionMapping($mapping);
1238
        }
1239 846
        if (isset($mapping['lock'])) {
1240 26
            $mapping['notSaved'] = true;
1241 26
            $this->setLockMapping($mapping);
1242
        }
1243 846
        $mapping['isOwningSide'] = true;
1244 846
        $mapping['isInverseSide'] = false;
1245 846
        if (isset($mapping['reference'])) {
1246 526 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...
1247 221
                $mapping['isOwningSide'] = true;
1248 221
                $mapping['isInverseSide'] = false;
1249
            }
1250 526 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...
1251 266
                $mapping['isInverseSide'] = true;
1252 266
                $mapping['isOwningSide'] = false;
1253
            }
1254 526 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...
1255 201
                $mapping['isInverseSide'] = true;
1256 201
                $mapping['isOwningSide'] = false;
1257
            }
1258 526
            if (!isset($mapping['orphanRemoval'])) {
1259 506
                $mapping['orphanRemoval'] = false;
1260
            }
1261
        }
1262
1263 846
        $this->applyStorageStrategy($mapping);
1264
1265 845
        $this->fieldMappings[$mapping['fieldName']] = $mapping;
1266 845
        if (isset($mapping['association'])) {
1267 665
            $this->associationMappings[$mapping['fieldName']] = $mapping;
1268
        }
1269
1270 845
        return $mapping;
1271
    }
1272
1273
    /**
1274
     * Validates the storage strategy of a mapping for consistency
1275
     * @param array $mapping
1276
     * @throws \Doctrine\ODM\MongoDB\Mapping\MappingException
1277
     */
1278 846
    private function applyStorageStrategy(array &$mapping)
1279
    {
1280 846
        if (! isset($mapping['type']) || isset($mapping['id'])) {
1281 834
            return;
1282
        }
1283
1284
        switch (true) {
1285 814
            case $mapping['type'] == 'int':
1286 814
            case $mapping['type'] == 'float':
1287 814
            case $mapping['type'] == 'increment':
1288 307
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1289 307
                $allowedStrategies = [self::STORAGE_STRATEGY_SET, self::STORAGE_STRATEGY_INCREMENT];
1290 307
                break;
1291
1292 813
            case $mapping['type'] == 'many':
1293 559
                $defaultStrategy = CollectionHelper::DEFAULT_STRATEGY;
1294
                $allowedStrategies = [
1295 559
                    self::STORAGE_STRATEGY_PUSH_ALL,
1296 559
                    self::STORAGE_STRATEGY_ADD_TO_SET,
1297 559
                    self::STORAGE_STRATEGY_SET,
1298 559
                    self::STORAGE_STRATEGY_SET_ARRAY,
1299 559
                    self::STORAGE_STRATEGY_ATOMIC_SET,
1300 559
                    self::STORAGE_STRATEGY_ATOMIC_SET_ARRAY,
1301
                ];
1302 559
                break;
1303
1304
            default:
1305 807
                $defaultStrategy = self::STORAGE_STRATEGY_SET;
1306 807
                $allowedStrategies = [self::STORAGE_STRATEGY_SET];
1307
        }
1308
1309 814
        if (! isset($mapping['strategy'])) {
1310 808
            $mapping['strategy'] = $defaultStrategy;
1311
        }
1312
1313 814
        if (! in_array($mapping['strategy'], $allowedStrategies)) {
1314
            throw MappingException::invalidStorageStrategy($this->name, $mapping['fieldName'], $mapping['type'], $mapping['strategy']);
1315
        }
1316
1317 814
        if (isset($mapping['reference']) && $mapping['type'] === 'many' && $mapping['isOwningSide']
1318 814
            && ! empty($mapping['sort']) && ! CollectionHelper::usesSet($mapping['strategy'])) {
1319 1
            throw MappingException::referenceManySortMustNotBeUsedWithNonSetCollectionStrategy($this->name, $mapping['fieldName'], $mapping['strategy']);
1320
        }
1321 813
    }
1322
1323
    /**
1324
     * Map a MongoGridFSFile.
1325
     *
1326
     * @param array $mapping The mapping information.
1327
     */
1328
    public function mapFile(array $mapping)
1329
    {
1330
        $mapping['file'] = true;
1331
        $mapping['type'] = 'file';
1332
        $this->mapField($mapping);
1333
    }
1334
1335
    /**
1336
     * Map a single embedded document.
1337
     *
1338
     * @param array $mapping The mapping information.
1339
     */
1340 6
    public function mapOneEmbedded(array $mapping)
1341
    {
1342 6
        $mapping['embedded'] = true;
1343 6
        $mapping['type'] = 'one';
1344 6
        $this->mapField($mapping);
1345 5
    }
1346
1347
    /**
1348
     * Map a collection of embedded documents.
1349
     *
1350
     * @param array $mapping The mapping information.
1351
     */
1352 3
    public function mapManyEmbedded(array $mapping)
1353
    {
1354 3
        $mapping['embedded'] = true;
1355 3
        $mapping['type'] = 'many';
1356 3
        $this->mapField($mapping);
1357 3
    }
1358
1359
    /**
1360
     * Map a single document reference.
1361
     *
1362
     * @param array $mapping The mapping information.
1363
     */
1364 8
    public function mapOneReference(array $mapping)
1365
    {
1366 8
        $mapping['reference'] = true;
1367 8
        $mapping['type'] = 'one';
1368 8
        $this->mapField($mapping);
1369 8
    }
1370
1371
    /**
1372
     * Map a collection of document references.
1373
     *
1374
     * @param array $mapping The mapping information.
1375
     */
1376 8
    public function mapManyReference(array $mapping)
1377
    {
1378 8
        $mapping['reference'] = true;
1379 8
        $mapping['type'] = 'many';
1380 8
        $this->mapField($mapping);
1381 8
    }
1382
1383
    /**
1384
     * INTERNAL:
1385
     * Adds a field mapping without completing/validating it.
1386
     * This is mainly used to add inherited field mappings to derived classes.
1387
     *
1388
     * @param array $fieldMapping
1389
     */
1390 115
    public function addInheritedFieldMapping(array $fieldMapping)
1391
    {
1392 115
        $this->fieldMappings[$fieldMapping['fieldName']] = $fieldMapping;
1393
1394 115
        if (isset($fieldMapping['association'])) {
1395 76
            $this->associationMappings[$fieldMapping['fieldName']] = $fieldMapping;
1396
        }
1397 115
    }
1398
1399
    /**
1400
     * INTERNAL:
1401
     * Adds an association mapping without completing/validating it.
1402
     * This is mainly used to add inherited association mappings to derived classes.
1403
     *
1404
     * @param array $mapping
1405
     *
1406
     * @return void
1407
     *
1408
     * @throws MappingException
1409
     */
1410 77
    public function addInheritedAssociationMapping(array $mapping/*, $owningClassName = null*/)
1411
    {
1412 77
        $this->associationMappings[$mapping['fieldName']] = $mapping;
1413 77
    }
1414
1415
    /**
1416
     * Checks whether the class has a mapped association with the given field name.
1417
     *
1418
     * @param string $fieldName
1419
     * @return boolean
1420
     */
1421 7
    public function hasReference($fieldName)
1422
    {
1423 7
        return isset($this->fieldMappings[$fieldName]['reference']);
1424
    }
1425
1426
    /**
1427
     * Checks whether the class has a mapped embed with the given field name.
1428
     *
1429
     * @param string $fieldName
1430
     * @return boolean
1431
     */
1432 5
    public function hasEmbed($fieldName)
1433
    {
1434 5
        return isset($this->fieldMappings[$fieldName]['embedded']);
1435
    }
1436
1437
    /**
1438
     * {@inheritDoc}
1439
     *
1440
     * Checks whether the class has a mapped association (embed or reference) with the given field name.
1441
     */
1442 7
    public function hasAssociation($fieldName)
1443
    {
1444 7
        return $this->hasReference($fieldName) || $this->hasEmbed($fieldName);
1445
    }
1446
1447
    /**
1448
     * {@inheritDoc}
1449
     *
1450
     * Checks whether the class has a mapped reference or embed for the specified field and
1451
     * is a single valued association.
1452
     */
1453
    public function isSingleValuedAssociation($fieldName)
1454
    {
1455
        return $this->isSingleValuedReference($fieldName) || $this->isSingleValuedEmbed($fieldName);
1456
    }
1457
1458
    /**
1459
     * {@inheritDoc}
1460
     *
1461
     * Checks whether the class has a mapped reference or embed for the specified field and
1462
     * is a collection valued association.
1463
     */
1464
    public function isCollectionValuedAssociation($fieldName)
1465
    {
1466
        return $this->isCollectionValuedReference($fieldName) || $this->isCollectionValuedEmbed($fieldName);
1467
    }
1468
1469
    /**
1470
     * Checks whether the class has a mapped association for the specified field
1471
     * and if yes, checks whether it is a single-valued association (to-one).
1472
     *
1473
     * @param string $fieldName
1474
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1475
     */
1476
    public function isSingleValuedReference($fieldName)
1477
    {
1478
        return isset($this->fieldMappings[$fieldName]['association']) &&
1479
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_ONE;
1480
    }
1481
1482
    /**
1483
     * Checks whether the class has a mapped association for the specified field
1484
     * and if yes, checks whether it is a collection-valued association (to-many).
1485
     *
1486
     * @param string $fieldName
1487
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1488
     */
1489
    public function isCollectionValuedReference($fieldName)
1490
    {
1491
        return isset($this->fieldMappings[$fieldName]['association']) &&
1492
            $this->fieldMappings[$fieldName]['association'] === self::REFERENCE_MANY;
1493
    }
1494
1495
    /**
1496
     * Checks whether the class has a mapped embedded document for the specified field
1497
     * and if yes, checks whether it is a single-valued association (to-one).
1498
     *
1499
     * @param string $fieldName
1500
     * @return boolean TRUE if the association exists and is single-valued, FALSE otherwise.
1501
     */
1502
    public function isSingleValuedEmbed($fieldName)
1503
    {
1504
        return isset($this->fieldMappings[$fieldName]['association']) &&
1505
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_ONE;
1506
    }
1507
1508
    /**
1509
     * Checks whether the class has a mapped embedded document for the specified field
1510
     * and if yes, checks whether it is a collection-valued association (to-many).
1511
     *
1512
     * @param string $fieldName
1513
     * @return boolean TRUE if the association exists and is collection-valued, FALSE otherwise.
1514
     */
1515
    public function isCollectionValuedEmbed($fieldName)
1516
    {
1517
        return isset($this->fieldMappings[$fieldName]['association']) &&
1518
            $this->fieldMappings[$fieldName]['association'] === self::EMBED_MANY;
1519
    }
1520
1521
    /**
1522
     * Sets the ID generator used to generate IDs for instances of this class.
1523
     *
1524
     * @param \Doctrine\ODM\MongoDB\Id\AbstractIdGenerator $generator
1525
     */
1526 759
    public function setIdGenerator($generator)
1527
    {
1528 759
        $this->idGenerator = $generator;
1529 759
    }
1530
1531
    /**
1532
     * Casts the identifier to its portable PHP type.
1533
     *
1534
     * @param mixed $id
1535
     * @return mixed $id
1536
     */
1537 594
    public function getPHPIdentifierValue($id)
1538
    {
1539 594
        $idType = $this->fieldMappings[$this->identifier]['type'];
1540 594
        return Type::getType($idType)->convertToPHPValue($id);
1541
    }
1542
1543
    /**
1544
     * Casts the identifier to its database type.
1545
     *
1546
     * @param mixed $id
1547
     * @return mixed $id
1548
     */
1549 656
    public function getDatabaseIdentifierValue($id)
1550
    {
1551 656
        $idType = $this->fieldMappings[$this->identifier]['type'];
1552 656
        return Type::getType($idType)->convertToDatabaseValue($id);
1553
    }
1554
1555
    /**
1556
     * Sets the document identifier of a document.
1557
     *
1558
     * The value will be converted to a PHP type before being set.
1559
     *
1560
     * @param object $document
1561
     * @param mixed $id
1562
     */
1563 527
    public function setIdentifierValue($document, $id)
1564
    {
1565 527
        $id = $this->getPHPIdentifierValue($id);
1566 527
        $this->reflFields[$this->identifier]->setValue($document, $id);
1567 527
    }
1568
1569
    /**
1570
     * Gets the document identifier as a PHP type.
1571
     *
1572
     * @param object $document
1573
     * @return mixed $id
1574
     */
1575 607
    public function getIdentifierValue($document)
1576
    {
1577 607
        return $this->reflFields[$this->identifier]->getValue($document);
1578
    }
1579
1580
    /**
1581
     * {@inheritDoc}
1582
     *
1583
     * Since MongoDB only allows exactly one identifier field this is a proxy
1584
     * to {@see getIdentifierValue()} and returns an array with the identifier
1585
     * field as a key.
1586
     */
1587
    public function getIdentifierValues($object)
1588
    {
1589
        return array($this->identifier => $this->getIdentifierValue($object));
1590
    }
1591
1592
    /**
1593
     * Get the document identifier object as a database type.
1594
     *
1595
     * @param object $document
1596
     *
1597
     * @return \MongoId $id The MongoID object.
1598
     */
1599 32
    public function getIdentifierObject($document)
1600
    {
1601 32
        return $this->getDatabaseIdentifierValue($this->getIdentifierValue($document));
1602
    }
1603
1604
    /**
1605
     * Sets the specified field to the specified value on the given document.
1606
     *
1607
     * @param object $document
1608
     * @param string $field
1609
     * @param mixed $value
1610
     */
1611 7
    public function setFieldValue($document, $field, $value)
1612
    {
1613 7
        if ($document instanceof Proxy && ! $document->__isInitialized()) {
1614
            //property changes to an uninitialized proxy will not be tracked or persisted,
1615
            //so the proxy needs to be loaded first.
1616 1
            $document->__load();
1617
        }
1618
1619 7
        $this->reflFields[$field]->setValue($document, $value);
1620 7
    }
1621
1622
    /**
1623
     * Gets the specified field's value off the given document.
1624
     *
1625
     * @param object $document
1626
     * @param string $field
1627
     *
1628
     * @return mixed
1629
     */
1630 26
    public function getFieldValue($document, $field)
1631
    {
1632 26
        if ($document instanceof Proxy && $field !== $this->identifier && ! $document->__isInitialized()) {
1633 1
            $document->__load();
1634
        }
1635
1636 26
        return $this->reflFields[$field]->getValue($document);
1637
    }
1638
1639
    /**
1640
     * Gets the mapping of a field.
1641
     *
1642
     * @param string $fieldName  The field name.
1643
     *
1644
     * @return array  The field mapping.
1645
     *
1646
     * @throws MappingException if the $fieldName is not found in the fieldMappings array
1647
     *
1648
     * @throws MappingException
1649
     */
1650 90
    public function getFieldMapping($fieldName)
1651
    {
1652 90
        if ( ! isset($this->fieldMappings[$fieldName])) {
1653 6
            throw MappingException::mappingNotFound($this->name, $fieldName);
1654
        }
1655 88
        return $this->fieldMappings[$fieldName];
1656
    }
1657
1658
    /**
1659
     * Gets mappings of fields holding embedded document(s).
1660
     *
1661
     * @return array of field mappings
1662
     */
1663 569
    public function getEmbeddedFieldsMappings()
1664
    {
1665 569
        return array_filter(
1666 569
            $this->associationMappings,
1667
            function($assoc) { return ! empty($assoc['embedded']); }
1668
        );
1669
    }
1670
1671
    /**
1672
     * Check if the field is not null.
1673
     *
1674
     * @param string $fieldName  The field name
1675
     *
1676
     * @return boolean  TRUE if the field is not null, FALSE otherwise.
1677
     */
1678 1
    public function isNullable($fieldName)
1679
    {
1680 1
        $mapping = $this->getFieldMapping($fieldName);
1681 1
        if ($mapping !== false) {
1682 1
            return isset($mapping['nullable']) && $mapping['nullable'] == true;
1683
        }
1684
        return false;
1685
    }
1686
1687
    /**
1688
     * Checks whether the document has a discriminator field and value configured.
1689
     *
1690
     * @return boolean
1691
     */
1692 488
    public function hasDiscriminator()
1693
    {
1694 488
        return isset($this->discriminatorField, $this->discriminatorValue);
1695
    }
1696
1697
    /**
1698
     * Sets the type of Id generator to use for the mapped class.
1699
     *
1700
     * @param string $generatorType Generator type.
1701
     */
1702 343
    public function setIdGeneratorType($generatorType)
1703
    {
1704 343
        $this->generatorType = $generatorType;
1705 343
    }
1706
1707
    /**
1708
     * Sets the Id generator options.
1709
     *
1710
     * @param array $generatorOptions Generator options.
1711
     */
1712
    public function setIdGeneratorOptions($generatorOptions)
1713
    {
1714
        $this->generatorOptions = $generatorOptions;
1715
    }
1716
1717
    /**
1718
     * @return boolean
1719
     */
1720 575
    public function isInheritanceTypeNone()
1721
    {
1722 575
        return $this->inheritanceType == self::INHERITANCE_TYPE_NONE;
1723
    }
1724
1725
    /**
1726
     * Checks whether the mapped class uses the SINGLE_COLLECTION inheritance mapping strategy.
1727
     *
1728
     * @return boolean
1729
     */
1730 337
    public function isInheritanceTypeSingleCollection()
1731
    {
1732 337
        return $this->inheritanceType == self::INHERITANCE_TYPE_SINGLE_COLLECTION;
1733
    }
1734
1735
    /**
1736
     * Checks whether the mapped class uses the COLLECTION_PER_CLASS inheritance mapping strategy.
1737
     *
1738
     * @return boolean
1739
     */
1740
    public function isInheritanceTypeCollectionPerClass()
1741
    {
1742
        return $this->inheritanceType == self::INHERITANCE_TYPE_COLLECTION_PER_CLASS;
1743
    }
1744
1745
    /**
1746
     * Sets the mapped subclasses of this class.
1747
     *
1748
     * @param string[] $subclasses The names of all mapped subclasses.
1749
     */
1750 2 View Code Duplication
    public function setSubclasses(array $subclasses)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
1751
    {
1752 2
        foreach ($subclasses as $subclass) {
1753 2
            if (strpos($subclass, '\\') === false && strlen($this->namespace)) {
1754 1
                $this->subClasses[] = $this->namespace . '\\' . $subclass;
1755
            } else {
1756 2
                $this->subClasses[] = $subclass;
1757
            }
1758
        }
1759 2
    }
1760
1761
    /**
1762
     * Sets the parent class names.
1763
     * Assumes that the class names in the passed array are in the order:
1764
     * directParent -> directParentParent -> directParentParentParent ... -> root.
1765
     *
1766
     * @param string[] $classNames
1767
     */
1768 814
    public function setParentClasses(array $classNames)
1769
    {
1770 814
        $this->parentClasses = $classNames;
1771
1772 814
        if (count($classNames) > 0) {
1773 101
            $this->rootDocumentName = array_pop($classNames);
1774
        }
1775 814
    }
1776
1777
    /**
1778
     * Checks whether the class will generate a new \MongoId instance for us.
1779
     *
1780
     * @return boolean TRUE if the class uses the AUTO generator, FALSE otherwise.
1781
     */
1782
    public function isIdGeneratorAuto()
1783
    {
1784
        return $this->generatorType == self::GENERATOR_TYPE_AUTO;
1785
    }
1786
1787
    /**
1788
     * Checks whether the class will use a collection to generate incremented identifiers.
1789
     *
1790
     * @return boolean TRUE if the class uses the INCREMENT generator, FALSE otherwise.
1791
     */
1792
    public function isIdGeneratorIncrement()
1793
    {
1794
        return $this->generatorType == self::GENERATOR_TYPE_INCREMENT;
1795
    }
1796
1797
    /**
1798
     * Checks whether the class will generate a uuid id.
1799
     *
1800
     * @return boolean TRUE if the class uses the UUID generator, FALSE otherwise.
1801
     */
1802
    public function isIdGeneratorUuid()
1803
    {
1804
        return $this->generatorType == self::GENERATOR_TYPE_UUID;
1805
    }
1806
1807
    /**
1808
     * Checks whether the class uses no id generator.
1809
     *
1810
     * @return boolean TRUE if the class does not use any id generator, FALSE otherwise.
1811
     */
1812
    public function isIdGeneratorNone()
1813
    {
1814
        return $this->generatorType == self::GENERATOR_TYPE_NONE;
1815
    }
1816
1817
    /**
1818
     * Sets the version field mapping used for versioning. Sets the default
1819
     * value to use depending on the column type.
1820
     *
1821
     * @param array $mapping   The version field mapping array
1822
     *
1823
     * @throws LockException
1824
     */
1825 93
    public function setVersionMapping(array &$mapping)
1826
    {
1827 93
        if ($mapping['type'] !== 'int' && $mapping['type'] !== 'date') {
1828 1
            throw LockException::invalidVersionFieldType($mapping['type']);
1829
        }
1830
1831 92
        $this->isVersioned  = true;
1832 92
        $this->versionField = $mapping['fieldName'];
1833 92
    }
1834
1835
    /**
1836
     * Sets whether this class is to be versioned for optimistic locking.
1837
     *
1838
     * @param boolean $bool
1839
     */
1840 337
    public function setVersioned($bool)
1841
    {
1842 337
        $this->isVersioned = $bool;
1843 337
    }
1844
1845
    /**
1846
     * Sets the name of the field that is to be used for versioning if this class is
1847
     * versioned for optimistic locking.
1848
     *
1849
     * @param string $versionField
1850
     */
1851 337
    public function setVersionField($versionField)
1852
    {
1853 337
        $this->versionField = $versionField;
1854 337
    }
1855
1856
    /**
1857
     * Sets the version field mapping used for versioning. Sets the default
1858
     * value to use depending on the column type.
1859
     *
1860
     * @param array $mapping   The version field mapping array
1861
     *
1862
     * @throws \Doctrine\ODM\MongoDB\LockException
1863
     */
1864 26
    public function setLockMapping(array &$mapping)
1865
    {
1866 26
        if ($mapping['type'] !== 'int') {
1867 1
            throw LockException::invalidLockFieldType($mapping['type']);
1868
        }
1869
1870 25
        $this->isLockable = true;
1871 25
        $this->lockField = $mapping['fieldName'];
1872 25
    }
1873
1874
    /**
1875
     * Sets whether this class is to allow pessimistic locking.
1876
     *
1877
     * @param boolean $bool
1878
     */
1879
    public function setLockable($bool)
1880
    {
1881
        $this->isLockable = $bool;
1882
    }
1883
1884
    /**
1885
     * Sets the name of the field that is to be used for storing whether a document
1886
     * is currently locked or not.
1887
     *
1888
     * @param string $lockField
1889
     */
1890
    public function setLockField($lockField)
1891
    {
1892
        $this->lockField = $lockField;
1893
    }
1894
1895
    /**
1896
     * {@inheritDoc}
1897
     */
1898
    public function getFieldNames()
1899
    {
1900
        return array_keys($this->fieldMappings);
1901
    }
1902
1903
    /**
1904
     * {@inheritDoc}
1905
     */
1906
    public function getAssociationNames()
1907
    {
1908
        return array_keys($this->associationMappings);
1909
    }
1910
1911
    /**
1912
     * {@inheritDoc}
1913
     */
1914 22
    public function getTypeOfField($fieldName)
1915
    {
1916 22
        return isset($this->fieldMappings[$fieldName]) ?
1917 22
            $this->fieldMappings[$fieldName]['type'] : null;
1918
    }
1919
1920
    /**
1921
     * {@inheritDoc}
1922
     */
1923 6
    public function getAssociationTargetClass($assocName)
1924
    {
1925 6
        if ( ! isset($this->associationMappings[$assocName])) {
1926 3
            throw new InvalidArgumentException("Association name expected, '" . $assocName . "' is not an association.");
1927
        }
1928
1929 3
        return $this->associationMappings[$assocName]['targetDocument'];
1930
    }
1931
1932
    /**
1933
     * {@inheritDoc}
1934
     */
1935
    public function isAssociationInverseSide($fieldName)
1936
    {
1937
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
1938
    }
1939
1940
    /**
1941
     * {@inheritDoc}
1942
     */
1943
    public function getAssociationMappedByTargetField($fieldName)
1944
    {
1945
        throw new \BadMethodCallException(__METHOD__ . '() is not implemented yet.');
1946
    }
1947
}
1948