Completed
Push — 1.0.x ( bcb4f8...f4d4ca )
by Andreas
9s
created

PersistentCollection   D

Complexity

Total Complexity 107

Size/Duplication

Total Lines 778
Duplicated Lines 6.17 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 88.59%

Importance

Changes 8
Bugs 1 Features 1
Metric Value
wmc 107
c 8
b 1
f 1
lcom 1
cbo 9
dl 48
loc 778
ccs 233
cts 263
cp 0.8859
rs 4.4444

56 Methods

Rating   Name   Duplication   Size   Complexity  
A getMapping() 0 4 1
A __construct() 0 6 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
C initialize() 0 34 7
B changed() 0 16 7
A isDirty() 0 4 1
A setDirty() 0 4 1
A setOwner() 0 5 1
A takeSnapshot() 0 12 3
A clearSnapshot() 0 5 2
A getSnapshot() 0 4 1
A getDeleteDiff() 8 8 2
B getDeletedDocuments() 0 14 5
A getInsertDiff() 8 8 2
A getOwner() 0 4 1
A getTypeClass() 0 13 4
A setInitialized() 0 4 1
A isInitialized() 0 4 1
A first() 0 5 1
A last() 0 5 1
A remove() 13 13 2
A removeElement() 13 13 2
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 count() 0 14 4
A set() 3 11 4
B add() 3 18 6
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
C clear() 0 29 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 397
    public function __construct(BaseCollection $coll, DocumentManager $dm, UnitOfWork $uow)
111
    {
112 397
        $this->coll = $coll;
113 397
        $this->dm = $dm;
114 397
        $this->uow = $uow;
115 397
    }
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 169
    public function setMongoData(array $mongoData)
134
    {
135 169
        $this->mongoData = $mongoData;
136 169
    }
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 146
    public function getMongoData()
144
    {
145 146
        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 252
    public function setHints(array $hints)
154
    {
155 252
        $this->hints = $hints;
156 252
    }
157
158
    /**
159
     * Get hints to account for during reconstitution/lookup of the documents.
160
     *
161
     * @return array $hints
162
     */
163 82
    public function getHints()
164
    {
165 82
        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 362
    public function initialize()
173
    {
174 362
        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 355
            return;
176
        }
177
178 158
        $newObjects = array();
179
180 158
        if ($this->isDirty) {
181
            // Remember any NEW objects added through add()
182 21
            $newObjects = $this->coll->toArray();
183 21
        }
184
185 158
        $this->initialized = true;
186
187 158
        $this->coll->clear();
188 158
        $this->uow->loadCollection($this);
189 158
        $this->takeSnapshot();
190
191 158
        $this->mongoData = array();
192
193
        // Reattach any NEW objects added through add()
194 158
        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 158
    }
206
207
    /**
208
     * Marks this collection as changed/dirty.
209
     */
210 179
    private function changed()
211
    {
212 179
        if ($this->isDirty) {
213 117
            return;
214
        }
215
216 179
        $this->isDirty = true;
217
218 179
        if ($this->dm &&
219 179
            $this->mapping !== null &&
220 179
            $this->mapping['isOwningSide'] &&
221 179
            $this->owner &&
222 179
            $this->dm->getClassMetadata(get_class($this->owner))->isChangeTrackingNotify()) {
223 1
            $this->uow->scheduleForDirtyCheck($this->owner);
224 1
        }
225 179
    }
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 379
    public function isDirty()
234
    {
235 379
        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 372
    public function setDirty($dirty)
244
    {
245 372
        $this->isDirty = $dirty;
246 372
    }
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 390
    public function setOwner($document, array $mapping)
257
    {
258 390
        $this->owner = $document;
259 390
        $this->mapping = $mapping;
260 390
    }
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 260
    public function takeSnapshot()
270
    {
271 260
        if (CollectionHelper::isList($this->mapping['strategy'])) {
272 246
            $array = $this->coll->toArray();
273 246
            $this->coll->clear();
274 246
            foreach ($array as $document) {
275 221
                $this->coll->add($document);
276 246
            }
277 246
        }
278 260
        $this->snapshot = $this->coll->toArray();
279 260
        $this->isDirty = false;
280 260
    }
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 140
    public function getDeletedDocuments()
325
    {
326
        $compare = function ($a, $b) {
327 97
            $compareA = is_object($a) ? spl_object_hash($a) : $a;
328 97
            $compareb = is_object($b) ? spl_object_hash($b) : $b;
329 97
            return $compareA === $compareb ? 0 : ($compareA > $compareb ? 1 : -1);
330 140
        };
331
332 140
        return array_udiff(
333 140
            $this->snapshot,
334 140
            $this->coll->toArray(),
335
            $compare
336 140
        );
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 379
    public function getOwner()
361
    {
362 379
        return $this->owner;
363
    }
364
365
    /**
366
     * @return array
367
     */
368 258
    public function getMapping()
369
    {
370 258
        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 253
    public function setInitialized($bool)
397
    {
398 253
        $this->initialized = $bool;
399 253
    }
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 360
    public function count()
526
    {
527 360
        $count = $this->coll->count();
528
529
        // If this collection is inversed and not initialized, add the count returned from the database
530 360
        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 360
        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 153
    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 153
        if (isset($this->mapping['strategy']) && CollectionHelper::isHash($this->mapping['strategy'])) {
565 11
            $this->initialize();
566 11
        }
567 153
        $this->coll->add($value);
568 153
        $this->changed();
569
570 153 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 88
            $this->uow->unscheduleOrphanRemoval($value);
572 88
        }
573
574 153
        return true;
575
    }
576
577
    /**
578
     * {@inheritdoc}
579
     */
580 353
    public function isEmpty()
581
    {
582 353
        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 210
    public function map(\Closure $func)
598
    {
599 210
        $this->initialize();
600 210
        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 27
    public function clear()
643
    {
644 27
        if ($this->initialized && $this->isEmpty()) {
645
            return;
646
        }
647
648 27
        if ($this->isOrphanRemovalEnabled()) {
649 26
            foreach ($this->coll as $element) {
650 24
                $this->uow->scheduleOrphanRemoval($element);
651 26
            }
652 26
        }
653
654 27
        $this->mongoData = array();
655 27
        $this->coll->clear();
656
657
        // Nothing to do for inverse-side collections
658 27
        if ( ! $this->mapping['isOwningSide']) {
659
            return;
660
        }
661
662
        // Nothing to do if the collection was initialized but contained no data
663 27
        if ($this->initialized && empty($this->snapshot)) {
664 2
            return;
665
        }
666
667 25
        $this->changed();
668 25
        $this->uow->scheduleCollectionDeletion($this);
669 25
        $this->takeSnapshot();
670 25
    }
671
672
    /**
673
     * {@inheritdoc}
674
     */
675 1
    public function slice($offset, $length = null)
676
    {
677 1
        $this->initialize();
678 1
        return $this->coll->slice($offset, $length);
679
    }
680
681
    /**
682
     * Called by PHP when this collection is serialized. Ensures that only the
683
     * elements are properly serialized.
684
     *
685
     * @internal Tried to implement Serializable first but that did not work well
686
     *           with circular references. This solution seems simpler and works well.
687
     */
688 4
    public function __sleep()
689
    {
690 4
        return array('coll', 'initialized');
691
    }
692
693
    /* ArrayAccess implementation */
694
695
    /**
696
     * @see containsKey()
697
     */
698 1
    public function offsetExists($offset)
699
    {
700 1
        return $this->containsKey($offset);
701
    }
702
703
    /**
704
     * @see get()
705
     */
706 75
    public function offsetGet($offset)
707
    {
708 75
        return $this->get($offset);
709
    }
710
711
    /**
712
     * @see add()
713
     * @see set()
714
     */
715 39
    public function offsetSet($offset, $value)
716
    {
717 39
        if ( ! isset($offset)) {
718 38
            return $this->add($value);
719
        }
720
721 2
        return $this->set($offset, $value);
722
    }
723
724
    /**
725
     * @see remove()
726
     */
727 17
    public function offsetUnset($offset)
728
    {
729 17
        return $this->remove($offset);
730
    }
731
732
    public function key()
733
    {
734
        return $this->coll->key();
735
    }
736
737
    /**
738
     * Gets the element of the collection at the current iterator position.
739
     */
740 1
    public function current()
741
    {
742 1
        return $this->coll->current();
743
    }
744
745
    /**
746
     * Moves the internal iterator position to the next element.
747
     */
748
    public function next()
749
    {
750
        return $this->coll->next();
751
    }
752
753
    /**
754
     * Retrieves the wrapped Collection instance.
755
     */
756 379
    public function unwrap()
757
    {
758 379
        return $this->coll;
759
    }
760
761
    /**
762
     * Cleanup internal state of cloned persistent collection.
763
     *
764
     * The following problems have to be prevented:
765
     * 1. Added documents are added to old PersistentCollection
766
     * 2. New collection is not dirty, if reused on other document nothing
767
     * changes.
768
     * 3. Snapshot leads to invalid diffs being generated.
769
     * 4. Lazy loading grabs entities from old owner object.
770
     * 5. New collection is connected to old owner and leads to duplicate keys.
771
     */
772 8
    public function __clone()
773
    {
774 8
        if (is_object($this->coll)) {
775 8
            $this->coll = clone $this->coll;
776 8
        }
777
778 8
        $this->initialize();
779
780 8
        $this->owner = null;
781 8
        $this->snapshot = array();
782
783 8
        $this->changed();
784 8
    }
785
786
    /**
787
     * Returns whether or not this collection has orphan removal enabled.
788
     *
789
     * Embedded documents are automatically considered as "orphan removal enabled" because they might have references
790
     * that require to trigger cascade remove operations.
791
     *
792
     * @return boolean
793
     */
794 173
    private function isOrphanRemovalEnabled()
795
    {
796 173
        if ($this->mapping === null) {
797 5
            return false;
798
        }
799
800 168
        if (isset($this->mapping['embedded'])) {
801 100
            return true;
802
        }
803
804 73
        if (isset($this->mapping['reference']) && $this->mapping['isOwningSide'] && $this->mapping['orphanRemoval']) {
805 7
            return true;
806
        }
807
808 66
        return false;
809
    }
810
}
811