Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
BaseCollection.php
1<?php
2
3namespace Bitrix\Catalog\v2;
4
7
16abstract class BaseCollection implements \IteratorAggregate, \Countable
17{
19 private $parent;
20
22 protected $items = [];
24 protected $removedItems = [];
25
27 private $iteratorCallback;
29 private $loaded = false;
31 private $parentChanged = false;
32
33 public function getParent(): ?BaseEntity
34 {
35 return $this->parent;
36 }
37
38 public function setParent(?BaseEntity $parent): self
39 {
40 if ($this->parent !== null && $this->parent !== $parent)
41 {
42 $this->parentChanged = true;
43 }
44
45 $this->parent = $parent;
46
47 return $this;
48 }
49
50 public function add(BaseEntity ...$items): self
51 {
52 foreach ($items as $item)
53 {
54 $this->addInternal($item);
55 }
56
57 return $this;
58 }
59
60 protected function addInternal(BaseEntity $item): void
61 {
62 $item->setParentCollection($this);
63 $this->setItem($item);
64 }
65
66 public function remove(BaseEntity ...$items): self
67 {
68 foreach ($items as $item)
69 {
70 if ($item->getParentCollection() !== $this)
71 {
72 continue;
73 }
74
75 $item->setParentCollection(null);
76 $this->unsetItem($item);
77 }
78
79 return $this;
80 }
81
82 private function setItem(BaseEntity $item): self
83 {
84 // ToDo merge changed items? same sku with different hashes...
85 $this->items[$item->getHash()] = $item;
86 unset($this->removedItems[$item->getHash()]);
87
88 return $this;
89 }
90
91 private function unsetItem(BaseEntity $item): self
92 {
93 unset($this->items[$item->getHash()]);
94 $this->removedItems[$item->getHash()] = $item;
95
96 return $this;
97 }
98
104 public function clearRemoved(BaseEntity ...$items): self
105 {
106 foreach ($items as $item)
107 {
108 unset($this->removedItems[$item->getHash()]);
109 }
110
111 return $this;
112 }
113
118 public function clearChanged(): self
119 {
120 foreach ($this->items as $item)
121 {
122 $item->clearChangedFields();
123 }
124
125 $this->clearRemoved(...$this->getRemovedItems());
126
127 return $this;
128 }
129
130 public function isEmpty(): bool
131 {
132 return empty($this->items);
133 }
134
135 public function isChanged(): bool
136 {
137 if ($this->parentChanged)
138 {
139 return true;
140 }
141
142 if (!empty($this->removedItems))
143 {
144 return true;
145 }
146
147 foreach ($this->items as $entity)
148 {
149 if ($entity->isChanged())
150 {
151 return true;
152 }
153 }
154
155 return false;
156 }
157
158 public function findById(int $id): ?BaseEntity
159 {
160 foreach ($this->getIterator() as $item)
161 {
162 if ($item->getId() === $id)
163 {
164 return $item;
165 }
166 }
167
168 return null;
169 }
170
171 public function getFirst(callable $callback = null): ?BaseEntity
172 {
173 if ((empty($this->items) || $callback) && !$this->isLoaded())
174 {
175 $this->loadItems();
176 }
177
178 if ($callback)
179 {
180 foreach ($this->getIterator() as $item)
181 {
182 if ($callback($item))
183 {
184 return $item;
185 }
186 }
187 }
188 else
189 {
190 return reset($this->items) ?: null;
191 }
192
193 return null;
194 }
195
196 public function toArray(): array
197 {
198 $result = [];
199 $counter = 0;
200
201 foreach ($this->items as $entity)
202 {
203 $fields = $entity->getFields();
204
205 if ($entity->isNew())
206 {
207 $result['n' . $counter++] = $fields;
208 }
209 else
210 {
211 $result[$entity->getId()] = $fields;
212 }
213 }
214
215 return $result;
216 }
217
218 /*public function max($field)
219 {
220 return array_reduce($this->collection, static function ($result, $item) use ($field) {
221 $value = $item[$field] ?? null;
222
223 return $result === null || $value > $result ? $value : $result;
224 });
225 }
226
227 public function min($field)
228 {
229 return array_reduce($this->collection, static function ($result, $item) use ($field) {
230 $value = $item[$field] ?? null;
231
232 return $result === null || $value < $result ? $value : $result;
233 });
234 }
235
236 public function avg($field)
237 {
238 return array_sum(array_column($this->collection, $field)) / count($this->collection);
239 }*/
240
244 public function getRemovedItems(): \ArrayIterator
245 {
246 return new \ArrayIterator(array_values($this->removedItems));
247 }
248
252 public function getIterator(): \Traversable
253 {
254 $this->loadItems();
255
256 // workaround - spread operator (...) doesn't work with associative arrays
257 return new \ArrayIterator(array_values($this->items));
258 }
259
260 protected function loadItems(): void
261 {
262 if ($this->iteratorCallback && !$this->isLoaded())
263 {
264 $this->loadByIteratorCallback();
265 }
266
267 $this->loaded = true;
268 }
269
273 protected function isLoaded(): bool
274 {
275 return $this->loaded;
276 }
277
278 protected function loadByIteratorCallback(): void
279 {
280 $iterator = $this->iteratorCallback;
281 $params = [
282 'filter' => $this->getAlreadyLoadedFilter(),
283 ];
284
285 foreach ($iterator($params) as $entity)
286 {
287 $this->addInternal($entity);
288 }
289 }
290
291 protected function getAlreadyLoadedFilter(): array
292 {
293 return [];
294 }
295
301 public function setIteratorCallback(\Closure $iteratorCallback): BaseCollection
302 {
303 if ($this->iteratorCallback !== null)
304 {
305 throw new InvalidOperationException('Collection iterator already exists.');
306 }
307
308 $this->iteratorCallback = $iteratorCallback;
309
310 return $this;
311 }
312
317 public function saveInternal(): Result
318 {
319 $result = new Result();
320
321 if ($this->isChanged())
322 {
323 foreach ($this->items as $item)
324 {
325 $res = $item->saveInternal();
326
327 if (!$res->isSuccess())
328 {
329 $result->addErrors($res->getErrors());
330 }
331 }
332
333 foreach ($this->getRemovedItems() as $item)
334 {
335 $res = $item->deleteInternal();
336
337 if ($res->isSuccess())
338 {
339 $this->clearRemoved($item);
340 }
341 else
342 {
343 $result->addErrors($res->getErrors());
344 }
345 }
346 }
347
348 return $result;
349 }
350
355 public function deleteInternal(): Result
356 {
357 $result = new Result();
358
359 $this->loadItems();
360 $items = array_merge($this->items, $this->removedItems);
361
362 foreach ($items as $item)
363 {
364 $res = $item->deleteInternal();
365
366 if (!$res->isSuccess())
367 {
368 $result->addErrors($res->getErrors());
369 }
370 }
371
372 return $result;
373 }
374
375 public function count(): int
376 {
377 return count($this->getIterator());
378 }
379}
clearRemoved(BaseEntity ... $items)
setIteratorCallback(\Closure $iteratorCallback)
getFirst(callable $callback=null)
setParentCollection(?BaseCollection $collection)