Completed
Pull Request — 1.0.x (#1416)
by Maciej
09:32
created

PersistentCollection   D

Complexity

Total Complexity 106

Size/Duplication

Total Lines 782
Duplicated Lines 6.14 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 86.87%

Importance

Changes 8
Bugs 0 Features 1
Metric Value
wmc 106
lcom 1
cbo 9
dl 48
loc 782
ccs 225
cts 259
cp 0.8687
rs 4.4444
c 8
b 0
f 1

56 Methods

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