PersistentCollection   F
last analyzed

Complexity

Total Complexity 110

Size/Duplication

Total Lines 689
Duplicated Lines 0 %

Test Coverage

Coverage 97.07%

Importance

Changes 0
Metric Value
eloc 195
dl 0
loc 689
ccs 232
cts 239
cp 0.9707
rs 2
c 0
b 0
f 0
wmc 110

37 Methods

Rating   Name   Duplication   Size   Complexity  
A isDirty() 0 3 1
A offsetUnset() 0 3 1
A unwrap() 0 3 1
A __sleep() 0 3 1
A offsetGet() 0 3 1
A getMapping() 0 3 1
A setInitialized() 0 3 1
A takeSnapshot() 0 4 1
A getSnapshot() 0 3 1
A getTypeClass() 0 3 1
A setDirty() 0 3 1
A getOwner() 0 3 1
A offsetExists() 0 3 1
B get() 0 14 7
A set() 0 8 3
A setOwner() 0 5 2
A getInsertDiff() 0 7 1
A initialize() 0 9 3
A containsKey() 0 12 6
A __clone() 0 12 2
A doInitialize() 0 15 3
B removeElement() 0 30 10
A remove() 0 22 6
A getDeleteDiff() 0 7 1
A count() 0 11 5
A changed() 0 13 6
A slice() 0 12 5
A restoreNewObjectsInDirtyCollection() 0 12 2
A contains() 0 11 5
A add() 0 11 3
A isEmpty() 0 7 3
A __construct() 0 6 1
B clear() 0 32 9
A matching() 0 29 6
A offsetSet() 0 8 2
A hydrateSet() 0 16 3
A hydrateAdd() 0 16 3

How to fix   Complexity   

Complex Class

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.

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
declare(strict_types=1);
4
5
namespace Doctrine\ORM;
6
7
use Doctrine\Common\Collections\AbstractLazyCollection;
8
use Doctrine\Common\Collections\ArrayCollection;
9
use Doctrine\Common\Collections\Collection;
10
use Doctrine\Common\Collections\Criteria;
11
use Doctrine\Common\Collections\Selectable;
12
use Doctrine\ORM\Mapping\AssociationMetadata;
13
use Doctrine\ORM\Mapping\ChangeTrackingPolicy;
14
use Doctrine\ORM\Mapping\ClassMetadata;
15
use Doctrine\ORM\Mapping\FetchMode;
16
use Doctrine\ORM\Mapping\ManyToManyAssociationMetadata;
17
use Doctrine\ORM\Mapping\OneToManyAssociationMetadata;
18
use Doctrine\ORM\Mapping\ToManyAssociationMetadata;
19
use function array_combine;
20
use function array_diff_key;
21
use function array_map;
22
use function array_values;
23
use function array_walk;
24
use function get_class;
25
use function is_object;
26
use function spl_object_id;
27
28
/**
29
 * A PersistentCollection represents a collection of elements that have persistent state.
30
 *
31
 * Collections of entities represent only the associations (links) to those entities.
32
 * That means, if the collection is part of a many-many mapping and you remove
33
 * entities from the collection, only the links in the relation table are removed (on flush).
34
 * Similarly, if you remove entities from a collection that is part of a one-many
35
 * mapping this will only result in the nulling out of the foreign keys on flush.
36
 */
37
final class PersistentCollection extends AbstractLazyCollection implements Selectable
38
{
39
    /**
40
     * A snapshot of the collection at the moment it was fetched from the database.
41
     * This is used to create a diff of the collection at commit time.
42
     *
43
     * @var object[]
44
     */
45
    private $snapshot = [];
46
47
    /**
48
     * The entity that owns this collection.
49
     *
50
     * @var object
51
     */
52
    private $owner;
53
54
    /**
55
     * The association mapping the collection belongs to.
56
     * This is currently either a OneToManyMapping or a ManyToManyMapping.
57
     *
58
     * @var ToManyAssociationMetadata
59
     */
60
    private $association;
61
62
    /**
63
     * The EntityManager that manages the persistence of the collection.
64
     *
65
     * @var EntityManagerInterface
66
     */
67
    private $em;
68
69
    /**
70
     * The name of the field on the target entities that points to the owner
71
     * of the collection. This is only set if the association is bi-directional.
72
     *
73
     * @var string
74
     */
75
    private $backRefFieldName;
76
77
    /**
78
     * The class descriptor of the collection's entity type.
79
     *
80
     * @var ClassMetadata
81
     */
82
    private $typeClass;
83
84
    /**
85
     * Whether the collection is dirty and needs to be synchronized with the database
86
     * when the UnitOfWork that manages its persistent state commits.
87
     *
88
     * @var bool
89
     */
90
    private $isDirty = false;
91
92
    /**
93
     * Creates a new persistent collection.
94
     *
95
     * @param EntityManagerInterface $em         The EntityManager the collection will be associated with.
96
     * @param ClassMetadata          $class      The class descriptor of the entity type of this collection.
97
     * @param Collection|object[]    $collection The collection elements.
98
     */
99 866
    public function __construct(EntityManagerInterface $em, $class, Collection $collection)
100
    {
101 866
        $this->collection  = $collection;
102 866
        $this->em          = $em;
103 866
        $this->typeClass   = $class;
104 866
        $this->initialized = true;
105 866
    }
106
107
    /**
108
     * INTERNAL:
109
     * Sets the collection's owning entity together with the AssociationMapping that
110
     * describes the association between the owner and the elements of the collection.
111
     *
112
     * @param object $entity
113
     */
114 860
    public function setOwner($entity, ToManyAssociationMetadata $association)
115
    {
116 860
        $this->owner            = $entity;
117 860
        $this->association      = $association;
118 860
        $this->backRefFieldName = $association->getInversedBy() ?: $association->getMappedBy();
119 860
    }
120
121
    /**
122
     * INTERNAL:
123
     * Gets the collection owner.
124
     *
125
     * @return object
126
     */
127 514
    public function getOwner()
128
    {
129 514
        return $this->owner;
130
    }
131
132
    /**
133
     * @return Mapping\ClassMetadata
134
     */
135 327
    public function getTypeClass()
136
    {
137 327
        return $this->typeClass;
138
    }
139
140
    /**
141
     * INTERNAL:
142
     * Adds an element to a collection during hydration. This will automatically
143
     * complete bidirectional associations in the case of a one-to-many association.
144
     *
145
     * @param mixed $element The element to add.
146
     */
147 151
    public function hydrateAdd($element)
148
    {
149 151
        $this->collection->add($element);
150
151
        // If _backRefFieldName is set and its a one-to-many association,
152
        // we need to set the back reference.
153 151
        if ($this->backRefFieldName && $this->association instanceof OneToManyAssociationMetadata) {
154 99
            $inversedAssociation = $this->typeClass->getProperty($this->backRefFieldName);
155
156
            // Set back reference to owner
157 99
            $inversedAssociation->setValue($element, $this->owner);
158
159 99
            $this->em->getUnitOfWork()->setOriginalEntityProperty(
160 99
                spl_object_id($element),
161 99
                $this->backRefFieldName,
162 99
                $this->owner
163
            );
164
        }
165 151
    }
166
167
    /**
168
     * INTERNAL:
169
     * Sets a keyed element in the collection during hydration.
170
     *
171
     * @param mixed $key     The key to set.
172
     * @param mixed $element The element to set.
173
     */
174 34
    public function hydrateSet($key, $element)
175
    {
176 34
        $this->collection->set($key, $element);
177
178
        // If _backRefFieldName is set, then the association is bidirectional
179
        // and we need to set the back reference.
180 34
        if ($this->backRefFieldName && $this->association instanceof OneToManyAssociationMetadata) {
181 21
            $inversedAssociation = $this->typeClass->getProperty($this->backRefFieldName);
182
183
            // Set back reference to owner
184 21
            $inversedAssociation->setValue($element, $this->owner);
185
186 21
            $this->em->getUnitOfWork()->setOriginalEntityProperty(
187 21
                spl_object_id($element),
188 21
                $this->backRefFieldName,
189 21
                $this->owner
190
            );
191
        }
192 34
    }
193
194
    /**
195
     * Initializes the collection by loading its contents from the database
196
     * if the collection is not yet initialized.
197
     */
198 258
    public function initialize()
199
    {
200 258
        if ($this->initialized || ! $this->association) {
201 200
            return;
202
        }
203
204 138
        $this->doInitialize();
205
206 138
        $this->initialized = true;
207 138
    }
208
209
    /**
210
     * INTERNAL:
211
     * Tells this collection to take a snapshot of its current state.
212
     */
213 576
    public function takeSnapshot()
214
    {
215 576
        $this->snapshot = $this->collection->toArray();
216 576
        $this->isDirty  = false;
217 576
    }
218
219
    /**
220
     * INTERNAL:
221
     * Returns the last snapshot of the elements in the collection.
222
     *
223
     * @return object[] The last snapshot of the elements.
224
     */
225 24
    public function getSnapshot()
226
    {
227 24
        return $this->snapshot;
228
    }
229
230
    /**
231
     * INTERNAL:
232
     * getDeleteDiff
233
     *
234
     * @return object[]
235
     */
236 327
    public function getDeleteDiff()
237
    {
238 327
        $collectionItems = $this->collection->toArray();
239
240 327
        return array_values(array_diff_key(
241 327
            array_combine(array_map('spl_object_id', $this->snapshot), $this->snapshot),
242 327
            array_combine(array_map('spl_object_id', $collectionItems), $collectionItems)
243
        ));
244
    }
245
246
    /**
247
     * INTERNAL:
248
     * getInsertDiff
249
     *
250
     * @return object[]
251
     */
252 327
    public function getInsertDiff()
253
    {
254 327
        $collectionItems = $this->collection->toArray();
255
256 327
        return array_values(array_diff_key(
257 327
            array_combine(array_map('spl_object_id', $collectionItems), $collectionItems),
258 327
            array_combine(array_map('spl_object_id', $this->snapshot), $this->snapshot)
259
        ));
260
    }
261
262
    /**
263
     * INTERNAL: Gets the association mapping of the collection.
264
     *
265
     * @return AssociationMetadata
266
     */
267 542
    public function getMapping()
268
    {
269 542
        return $this->association;
270
    }
271
272
    /**
273
     * Marks this collection as changed/dirty.
274
     */
275 146
    private function changed()
276
    {
277 146
        if ($this->isDirty) {
278 74
            return;
279
        }
280
281 146
        $this->isDirty = true;
282
283 146
        if ($this->association instanceof ManyToManyAssociationMetadata &&
284 146
            $this->owner &&
285 146
            $this->association->isOwningSide() &&
286 146
            $this->em->getClassMetadata(get_class($this->owner))->changeTrackingPolicy === ChangeTrackingPolicy::NOTIFY) {
0 ignored issues
show
Bug introduced by
Accessing changeTrackingPolicy on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
287 1
            $this->em->getUnitOfWork()->scheduleForSynchronization($this->owner);
288
        }
289 146
    }
290
291
    /**
292
     * Gets a boolean flag indicating whether this collection is dirty which means
293
     * its state needs to be synchronized with the database.
294
     *
295
     * @return bool TRUE if the collection is dirty, FALSE otherwise.
296
     */
297 784
    public function isDirty()
298
    {
299 784
        return $this->isDirty;
300
    }
301
302
    /**
303
     * Sets a boolean flag, indicating whether this collection is dirty.
304
     *
305
     * @param bool $dirty Whether the collection should be marked dirty or not.
306
     */
307 817
    public function setDirty($dirty)
308
    {
309 817
        $this->isDirty = $dirty;
310 817
    }
311
312
    /**
313
     * Sets the initialized flag of the collection, forcing it into that state.
314
     *
315
     * @param bool $bool
316
     */
317 859
    public function setInitialized($bool)
318
    {
319 859
        $this->initialized = $bool;
320 859
    }
321
322
    /**
323
     * {@inheritdoc}
324
     */
325 16
    public function remove($key)
326
    {
327
        // TODO: If the keys are persistent as well (not yet implemented)
328
        //       and the collection is not initialized and orphanRemoval is
329
        //       not used we can issue a straight SQL delete/update on the
330
        //       association (table). Without initializing the collection.
331 16
        $removed = parent::remove($key);
332
333 16
        if (! $removed) {
334
            return $removed;
335
        }
336
337 16
        $this->changed();
338
339 16
        if ($this->association !== null &&
340 16
            $this->association instanceof ToManyAssociationMetadata &&
341 16
            $this->owner &&
342 16
            $this->association->isOrphanRemoval()) {
343 4
            $this->em->getUnitOfWork()->scheduleOrphanRemoval($removed);
344
        }
345
346 16
        return $removed;
347
    }
348
349
    /**
350
     * {@inheritdoc}
351
     */
352 25
    public function removeElement($element)
353
    {
354 25
        if (! $this->initialized &&
355 25
            $this->association !== null &&
356 25
            $this->association->getFetchMode() === FetchMode::EXTRA_LAZY) {
357 13
            if ($this->collection->contains($element)) {
358
                return $this->collection->removeElement($element);
359
            }
360
361 13
            $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
362
363 13
            return $persister->removeElement($this, $element);
364
        }
365
366 12
        $removed = parent::removeElement($element);
367
368 12
        if (! $removed) {
369
            return $removed;
370
        }
371
372 12
        $this->changed();
373
374 12
        if ($this->association !== null &&
375 12
            $this->association instanceof ToManyAssociationMetadata &&
376 12
            $this->owner &&
377 12
            $this->association->isOrphanRemoval()) {
378 3
            $this->em->getUnitOfWork()->scheduleOrphanRemoval($element);
379
        }
380
381 12
        return $removed;
382
    }
383
384
    /**
385
     * {@inheritdoc}
386
     */
387 28
    public function containsKey($key)
388
    {
389 28
        if (! $this->initialized &&
390 28
            $this->association !== null &&
391 28
            $this->association->getFetchMode() === FetchMode::EXTRA_LAZY &&
392 28
            $this->association->getIndexedBy()) {
393 11
            $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
394
395 11
            return $this->collection->containsKey($key) || $persister->containsKey($this, $key);
396
        }
397
398 17
        return parent::containsKey($key);
399
    }
400
401
    /**
402
     * {@inheritdoc}
403
     */
404 34
    public function contains($element)
405
    {
406 34
        if (! $this->initialized &&
407 34
            $this->association !== null &&
408 34
            $this->association->getFetchMode() === FetchMode::EXTRA_LAZY) {
409 17
            $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
410
411 17
            return $this->collection->contains($element) || $persister->contains($this, $element);
412
        }
413
414 17
        return parent::contains($element);
415
    }
416
417
    /**
418
     * {@inheritdoc}
419
     */
420 85
    public function get($key)
421
    {
422 85
        if (! $this->initialized &&
423 85
            $this->association !== null &&
424 85
            $this->association->getFetchMode() === FetchMode::EXTRA_LAZY &&
425 85
            $this->association->getIndexedBy()) {
426 5
            if (! $this->typeClass->isIdentifierComposite() && $this->typeClass->isIdentifier($this->association->getIndexedBy())) {
427 1
                return $this->em->find($this->typeClass->getClassName(), $key);
428
            }
429
430 4
            return $this->em->getUnitOfWork()->getCollectionPersister($this->association)->get($this, $key);
431
        }
432
433 80
        return parent::get($key);
434
    }
435
436
    /**
437
     * {@inheritdoc}
438
     */
439 165
    public function count()
440
    {
441 165
        if (! $this->initialized &&
442 165
            $this->association !== null &&
443 165
            $this->association->getFetchMode() === FetchMode::EXTRA_LAZY) {
444 32
            $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
445
446 32
            return $persister->count($this) + ($this->isDirty ? $this->collection->count() : 0);
447
        }
448
449 133
        return parent::count();
450
    }
451
452
    /**
453
     * {@inheritdoc}
454
     */
455 1
    public function set($key, $value)
456
    {
457 1
        parent::set($key, $value);
458
459 1
        $this->changed();
460
461 1
        if (is_object($value) && $this->em) {
462 1
            $this->em->getUnitOfWork()->cancelOrphanRemoval($value);
463
        }
464 1
    }
465
466
    /**
467
     * {@inheritdoc}
468
     */
469 114
    public function add($value)
470
    {
471 114
        $this->collection->add($value);
472
473 114
        $this->changed();
474
475 114
        if (is_object($value) && $this->em) {
476 113
            $this->em->getUnitOfWork()->cancelOrphanRemoval($value);
477
        }
478
479 114
        return true;
480
    }
481
482
    /* ArrayAccess implementation */
483
484
    /**
485
     * {@inheritdoc}
486
     */
487 17
    public function offsetExists($offset)
488
    {
489 17
        return $this->containsKey($offset);
490
    }
491
492
    /**
493
     * {@inheritdoc}
494
     */
495 61
    public function offsetGet($offset)
496
    {
497 61
        return $this->get($offset);
498
    }
499
500
    /**
501
     * {@inheritdoc}
502
     */
503 93
    public function offsetSet($offset, $value)
504
    {
505 93
        if (! isset($offset)) {
506 93
            $this->add($value);
507 93
            return;
508
        }
509
510
        $this->set($offset, $value);
511
    }
512
513
    /**
514
     * {@inheritdoc}
515
     */
516 6
    public function offsetUnset($offset)
517
    {
518 6
        return $this->remove($offset);
519
    }
520
521
    /**
522
     * {@inheritdoc}
523
     */
524 816
    public function isEmpty()
525
    {
526 816
        if ($this->initialized) {
527 816
            return $this->collection->isEmpty();
528
        }
529
530 1
        return $this->collection->isEmpty() && ! $this->count();
531
    }
532
533
    /**
534
     * {@inheritdoc}
535
     */
536 21
    public function clear()
537
    {
538 21
        if ($this->initialized && $this->isEmpty()) {
539 1
            $this->collection->clear();
540
541 1
            return;
542
        }
543
544 20
        $uow = $this->em->getUnitOfWork();
545
546 20
        if ($this->owner !== null &&
547 20
            $this->association !== null &&
548 20
            $this->association->isOrphanRemoval()) {
549
            // we need to initialize here, as orphan removal acts like implicit cascadeRemove,
550
            // hence for event listeners we need the objects in memory.
551 6
            $this->initialize();
552
553 6
            foreach ($this->collection as $element) {
554 6
                $uow->scheduleOrphanRemoval($element);
555
            }
556
        }
557
558 20
        $this->collection->clear();
559
560 20
        $this->initialized = true; // direct call, {@link initialize()} is too expensive
561
562 20
        if ($this->association->isOwningSide() && $this->owner) {
563 14
            $this->changed();
564
565 14
            $uow->scheduleCollectionDeletion($this);
566
567 14
            $this->takeSnapshot();
568
        }
569 20
    }
570
571
    /**
572
     * Called by PHP when this collection is serialized. Ensures that only the
573
     * elements are properly serialized.
574
     *
575
     * {@internal Tried to implement Serializable first but that did not work well
576
     *            with circular references. This solution seems simpler and works well. }}
577
     *
578
     * @return string[]
579
     */
580
    public function __sleep()
581
    {
582
        return ['collection', 'initialized'];
583
    }
584
585
    /**
586
     * Extracts a slice of $length elements starting at position $offset from the Collection.
587
     *
588
     * If $length is null it returns all elements from $offset to the end of the Collection.
589
     * Keys have to be preserved by this method. Calling this method will only return the
590
     * selected slice and NOT change the elements contained in the collection slice is called on.
591
     *
592
     * @param int      $offset
593
     * @param int|null $length
594
     *
595
     * @return object[]
596
     */
597 15
    public function slice($offset, $length = null)
598
    {
599 15
        if (! $this->initialized &&
600 15
            ! $this->isDirty &&
601 15
            $this->association !== null &&
602 15
            $this->association->getFetchMode() === FetchMode::EXTRA_LAZY) {
603 13
            $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
604
605 13
            return $persister->slice($this, $offset, $length);
606
        }
607
608 2
        return parent::slice($offset, $length);
609
    }
610
611
    /**
612
     * Cleans up internal state of cloned persistent collection.
613
     *
614
     * The following problems have to be prevented:
615
     * 1. Added entities are added to old PC
616
     * 2. New collection is not dirty, if reused on other entity nothing
617
     * changes.
618
     * 3. Snapshot leads to invalid diffs being generated.
619
     * 4. Lazy loading grabs entities from old owner object.
620
     * 5. New collection is connected to old owner and leads to duplicate keys.
621
     */
622 11
    public function __clone()
623
    {
624 11
        if (is_object($this->collection)) {
625 11
            $this->collection = clone $this->collection;
626
        }
627
628 11
        $this->initialize();
629
630 11
        $this->owner    = null;
631 11
        $this->snapshot = [];
632
633 11
        $this->changed();
634 11
    }
635
636
    /**
637
     * Selects all elements from a selectable that match the expression and
638
     * return a new collection containing these elements.
639
     *
640
     * @return Collection|object[]
641
     *
642
     * @throws \RuntimeException
643
     */
644 19
    public function matching(Criteria $criteria)
645
    {
646 19
        if ($this->isDirty) {
647 3
            $this->initialize();
648
        }
649
650 19
        if ($this->initialized) {
651 3
            return $this->collection->matching($criteria);
0 ignored issues
show
Bug introduced by
The method matching() does not exist on Doctrine\Common\Collections\Collection. It seems like you code against a sub-type of said class. However, the method does not exist in Doctrine\Common\Collections\AbstractLazyCollection. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

651
            return $this->collection->/** @scrutinizer ignore-call */ matching($criteria);
Loading history...
652
        }
653
654 16
        if ($this->association instanceof ManyToManyAssociationMetadata) {
655 12
            $persister = $this->em->getUnitOfWork()->getCollectionPersister($this->association);
656
657 12
            return new ArrayCollection($persister->loadCriteria($this, $criteria));
658
        }
659
660 4
        $builder         = Criteria::expr();
661 4
        $ownerExpression = $builder->eq($this->backRefFieldName, $this->owner);
662 4
        $expression      = $criteria->getWhereExpression();
663 4
        $expression      = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression;
664
665 4
        $criteria = clone $criteria;
666 4
        $criteria->where($expression);
667
668 4
        $persister = $this->em->getUnitOfWork()->getEntityPersister($this->association->getTargetEntity());
669
670 4
        return ($this->association->getFetchMode() === FetchMode::EXTRA_LAZY)
671 2
            ? new LazyCriteriaCollection($persister, $criteria)
672 4
            : new ArrayCollection($persister->loadCriteria($criteria));
673
    }
674
675
    /**
676
     * Retrieves the wrapped Collection instance.
677
     *
678
     * @return Collection|object[]
679
     */
680 777
    public function unwrap()
681
    {
682 777
        return $this->collection;
683
    }
684
685
    /**
686
     * {@inheritdoc}
687
     */
688 138
    protected function doInitialize()
689
    {
690
        // Has NEW objects added through add(). Remember them.
691 138
        $newlyAddedDirtyObjects = [];
692
693 138
        if ($this->isDirty) {
694 17
            $newlyAddedDirtyObjects = $this->collection->toArray();
695
        }
696
697 138
        $this->collection->clear();
698 138
        $this->em->getUnitOfWork()->loadCollection($this);
699 138
        $this->takeSnapshot();
700
701 138
        if ($newlyAddedDirtyObjects) {
702 17
            $this->restoreNewObjectsInDirtyCollection($newlyAddedDirtyObjects);
703
        }
704 138
    }
705
706
    /**
707
     * @param object[] $newObjects
708
     *
709
     * Note: the only reason why this entire looping/complexity is performed via `spl_object_id`
710
     *       is because we want to prevent using `array_udiff()`, which is likely to cause very
711
     *       high overhead (complexity of O(n^2)). `array_diff_key()` performs the operation in
712
     *       core, which is faster than using a callback for comparisons
713
     */
714 17
    private function restoreNewObjectsInDirtyCollection(array $newObjects) : void
715
    {
716 17
        $loadedObjects               = $this->collection->toArray();
717 17
        $newObjectsByOid             = array_combine(array_map('spl_object_id', $newObjects), $newObjects);
718 17
        $loadedObjectsByOid          = array_combine(array_map('spl_object_id', $loadedObjects), $loadedObjects);
719 17
        $newObjectsThatWereNotLoaded = array_diff_key($newObjectsByOid, $loadedObjectsByOid);
720
721 17
        if ($newObjectsThatWereNotLoaded) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $newObjectsThatWereNotLoaded 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...
722
            // Reattach NEW objects added through add(), if any.
723 16
            array_walk($newObjectsThatWereNotLoaded, [$this->collection, 'add']);
724
725 16
            $this->isDirty = true;
726
        }
727 17
    }
728
}
729