1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Bankiru\Api\Doctrine\Proxy; |
4
|
|
|
|
5
|
|
|
use Bankiru\Api\Doctrine\ApiEntityManager; |
6
|
|
|
use Bankiru\Api\Doctrine\Mapping\ApiMetadata; |
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 ScayTrase\Api\Rpc\Exception\RpcExceptionInterface; |
13
|
|
|
|
14
|
|
|
class ApiCollection extends AbstractLazyCollection implements Selectable |
15
|
|
|
{ |
16
|
|
|
/** @var ApiEntityManager */ |
17
|
|
|
private $manager; |
18
|
|
|
/** @var ApiMetadata */ |
19
|
|
|
private $metadata; |
20
|
|
|
/** @var object */ |
21
|
|
|
private $owner; |
22
|
|
|
/** @var array */ |
23
|
|
|
private $association; |
24
|
|
|
/** @var bool */ |
25
|
|
|
private $isDirty = false; |
26
|
|
|
/** @var array */ |
27
|
|
|
private $snapshot = []; |
28
|
|
|
/** @var string */ |
29
|
|
|
private $backRefFieldName; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* ApiCollection constructor. |
33
|
|
|
* |
34
|
|
|
* @param ApiEntityManager $manager |
35
|
|
|
* @param ApiMetadata $class |
36
|
|
|
* @param Collection $collection |
37
|
|
|
*/ |
38
|
12 |
|
public function __construct( |
39
|
|
|
ApiEntityManager $manager, |
40
|
|
|
ApiMetadata $class, |
41
|
|
|
Collection $collection = null |
42
|
|
|
) { |
43
|
12 |
|
$this->manager = $manager; |
44
|
12 |
|
$this->metadata = $class; |
45
|
12 |
|
$this->collection = $collection ?: new ArrayCollection(); |
46
|
12 |
|
$this->initialized = true; |
47
|
12 |
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @return boolean |
51
|
|
|
*/ |
52
|
2 |
|
public function isDirty() |
53
|
|
|
{ |
54
|
2 |
|
return $this->isDirty; |
55
|
|
|
} |
56
|
|
|
|
57
|
2 |
|
public function unwrap() |
58
|
|
|
{ |
59
|
2 |
|
return $this->collection; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* INTERNAL: |
64
|
|
|
* Tells this collection to take a snapshot of its current state. |
65
|
|
|
* |
66
|
|
|
* @return void |
67
|
|
|
*/ |
68
|
4 |
|
public function takeSnapshot() |
69
|
|
|
{ |
70
|
4 |
|
$this->snapshot = $this->collection->toArray(); |
71
|
4 |
|
$this->isDirty = false; |
72
|
4 |
|
} |
73
|
|
|
|
74
|
2 |
|
public function getMapping() |
75
|
|
|
{ |
76
|
2 |
|
return $this->association; |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @return object |
81
|
|
|
*/ |
82
|
3 |
|
public function getOwner() |
83
|
|
|
{ |
84
|
3 |
|
return $this->owner; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @param object $owner |
89
|
|
|
* @param array $assoc |
90
|
|
|
*/ |
91
|
12 |
|
public function setOwner($owner, array $assoc) |
92
|
|
|
{ |
93
|
12 |
|
$this->owner = $owner; |
94
|
12 |
|
$this->association = $assoc; |
95
|
12 |
|
$this->backRefFieldName = $assoc['inversedBy'] ?: $assoc['mappedBy']; |
96
|
12 |
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @param bool $dirty |
100
|
|
|
*/ |
101
|
2 |
|
public function setDirty($dirty) |
102
|
|
|
{ |
103
|
2 |
|
$this->isDirty = (bool)$dirty; |
104
|
2 |
|
} |
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* Initializes the collection by loading its contents from the database |
108
|
|
|
* if the collection is not yet initialized. |
109
|
|
|
* |
110
|
|
|
* @return void |
111
|
|
|
*/ |
112
|
3 |
|
public function initialize() |
113
|
|
|
{ |
114
|
3 |
|
if ($this->initialized || !$this->association) { |
|
|
|
|
115
|
2 |
|
return; |
116
|
|
|
} |
117
|
2 |
|
$this->doInitialize(); |
118
|
2 |
|
$this->initialized = true; |
119
|
2 |
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @return ApiMetadata |
123
|
|
|
*/ |
124
|
|
|
public function getMetadata() |
125
|
|
|
{ |
126
|
|
|
return $this->metadata; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* INTERNAL: |
131
|
|
|
* Adds an element to a collection during hydration. This will automatically |
132
|
|
|
* complete bidirectional associations in the case of a one-to-many association. |
133
|
|
|
* |
134
|
|
|
* @param mixed $element The element to add. |
135
|
|
|
* |
136
|
|
|
* @return void |
137
|
|
|
*/ |
138
|
|
|
public function hydrateAdd($element) |
139
|
|
|
{ |
140
|
|
|
$this->collection->add($element); |
141
|
|
|
// If _backRefFieldName is set and its a one-to-many association, |
142
|
|
|
// we need to set the back reference. |
143
|
|
|
if ($this->backRefFieldName && $this->association['type'] === ApiMetadata::ONE_TO_MANY) { |
144
|
|
|
// Set back reference to owner |
145
|
|
|
$this->metadata->getReflectionProperty($this->backRefFieldName)->setValue( |
146
|
|
|
$element, |
147
|
|
|
$this->owner |
148
|
|
|
); |
149
|
|
|
$this->manager->getUnitOfWork()->setOriginalEntityProperty( |
150
|
|
|
spl_object_hash($element), |
151
|
|
|
$this->backRefFieldName, |
152
|
|
|
$this->owner |
153
|
|
|
); |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* INTERNAL: |
159
|
|
|
* Sets a keyed element in the collection during hydration. |
160
|
|
|
* |
161
|
|
|
* @param mixed $key The key to set. |
162
|
|
|
* @param mixed $element The element to set. |
163
|
|
|
* |
164
|
|
|
* @return void |
165
|
|
|
*/ |
166
|
|
|
public function hydrateSet($key, $element) |
167
|
|
|
{ |
168
|
|
|
$this->collection->set($key, $element); |
169
|
|
|
// If _backRefFieldName is set, then the association is bidirectional |
170
|
|
|
// and we need to set the back reference. |
171
|
|
|
if ($this->backRefFieldName && $this->association['type'] === ApiMetadata::ONE_TO_MANY) { |
172
|
|
|
// Set back reference to owner |
173
|
|
|
$this->metadata->getReflectionProperty($this->backRefFieldName)->setValue( |
174
|
|
|
$element, |
175
|
|
|
$this->owner |
176
|
|
|
); |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* INTERNAL: |
182
|
|
|
* Returns the last snapshot of the elements in the collection. |
183
|
|
|
* |
184
|
|
|
* @return array The last snapshot of the elements. |
185
|
|
|
*/ |
186
|
|
|
public function getSnapshot() |
187
|
|
|
{ |
188
|
|
|
return $this->snapshot; |
189
|
|
|
} |
190
|
|
|
|
191
|
10 |
|
public function setInitialized($state) |
192
|
|
|
{ |
193
|
10 |
|
$this->initialized = (bool)$state; |
194
|
10 |
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* INTERNAL: |
198
|
|
|
* getDeleteDiff |
199
|
|
|
* |
200
|
|
|
* @return array |
201
|
|
|
*/ |
202
|
|
View Code Duplication |
public function getDeleteDiff() |
|
|
|
|
203
|
|
|
{ |
204
|
|
|
return array_udiff_assoc( |
205
|
|
|
$this->snapshot, |
206
|
|
|
$this->collection->toArray(), |
207
|
|
|
function ($a, $b) { |
208
|
|
|
return $a === $b ? 0 : 1; |
209
|
|
|
} |
210
|
|
|
); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* INTERNAL: |
215
|
|
|
* getInsertDiff |
216
|
|
|
* |
217
|
|
|
* @return array |
218
|
|
|
*/ |
219
|
|
View Code Duplication |
public function getInsertDiff() |
|
|
|
|
220
|
|
|
{ |
221
|
|
|
return array_udiff_assoc( |
222
|
|
|
$this->collection->toArray(), |
223
|
|
|
$this->snapshot, |
224
|
|
|
function ($a, $b) { |
225
|
|
|
return $a === $b ? 0 : 1; |
226
|
|
|
} |
227
|
|
|
); |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* {@inheritdoc} |
232
|
|
|
*/ |
233
|
|
View Code Duplication |
public function remove($key) |
|
|
|
|
234
|
|
|
{ |
235
|
|
|
$removed = parent::remove($key); |
236
|
|
|
if (!$removed) { |
237
|
|
|
return $removed; |
238
|
|
|
} |
239
|
|
|
$this->changed(); |
240
|
|
|
if ($this->association !== null && |
241
|
|
|
$this->association['type'] & ApiMetadata::TO_MANY && |
242
|
|
|
$this->owner && |
243
|
|
|
$this->association['orphanRemoval'] |
244
|
|
|
) { |
245
|
|
|
$this->manager->getUnitOfWork()->scheduleOrphanRemoval($removed); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
return $removed; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
/** |
252
|
|
|
* {@inheritdoc} |
253
|
|
|
*/ |
254
|
1 |
|
public function removeElement($element) |
255
|
|
|
{ |
256
|
1 |
|
if (!$this->initialized && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY) { |
257
|
|
|
if ($this->collection->contains($element)) { |
258
|
|
|
return $this->collection->removeElement($element); |
259
|
|
|
} |
260
|
|
|
$persister = $this->manager->getUnitOfWork()->getCollectionPersister($this->association); |
261
|
|
|
if ($persister->removeElement($this, $element)) { |
|
|
|
|
262
|
|
|
return $element; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
return null; |
266
|
|
|
} |
267
|
1 |
|
$removed = parent::removeElement($element); |
268
|
1 |
|
if (!$removed) { |
269
|
|
|
return $removed; |
270
|
|
|
} |
271
|
1 |
|
$this->changed(); |
272
|
1 |
|
if ($this->association !== null && |
273
|
1 |
|
$this->association['type'] & ApiMetadata::TO_MANY && |
274
|
1 |
|
$this->owner && |
275
|
1 |
|
$this->association['orphanRemoval'] |
276
|
1 |
|
) { |
277
|
|
|
$this->manager->getUnitOfWork()->scheduleOrphanRemoval($element); |
278
|
|
|
} |
279
|
|
|
|
280
|
1 |
|
return $removed; |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* {@inheritdoc} |
285
|
|
|
*/ |
286
|
|
View Code Duplication |
public function containsKey($key) |
|
|
|
|
287
|
|
|
{ |
288
|
|
|
if (!$this->initialized && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY |
289
|
|
|
&& isset($this->association['indexBy']) |
290
|
|
|
) { |
291
|
|
|
$persister = $this->manager->getUnitOfWork()->getCollectionPersister($this->association); |
292
|
|
|
|
293
|
|
|
return $this->collection->containsKey($key) || $persister->containsKey($this, $key); |
|
|
|
|
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
return parent::containsKey($key); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* {@inheritdoc} |
301
|
|
|
*/ |
302
|
1 |
View Code Duplication |
public function contains($element) |
|
|
|
|
303
|
|
|
{ |
304
|
1 |
|
if (!$this->initialized && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY) { |
305
|
|
|
$persister = $this->manager->getUnitOfWork()->getCollectionPersister($this->association); |
306
|
|
|
|
307
|
|
|
return $this->collection->contains($element) || $persister->contains($this, $element); |
|
|
|
|
308
|
|
|
} |
309
|
|
|
|
310
|
1 |
|
return parent::contains($element); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* {@inheritdoc} |
315
|
|
|
*/ |
316
|
|
|
public function get($key) |
317
|
|
|
{ |
318
|
|
|
if (!$this->initialized |
319
|
|
|
&& $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY |
320
|
|
|
&& isset($this->association['indexBy']) |
321
|
|
|
) { |
322
|
|
|
if (!$this->metadata->isIdentifierComposite() && |
323
|
|
|
$this->metadata->isIdentifier($this->association['indexBy']) |
324
|
|
|
) { |
325
|
|
|
return $this->manager->find($this->metadata->getName(), $key); |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
return $this->manager->getUnitOfWork()->getCollectionPersister($this->association)->get($this, $key); |
|
|
|
|
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
return parent::get($key); |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
/** |
335
|
|
|
* {@inheritdoc} |
336
|
|
|
*/ |
337
|
1 |
|
public function count() |
338
|
|
|
{ |
339
|
1 |
|
if (!$this->initialized && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY) { |
340
|
|
|
$persister = $this->manager->getUnitOfWork()->getCollectionPersister($this->association); |
341
|
|
|
|
342
|
|
|
return $persister->count($this) + ($this->isDirty ? $this->collection->count() : 0); |
|
|
|
|
343
|
|
|
} |
344
|
|
|
|
345
|
1 |
|
return parent::count(); |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
/** |
349
|
|
|
* {@inheritdoc} |
350
|
|
|
*/ |
351
|
|
View Code Duplication |
public function set($key, $value) |
|
|
|
|
352
|
|
|
{ |
353
|
|
|
parent::set($key, $value); |
354
|
|
|
$this->changed(); |
355
|
|
|
if (is_object($value) && $this->manager) { |
356
|
|
|
$this->manager->getUnitOfWork()->cancelOrphanRemoval($value); |
357
|
|
|
} |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* {@inheritdoc} |
362
|
|
|
*/ |
363
|
2 |
View Code Duplication |
public function add($value) |
|
|
|
|
364
|
|
|
{ |
365
|
2 |
|
$this->collection->add($value); |
366
|
2 |
|
$this->changed(); |
367
|
2 |
|
if (is_object($value) && $this->manager) { |
368
|
2 |
|
$this->manager->getUnitOfWork()->cancelOrphanRemoval($value); |
369
|
2 |
|
} |
370
|
|
|
|
371
|
2 |
|
return true; |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* {@inheritdoc} |
376
|
|
|
*/ |
377
|
|
|
public function offsetExists($offset) |
378
|
|
|
{ |
379
|
|
|
return $this->containsKey($offset); |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
/** |
383
|
|
|
* {@inheritdoc} |
384
|
|
|
*/ |
385
|
|
|
public function offsetGet($offset) |
386
|
|
|
{ |
387
|
|
|
return $this->get($offset); |
388
|
|
|
} |
389
|
|
|
/* ArrayAccess implementation */ |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* {@inheritdoc} |
393
|
|
|
*/ |
394
|
|
|
public function offsetSet($offset, $value) |
395
|
|
|
{ |
396
|
|
|
if (!isset($offset)) { |
397
|
|
|
return $this->add($value); |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
return $this->set($offset, $value); |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
/** |
404
|
|
|
* {@inheritdoc} |
405
|
|
|
*/ |
406
|
|
|
public function offsetUnset($offset) |
407
|
|
|
{ |
408
|
|
|
return $this->remove($offset); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* {@inheritdoc} |
413
|
|
|
*/ |
414
|
2 |
|
public function isEmpty() |
415
|
|
|
{ |
416
|
2 |
|
return $this->collection->isEmpty() && $this->count() === 0; |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
/** |
420
|
|
|
* {@inheritdoc} |
421
|
|
|
*/ |
422
|
|
|
public function clear() |
423
|
|
|
{ |
424
|
|
|
if ($this->initialized && $this->isEmpty()) { |
425
|
|
|
return; |
426
|
|
|
} |
427
|
|
|
$uow = $this->manager->getUnitOfWork(); |
428
|
|
|
if ($this->association['type'] & ApiMetadata::TO_MANY && |
429
|
|
|
$this->association['orphanRemoval'] && |
430
|
|
|
$this->owner |
431
|
|
|
) { |
432
|
|
|
// we need to initialize here, as orphan removal acts like implicit cascadeRemove, |
433
|
|
|
// hence for event listeners we need the objects in memory. |
434
|
|
|
$this->initialize(); |
435
|
|
|
foreach ($this->collection as $element) { |
436
|
|
|
$uow->scheduleOrphanRemoval($element); |
437
|
|
|
} |
438
|
|
|
} |
439
|
|
|
$this->collection->clear(); |
440
|
|
|
$this->initialized = true; // direct call, {@link initialize()} is too expensive |
441
|
|
|
if ($this->association['isOwningSide'] && $this->owner) { |
442
|
|
|
$this->changed(); |
443
|
|
|
$uow->scheduleCollectionDeletion($this); |
444
|
|
|
$this->takeSnapshot(); |
445
|
|
|
} |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
/** |
449
|
|
|
* Called by PHP when this collection is serialized. Ensures that only the |
450
|
|
|
* elements are properly serialized. |
451
|
|
|
* |
452
|
|
|
* Internal note: Tried to implement Serializable first but that did not work well |
453
|
|
|
* with circular references. This solution seems simpler and works well. |
454
|
|
|
* |
455
|
|
|
* @return array |
456
|
|
|
*/ |
457
|
|
|
public function __sleep() |
458
|
|
|
{ |
459
|
|
|
return ['collection', 'initialized']; |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
/** |
463
|
|
|
* Extracts a slice of $length elements starting at position $offset from the Collection. |
464
|
|
|
* |
465
|
|
|
* If $length is null it returns all elements from $offset to the end of the Collection. |
466
|
|
|
* Keys have to be preserved by this method. Calling this method will only return the |
467
|
|
|
* selected slice and NOT change the elements contained in the collection slice is called on. |
468
|
|
|
* |
469
|
|
|
* @param int $offset |
470
|
|
|
* @param int|null $length |
471
|
|
|
* |
472
|
|
|
* @return array |
473
|
|
|
*/ |
474
|
|
View Code Duplication |
public function slice($offset, $length = null) |
|
|
|
|
475
|
|
|
{ |
476
|
|
|
if (!$this->initialized && !$this->isDirty && $this->association['fetch'] === ApiMetadata::FETCH_EXTRA_LAZY) { |
477
|
|
|
$persister = $this->manager->getUnitOfWork()->getCollectionPersister($this->association); |
478
|
|
|
|
479
|
|
|
return $persister->slice($this, $offset, $length); |
|
|
|
|
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
return parent::slice($offset, $length); |
483
|
|
|
} |
484
|
|
|
|
485
|
|
|
/** |
486
|
|
|
* Cleans up internal state of cloned persistent collection. |
487
|
|
|
* |
488
|
|
|
* The following problems have to be prevented: |
489
|
|
|
* 1. Added entities are added to old PC |
490
|
|
|
* 2. New collection is not dirty, if reused on other entity nothing |
491
|
|
|
* changes. |
492
|
|
|
* 3. Snapshot leads to invalid diffs being generated. |
493
|
|
|
* 4. Lazy loading grabs entities from old owner object. |
494
|
|
|
* 5. New collection is connected to old owner and leads to duplicate keys. |
495
|
|
|
* |
496
|
|
|
* @return void |
497
|
|
|
*/ |
498
|
|
|
public function __clone() |
499
|
|
|
{ |
500
|
|
|
if (is_object($this->collection)) { |
501
|
|
|
$this->collection = clone $this->collection; |
502
|
|
|
} |
503
|
|
|
$this->initialize(); |
504
|
|
|
$this->owner = null; |
505
|
|
|
$this->snapshot = []; |
506
|
|
|
$this->changed(); |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
/** |
510
|
|
|
* Selects all elements from a selectable that match the expression and |
511
|
|
|
* return a new collection containing these elements. |
512
|
|
|
* |
513
|
|
|
* @param \Doctrine\Common\Collections\Criteria $criteria |
514
|
|
|
* |
515
|
|
|
* @return Collection |
516
|
|
|
* |
517
|
|
|
* @throws \RuntimeException |
518
|
|
|
*/ |
519
|
|
|
public function matching(Criteria $criteria) |
520
|
|
|
{ |
521
|
|
|
if ($this->isDirty) { |
522
|
|
|
$this->initialize(); |
523
|
|
|
} |
524
|
|
|
if ($this->initialized) { |
525
|
|
|
return $this->collection->matching($criteria); |
|
|
|
|
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
$builder = Criteria::expr(); |
529
|
|
|
$ownerExpression = $builder->eq($this->backRefFieldName, $this->owner); |
530
|
|
|
$expression = $criteria->getWhereExpression(); |
531
|
|
|
$expression = $expression ? $builder->andX($expression, $ownerExpression) : $ownerExpression; |
532
|
|
|
$criteria = clone $criteria; |
533
|
|
|
$criteria->where($expression); |
534
|
|
|
$persister = $this->manager->getUnitOfWork()->getEntityPersister($this->association['target']); |
535
|
|
|
|
536
|
|
|
return new LazyCriteriaCollection($persister, $criteria); |
537
|
|
|
} |
538
|
|
|
|
539
|
|
|
/** |
540
|
|
|
* Do the initialization logic |
541
|
|
|
* |
542
|
|
|
* @return void |
543
|
|
|
* @throws RpcExceptionInterface |
544
|
|
|
*/ |
545
|
2 |
|
protected function doInitialize() |
546
|
|
|
{ |
547
|
|
|
// Has NEW objects added through add(). Remember them. |
548
|
2 |
|
$newObjects = []; |
549
|
2 |
|
if ($this->isDirty) { |
550
|
|
|
$newObjects = $this->collection->toArray(); |
551
|
|
|
} |
552
|
2 |
|
$this->collection->clear(); |
553
|
2 |
|
$this->manager->getUnitOfWork()->loadCollection($this); |
554
|
2 |
|
$this->takeSnapshot(); |
555
|
|
|
// Reattach NEW objects added through add(), if any. |
556
|
2 |
|
if ($newObjects) { |
|
|
|
|
557
|
|
|
foreach ($newObjects as $obj) { |
558
|
|
|
$this->collection->add($obj); |
559
|
|
|
} |
560
|
|
|
$this->isDirty = true; |
561
|
|
|
} |
562
|
2 |
|
} |
563
|
|
|
|
564
|
|
|
/** |
565
|
|
|
* Marks this collection as changed/dirty. |
566
|
|
|
* |
567
|
|
|
* @return void |
568
|
|
|
*/ |
569
|
3 |
|
private function changed() |
570
|
|
|
{ |
571
|
3 |
|
$this->isDirty = true; |
572
|
3 |
|
} |
573
|
|
|
} |
574
|
|
|
|
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.