Completed
Pull Request — 1.0.x (#1500)
by
unknown
10:33
created

PersistentCollection   D

Complexity

Total Complexity 107

Size/Duplication

Total Lines 779
Duplicated Lines 6.16 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 88.64%

Importance

Changes 0
Metric Value
wmc 107
lcom 1
cbo 9
dl 48
loc 779
ccs 234
cts 264
cp 0.8864
rs 4.4444
c 0
b 0
f 0

56 Methods

Rating   Name   Duplication   Size   Complexity  
A getMapping() 0 4 1
A setDocumentManager() 0 5 1
A setMongoData() 0 4 1
A getMongoData() 0 4 1
A setHints() 0 4 1
A getHints() 0 4 1
B changed() 0 16 7
A isDirty() 0 4 1
A setDirty() 0 4 1
A setOwner() 0 5 1
A clearSnapshot() 0 5 2
A getSnapshot() 0 4 1
A getDeleteDiff() 8 8 2
A getInsertDiff() 8 8 2
A getOwner() 0 4 1
A setInitialized() 0 4 1
A isInitialized() 0 4 1
A first() 0 5 1
A last() 0 5 1
A containsKey() 0 5 1
A contains() 0 5 1
A exists() 0 5 1
A indexOf() 0 5 1
A get() 0 5 1
A getKeys() 0 5 1
A getValues() 0 5 1
A isEmpty() 0 4 1
A getIterator() 0 5 1
A map() 0 5 1
A filter() 0 5 1
A forAll() 0 5 1
A partition() 0 5 1
A toArray() 0 5 1
A takeSnapshot() 0 12 3
A count() 0 14 4
A __construct() 0 6 1
C initialize() 0 34 7
B getDeletedDocuments() 0 14 5
A getTypeClass() 0 13 4
A remove() 13 13 2
A removeElement() 13 13 2
A set() 3 11 4
B add() 3 18 6
C clear() 0 30 8
A slice() 0 5 1
A __sleep() 0 4 1
A offsetExists() 0 4 1
A offsetGet() 0 4 1
A offsetSet() 0 8 2
A offsetUnset() 0 4 1
A key() 0 4 1
A current() 0 4 1
A next() 0 4 1
A unwrap() 0 4 1
A __clone() 0 13 2
B isOrphanRemovalEnabled() 0 16 6

How to fix   Duplicated Code    Complexity   

Duplicated Code

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

Common duplication problems, and corresponding solutions are:

Complex Class

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

Complex classes like PersistentCollection 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 PersistentCollection, 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;
21
22
use Doctrine\Common\Collections\Collection as BaseCollection;
23
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
24
use Doctrine\ODM\MongoDB\Utility\CollectionHelper;
25
26
/**
27
 * A PersistentCollection represents a collection of elements that have persistent state.
28
 *
29
 * @since       1.0
30
 * @author      Jonathan H. Wage <[email protected]>
31
 * @author      Roman Borschel <[email protected]>
32
 */
33
class PersistentCollection implements BaseCollection
34
{
35
    /**
36
     * A snapshot of the collection at the moment it was fetched from the database.
37
     * This is used to create a diff of the collection at commit time.
38
     *
39
     * @var array
40
     */
41
    private $snapshot = array();
42
43
    /**
44
     * Collection's owning entity
45
     *
46
     * @var object
47
     */
48
    private $owner;
49
50
    /**
51
     * @var array
52
     */
53
    private $mapping;
54
55
    /**
56
     * Whether the collection is dirty and needs to be synchronized with the database
57
     * when the UnitOfWork that manages its persistent state commits.
58
     *
59
     * @var boolean
60
     */
61
    private $isDirty = false;
62
63
    /**
64
     * Whether the collection has already been initialized.
65
     *
66
     * @var boolean
67
     */
68
    private $initialized = true;
69
70
    /**
71
     * The wrapped Collection instance.
72
     *
73
     * @var BaseCollection
74
     */
75
    private $coll;
76
77
    /**
78
     * The DocumentManager that manages the persistence of the collection.
79
     *
80
     * @var DocumentManager
81
     */
82
    private $dm;
83
84
    /**
85
     * The UnitOfWork that manages the persistence of the collection.
86
     *
87
     * @var UnitOfWork
88
     */
89
    private $uow;
90
91
    /**
92
     * The raw mongo data that will be used to initialize this collection.
93
     *
94
     * @var array
95
     */
96
    private $mongoData = array();
97
98
    /**
99
     * Any hints to account for during reconstitution/lookup of the documents.
100
     *
101
     * @var array
102
     */
103
    private $hints = array();
104
105
    /**
106
     * @param BaseCollection $coll
107
     * @param DocumentManager $dm
108
     * @param UnitOfWork $uow
109
     */
110 398
    public function __construct(BaseCollection $coll, DocumentManager $dm, UnitOfWork $uow)
111
    {
112 398
        $this->coll = $coll;
113 398
        $this->dm = $dm;
114 398
        $this->uow = $uow;
115 398
    }
116
117
    /**
118
     * Sets the document manager and unit of work (used during merge operations).
119
     *
120
     * @param DocumentManager $dm
121
     */
122 2
    public function setDocumentManager(DocumentManager $dm)
123
    {
124 2
        $this->dm = $dm;
125 2
        $this->uow = $dm->getUnitOfWork();
126 2
    }
127
128
    /**
129
     * Sets the array of raw mongo data that will be used to initialize this collection.
130
     *
131
     * @param array $mongoData
132
     */
133 170
    public function setMongoData(array $mongoData)
134
    {
135 170
        $this->mongoData = $mongoData;
136 170
    }
137
138
    /**
139
     * Gets the array of raw mongo data that will be used to initialize this collection.
140
     *
141
     * @return array $mongoData
142
     */
143 147
    public function getMongoData()
144
    {
145 147
        return $this->mongoData;
146
    }
147
148
    /**
149
     * Set hints to account for during reconstitution/lookup of the documents.
150
     *
151
     * @param array $hints
152
     */
153 253
    public function setHints(array $hints)
154
    {
155 253
        $this->hints = $hints;
156 253
    }
157
158
    /**
159
     * Get hints to account for during reconstitution/lookup of the documents.
160
     *
161
     * @return array $hints
162
     */
163 83
    public function getHints()
164
    {
165 83
        return $this->hints;
166
    }
167
168
    /**
169
     * Initializes the collection by loading its contents from the database
170
     * if the collection is not yet initialized.
171
     */
172 363
    public function initialize()
173
    {
174 363
        if ($this->initialized || ! $this->mapping) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->mapping of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
175 356
            return;
176
        }
177
178 159
        $newObjects = array();
179
180 159
        if ($this->isDirty) {
181
            // Remember any NEW objects added through add()
182 21
            $newObjects = $this->coll->toArray();
183 21
        }
184
185 159
        $this->initialized = true;
186
187 159
        $this->coll->clear();
188 159
        $this->uow->loadCollection($this);
189 159
        $this->takeSnapshot();
190
191 159
        $this->mongoData = array();
192
193
        // Reattach any NEW objects added through add()
194 159
        if ($newObjects) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $newObjects of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
195 21
            foreach ($newObjects as $key => $obj) {
196 21
                if (CollectionHelper::isHash($this->mapping['strategy'])) {
197
                    $this->coll->set($key, $obj);
198
                } else {
199 21
                    $this->coll->add($obj);
200
                }
201 21
            }
202
203 21
            $this->isDirty = true;
204 21
        }
205 159
    }
206
207
    /**
208
     * Marks this collection as changed/dirty.
209
     */
210 180
    private function changed()
211
    {
212 180
        if ($this->isDirty) {
213 118
            return;
214
        }
215
216 180
        $this->isDirty = true;
217
218 180
        if ($this->dm &&
219 180
            $this->mapping !== null &&
220 180
            $this->mapping['isOwningSide'] &&
221 180
            $this->owner &&
222 180
            $this->dm->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
223 1
            $this->uow->scheduleForDirtyCheck($this->owner);
224 1
        }
225 180
    }
226
227
    /**
228
     * Gets a boolean flag indicating whether this collection is dirty which means
229
     * its state needs to be synchronized with the database.
230
     *
231
     * @return boolean TRUE if the collection is dirty, FALSE otherwise.
232
     */
233 380
    public function isDirty()
234
    {
235 380
        return $this->isDirty;
236
    }
237
238
    /**
239
     * Sets a boolean flag, indicating whether this collection is dirty.
240
     *
241
     * @param boolean $dirty Whether the collection should be marked dirty or not.
242
     */
243 373
    public function setDirty($dirty)
244
    {
245 373
        $this->isDirty = $dirty;
246 373
    }
247
248
    /**
249
     * INTERNAL:
250
     * Sets the collection's owning entity together with the AssociationMapping that
251
     * describes the association between the owner and the elements of the collection.
252
     *
253
     * @param object $document
254
     * @param array $mapping
255
     */
256 391
    public function setOwner($document, array $mapping)
257
    {
258 391
        $this->owner = $document;
259 391
        $this->mapping = $mapping;
260 391
    }
261
262
    /**
263
     * INTERNAL:
264
     * Tells this collection to take a snapshot of its current state reindexing
265
     * itself numerically if using save strategy that is enforcing BSON array.
266
     * Reindexing is safe as snapshot is taken only after synchronizing collection
267
     * with database or clearing it.
268
     */
269 261
    public function takeSnapshot()
270
    {
271 261
        if (CollectionHelper::isList($this->mapping['strategy'])) {
272 247
            $array = $this->coll->toArray();
273 247
            $this->coll->clear();
274 247
            foreach ($array as $document) {
275 222
                $this->coll->add($document);
276 247
            }
277 247
        }
278 261
        $this->snapshot = $this->coll->toArray();
279 261
        $this->isDirty = false;
280 261
    }
281
282
    /**
283
     * INTERNAL:
284
     * Clears the internal snapshot information and sets isDirty to true if the collection
285
     * has elements.
286
     */
287 22
    public function clearSnapshot()
288
    {
289 22
        $this->snapshot = array();
290 22
        $this->isDirty = $this->coll->count() ? true : false;
291 22
    }
292
293
    /**
294
     * INTERNAL:
295
     * Returns the last snapshot of the elements in the collection.
296
     *
297
     * @return array The last snapshot of the elements.
298
     */
299
    public function getSnapshot()
300
    {
301
        return $this->snapshot;
302
    }
303
304
    /**
305
     * INTERNAL:
306
     * getDeleteDiff
307
     *
308
     * @return array
309
     */
310 83 View Code Duplication
    public function getDeleteDiff()
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...
311
    {
312 83
        return array_udiff_assoc(
313 83
            $this->snapshot,
314 83
            $this->coll->toArray(),
315
            function ($a, $b) { return $a === $b ? 0 : 1; }
316 83
        );
317
    }
318
319
    /**
320
     * INTERNAL: get objects that were removed, unlike getDeleteDiff this doesn't care about indices.
321
     *
322
     * @return array
323
     */
324 141
    public function getDeletedDocuments()
325
    {
326
        $compare = function ($a, $b) {
327 98
            $compareA = is_object($a) ? spl_object_hash($a) : $a;
328 98
            $compareb = is_object($b) ? spl_object_hash($b) : $b;
329 98
            return $compareA === $compareb ? 0 : ($compareA > $compareb ? 1 : -1);
330 141
        };
331
332 141
        return array_udiff(
333 141
            $this->snapshot,
334 141
            $this->coll->toArray(),
335
            $compare
336 141
        );
337
    }
338
339
    /**
340
     * INTERNAL:
341
     * getInsertDiff
342
     *
343
     * @return array
344
     */
345 83 View Code Duplication
    public function getInsertDiff()
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...
346
    {
347 83
        return array_udiff_assoc(
348 83
            $this->coll->toArray(),
349 83
            $this->snapshot,
350
            function ($a, $b) { return $a === $b ? 0 : 1; }
351 83
        );
352
    }
353
354
    /**
355
     * INTERNAL:
356
     * Gets the collection owner.
357
     *
358
     * @return object
359
     */
360 380
    public function getOwner()
361
    {
362 380
        return $this->owner;
363
    }
364
365
    /**
366
     * @return array
367
     */
368 259
    public function getMapping()
369
    {
370 259
        return $this->mapping;
371
    }
372
373
    /**
374
     * @return ClassMetadata
375
     * @throws MongoDBException
376
     */
377 5
    public function getTypeClass()
378
    {
379 5
        switch (true) {
380 5
            case ($this->dm === null):
381 1
                throw new MongoDBException('No DocumentManager is associated with this PersistentCollection, please set one using setDocumentManager method.');
382 4
            case (empty($this->mapping)):
383 1
                throw new MongoDBException('No mapping is associated with this PersistentCollection, please set one using setOwner method.');
384 3
            case (empty($this->mapping['targetDocument'])):
385 1
                throw new MongoDBException('Specifying targetDocument is required for the ClassMetadata to be obtained.');
386 2
            default:
387 2
                return $this->dm->getClassMetadata($this->mapping['targetDocument']);
388 2
        }
389
    }
390
391
    /**
392
     * Sets the initialized flag of the collection, forcing it into that state.
393
     *
394
     * @param boolean $bool
395
     */
396 254
    public function setInitialized($bool)
397
    {
398 254
        $this->initialized = $bool;
399 254
    }
400
401
    /**
402
     * Checks whether this collection has been initialized.
403
     *
404
     * @return boolean
405
     */
406 15
    public function isInitialized()
407
    {
408 15
        return $this->initialized;
409
    }
410
411
    /** {@inheritdoc} */
412 12
    public function first()
413
    {
414 12
        $this->initialize();
415 12
        return $this->coll->first();
416
    }
417
418
    /** {@inheritdoc} */
419 1
    public function last()
420
    {
421 1
        $this->initialize();
422 1
        return $this->coll->last();
423
    }
424
425
    /**
426
     * {@inheritdoc}
427
     */
428 20 View Code Duplication
    public function remove($key)
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...
429
    {
430 20
        $this->initialize();
431 20
        $removed = $this->coll->remove($key);
432
433 20
        if ( ! $removed) {
434
            return $removed;
435
        }
436
437 20
        $this->changed();
438
439 20
        return $removed;
440
    }
441
442
    /**
443
     * {@inheritdoc}
444
     */
445 13 View Code Duplication
    public function removeElement($element)
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...
446
    {
447 13
        $this->initialize();
448 13
        $removed = $this->coll->removeElement($element);
449
450 13
        if ( ! $removed) {
451
            return $removed;
452
        }
453
454 13
        $this->changed();
455
456 13
        return $removed;
457
    }
458
459
    /**
460
     * {@inheritdoc}
461
     */
462 1
    public function containsKey($key)
463
    {
464 1
        $this->initialize();
465 1
        return $this->coll->containsKey($key);
466
    }
467
468
    /**
469
     * {@inheritdoc}
470
     */
471 2
    public function contains($element)
472
    {
473 2
        $this->initialize();
474 2
        return $this->coll->contains($element);
475
    }
476
477
    /**
478
     * {@inheritdoc}
479
     */
480
    public function exists(\Closure $p)
481
    {
482
        $this->initialize();
483
        return $this->coll->exists($p);
484
    }
485
486
    /**
487
     * {@inheritdoc}
488
     */
489 2
    public function indexOf($element)
490
    {
491 2
        $this->initialize();
492 2
        return $this->coll->indexOf($element);
493
    }
494
495
    /**
496
     * {@inheritdoc}
497
     */
498 87
    public function get($key)
499
    {
500 87
        $this->initialize();
501 87
        return $this->coll->get($key);
502
    }
503
504
    /**
505
     * {@inheritdoc}
506
     */
507
    public function getKeys()
508
    {
509
        $this->initialize();
510
        return $this->coll->getKeys();
511
    }
512
513
    /**
514
     * {@inheritdoc}
515
     */
516
    public function getValues()
517
    {
518
        $this->initialize();
519
        return $this->coll->getValues();
520
    }
521
522
    /**
523
     * {@inheritdoc}
524
     */
525 361
    public function count()
526
    {
527 361
        $count = $this->coll->count();
528
529
        // If this collection is inversed and not initialized, add the count returned from the database
530 361
        if ($this->mapping['isInverseSide'] && ! $this->initialized) {
531 10
            $documentPersister = $this->uow->getDocumentPersister(get_class($this->owner));
532 10
            $count += empty($this->mapping['repositoryMethod'])
533 10
                ? $documentPersister->createReferenceManyInverseSideQuery($this)->count()
534 10
                : $documentPersister->createReferenceManyWithRepositoryMethodCursor($this)->count();
535 10
        }
536
537 361
        return count($this->mongoData) + $count;
538
    }
539
540
    /**
541
     * {@inheritdoc}
542
     */
543 35
    public function set($key, $value)
544
    {
545 35
        $this->coll->set($key, $value);
546
547
        // Handle orphanRemoval
548 35 View Code Duplication
        if ($this->uow !== null && $this->isOrphanRemovalEnabled() && $value !== null) {
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...
549 34
            $this->uow->unscheduleOrphanRemoval($value);
550 34
        }
551
552 35
        $this->changed();
553 35
    }
554
555
    /**
556
     * {@inheritdoc}
557
     */
558 154
    public function add($value)
559
    {
560
        /* Initialize the collection before calling add() so this append operation
561
         * uses the appropriate key. Otherwise, we risk overwriting original data
562
         * when $newObjects are re-added in a later call to initialize().
563
         */
564 154
        if (isset($this->mapping['strategy']) && CollectionHelper::isHash($this->mapping['strategy'])) {
565 11
            $this->initialize();
566 11
        }
567 154
        $this->coll->add($value);
568 154
        $this->changed();
569
570 154 View Code Duplication
        if ($this->uow !== null && $this->isOrphanRemovalEnabled() && $value !== null) {
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...
571 89
            $this->uow->unscheduleOrphanRemoval($value);
572 89
        }
573
574 154
        return true;
575
    }
576
577
    /**
578
     * {@inheritdoc}
579
     */
580 354
    public function isEmpty()
581
    {
582 354
        return $this->count() === 0;
583
    }
584
585
    /**
586
     * {@inheritdoc}
587
     */
588 310
    public function getIterator()
589
    {
590 310
        $this->initialize();
591 310
        return $this->coll->getIterator();
592
    }
593
594
    /**
595
     * {@inheritdoc}
596
     */
597 211
    public function map(\Closure $func)
598
    {
599 211
        $this->initialize();
600 211
        return $this->coll->map($func);
601
    }
602
603
    /**
604
     * {@inheritdoc}
605
     */
606
    public function filter(\Closure $p)
607
    {
608
        $this->initialize();
609
        return $this->coll->filter($p);
610
    }
611
612
    /**
613
     * {@inheritdoc}
614
     */
615
    public function forAll(\Closure $p)
616
    {
617
        $this->initialize();
618
        return $this->coll->forAll($p);
619
    }
620
621
    /**
622
     * {@inheritdoc}
623
     */
624
    public function partition(\Closure $p)
625
    {
626
        $this->initialize();
627
        return $this->coll->partition($p);
628
    }
629
630
    /**
631
     * {@inheritdoc}
632
     */
633 23
    public function toArray()
634
    {
635 23
        $this->initialize();
636 23
        return $this->coll->toArray();
637
    }
638
639
    /**
640
     * {@inheritdoc}
641
     */
642 28
    public function clear()
643
    {
644 28
        if ($this->initialized && $this->isEmpty()) {
645
            return;
646
        }
647
648 28
        if ($this->isOrphanRemovalEnabled()) {
649 27
            $this->initialize();
650 27
            foreach ($this->coll as $element) {
651 27
                $this->uow->scheduleOrphanRemoval($element);
652 27
            }
653 27
        }
654
655 28
        $this->mongoData = array();
656 28
        $this->coll->clear();
657
658
        // Nothing to do for inverse-side collections
659 28
        if ( ! $this->mapping['isOwningSide']) {
660
            return;
661
        }
662
663
        // Nothing to do if the collection was initialized but contained no data
664 28
        if ($this->initialized && empty($this->snapshot)) {
665 2
            return;
666
        }
667
668 26
        $this->changed();
669 26
        $this->uow->scheduleCollectionDeletion($this);
670 26
        $this->takeSnapshot();
671 26
    }
672
673
    /**
674
     * {@inheritdoc}
675
     */
676 1
    public function slice($offset, $length = null)
677
    {
678 1
        $this->initialize();
679 1
        return $this->coll->slice($offset, $length);
680
    }
681
682
    /**
683
     * Called by PHP when this collection is serialized. Ensures that only the
684
     * elements are properly serialized.
685
     *
686
     * @internal Tried to implement Serializable first but that did not work well
687
     *           with circular references. This solution seems simpler and works well.
688
     */
689 4
    public function __sleep()
690
    {
691 4
        return array('coll', 'initialized');
692
    }
693
694
    /* ArrayAccess implementation */
695
696
    /**
697
     * @see containsKey()
698
     */
699 1
    public function offsetExists($offset)
700
    {
701 1
        return $this->containsKey($offset);
702
    }
703
704
    /**
705
     * @see get()
706
     */
707 75
    public function offsetGet($offset)
708
    {
709 75
        return $this->get($offset);
710
    }
711
712
    /**
713
     * @see add()
714
     * @see set()
715
     */
716 39
    public function offsetSet($offset, $value)
717
    {
718 39
        if ( ! isset($offset)) {
719 38
            return $this->add($value);
720
        }
721
722 2
        return $this->set($offset, $value);
723
    }
724
725
    /**
726
     * @see remove()
727
     */
728 17
    public function offsetUnset($offset)
729
    {
730 17
        return $this->remove($offset);
731
    }
732
733
    public function key()
734
    {
735
        return $this->coll->key();
736
    }
737
738
    /**
739
     * Gets the element of the collection at the current iterator position.
740
     */
741 1
    public function current()
742
    {
743 1
        return $this->coll->current();
744
    }
745
746
    /**
747
     * Moves the internal iterator position to the next element.
748
     */
749
    public function next()
750
    {
751
        return $this->coll->next();
752
    }
753
754
    /**
755
     * Retrieves the wrapped Collection instance.
756
     */
757 380
    public function unwrap()
758
    {
759 380
        return $this->coll;
760
    }
761
762
    /**
763
     * Cleanup internal state of cloned persistent collection.
764
     *
765
     * The following problems have to be prevented:
766
     * 1. Added documents are added to old PersistentCollection
767
     * 2. New collection is not dirty, if reused on other document nothing
768
     * changes.
769
     * 3. Snapshot leads to invalid diffs being generated.
770
     * 4. Lazy loading grabs entities from old owner object.
771
     * 5. New collection is connected to old owner and leads to duplicate keys.
772
     */
773 8
    public function __clone()
774
    {
775 8
        if (is_object($this->coll)) {
776 8
            $this->coll = clone $this->coll;
777 8
        }
778
779 8
        $this->initialize();
780
781 8
        $this->owner = null;
782 8
        $this->snapshot = array();
783
784 8
        $this->changed();
785 8
    }
786
787
    /**
788
     * Returns whether or not this collection has orphan removal enabled.
789
     *
790
     * Embedded documents are automatically considered as "orphan removal enabled" because they might have references
791
     * that require to trigger cascade remove operations.
792
     *
793
     * @return boolean
794
     */
795 174
    private function isOrphanRemovalEnabled()
796
    {
797 174
        if ($this->mapping === null) {
798 5
            return false;
799
        }
800
801 169
        if (isset($this->mapping['embedded'])) {
802 100
            return true;
803
        }
804
805 74
        if (isset($this->mapping['reference']) && $this->mapping['isOwningSide'] && $this->mapping['orphanRemoval']) {
806 8
            return true;
807
        }
808
809 66
        return false;
810
    }
811
}
812