Completed
Pull Request — master (#1410)
by Piotr
09:50
created

ClassMetadataInfo   F

Complexity

Total Complexity 265

Size/Duplication

Total Lines 1908
Duplicated Lines 3.41 %

Coupling/Cohesion

Components 7
Dependencies 5

Test Coverage

Coverage 83%

Importance

Changes 18
Bugs 2 Features 5
Metric Value
wmc 265
c 18
b 2
f 5
lcom 7
cbo 5
dl 65
loc 1908
ccs 410
cts 494
cp 0.83
rs 0.5217

104 Methods

Rating   Name   Duplication   Size   Complexity  
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
A __construct() 0 5 1
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() 12 12 5
B invokeLifecycleCallbacks() 0 18 5
A hasLifecycleCallbacks() 0 4 1
A getLifecycleCallbacks() 0 4 2
A addLifecycleCallback() 0 8 3
A setLifecycleCallbacks() 0 4 1
A registerAlsoLoadMethod() 0 4 2
A setAlsoLoadMethods() 0 4 1
C setDiscriminatorField() 0 25 7
B setDiscriminatorMap() 0 19 7
A setDefaultDiscriminatorValue() 0 14 3
A setDiscriminatorValue() 0 5 1
A setSlaveOkay() 0 4 2
B addIndex() 0 20 6
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
B setCollection() 0 14 6
A getCollectionCapped() 0 4 1
A setCollectionCapped() 0 4 1
A getCollectionSize() 0 4 1
A setCollectionSize() 0 4 1
A getCollectionMax() 0 4 1
A setCollectionMax() 0 4 1
A isMappedToCollection() 0 4 2
A isFile() 0 4 2
A getFile() 0 4 1
A setFile() 0 4 1
A getDistance() 0 4 1
A setDistance() 0 4 1
F mapField() 43 189 83

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