Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
PropertyRepository.php
1<?php
2
4
11use Bitrix\Main\Entity\ReferenceField;
15
25{
27 protected $factory;
29 private $propertyValueFactory;
30
31 public function __construct(PropertyFactory $factory, PropertyValueFactory $propertyValueFactory)
32 {
33 $this->factory = $factory;
34 $this->propertyValueFactory = $propertyValueFactory;
35 }
36
37 public function getEntityById(int $id): ?BaseEntity
38 {
39 if ($id <= 0)
40 {
41 throw new \OutOfRangeException($id);
42 }
43
44 $entities = $this->getEntitiesBy([
45 'filter' => [
46 '=ID' => $id,
47 ],
48 ]);
49
50 return reset($entities) ?: null;
51 }
52
53 public function getEntitiesBy($params, array $propertySettings = []): array
54 {
55 $entities = [];
56
57 $sortedSettings = [];
58 foreach ($propertySettings as $setting)
59 {
60 if ((int)$setting['ID'] > 0)
61 {
62 $sortedSettings[(int)$setting['ID']] = $setting;
63 }
64 }
65
66 foreach ($this->getList((array)$params) as $elementId => $properties)
67 {
68 if (!is_array($properties))
69 {
70 continue;
71 }
72
73 foreach ($properties as $propertyId => $item)
74 {
75 $settings = [];
76 if ($sortedSettings[$propertyId])
77 {
78 $settings = $sortedSettings[$propertyId];
79 $settings['IBLOCK_ELEMENT_ID'] = $elementId;
80 }
81 $entities[] = $this->createEntity($item, $settings);
82 }
83 }
84
85 return $entities;
86 }
87
88 public function save(BaseEntity ...$entities): Result
89 {
90 $result = new Result();
91
93 $parentEntity = null;
94 $props = [];
95
97 foreach ($entities as $property)
98 {
99 if ($parentEntity && $parentEntity !== $property->getParent())
100 {
101 $result->addError(new Error('Saving should only be done with properties of a common parent.'));
102 }
103
104 if ($parentEntity === null)
105 {
106 $parentEntity = $property->getParent();
107 }
108
109 $valueCollection = $property->getPropertyValueCollection();
110
111 $props[$property->getId()] = $valueCollection->toArray();
112
113 if ($property->getPropertyType() === PropertyTable::TYPE_FILE)
114 {
115 foreach ($props[$property->getId()] as $id => $prop)
116 {
117 if (is_numeric($id))
118 {
119 $props[$property->getId()][$id] = \CIBlock::makeFilePropArray(
120 $prop,
121 $prop['VALUE'] === '',
122 $prop['DESCRIPTION'],
123 ['allow_file_id' => true]
124 );
125 }
126 }
127
128 foreach ($valueCollection->getRemovedItems() as $removed)
129 {
130 if ($removed->isNew())
131 {
132 continue;
133 }
134
135 $fieldsToDelete = \CIBlock::makeFilePropArray($removed->getFields(), true);
136 $props[$property->getId()][$removed->getId()] = $fieldsToDelete;
137 }
138 }
139 }
140
141 if (!$parentEntity)
142 {
143 $result->addError(new Error('Parent entity not found while saving properties.'));
144 }
145
146 if (!($parentEntity instanceof BaseIblockElementEntity))
147 {
148 $result->addError(new Error(sprintf(
149 'Parent entity of property must be an instance of {%s}.',
150 BaseIblockElementEntity::class
151 )));
152 }
153
154 if (!empty($props) && $result->isSuccess())
155 {
156 $element = new \CIBlockElement();
157 $res = $element->update($parentEntity->getId(), [
158 'PROPERTY_VALUES' => $props,
159 ]);
160 if (!$res)
161 {
162 $result->addError(new Error($element->LAST_ERROR));
163 }
164 }
165
166 return $result;
167 }
168
169 public function delete(BaseEntity ...$entities): Result
170 {
171 return new Result();
172 }
173
175 {
176 if ($entity->isNew())
177 {
178 return $this->loadCollection([], $entity);
179 }
180
181 $result = $this->getList([
182 'filter' => [
183 'IBLOCK_ID' => $entity->getIblockId(),
184 'ID' => $entity->getId(),
185 ],
186 ]);
187
188 $entityFields = $result[$entity->getId()] ?? [];
189
190 return $this->loadCollection($entityFields, $entity);
191 }
192
193 protected function getList(array $params): array
194 {
195 $result = [];
196
197 $filter = $params['filter'] ?? [];
198
199 $propertyValuesIterator = \CIBlockElement::getPropertyValues($filter['IBLOCK_ID'], $filter, true);
200 while ($propertyValues = $propertyValuesIterator->fetch())
201 {
202 $descriptions = $propertyValues['DESCRIPTION'] ?? [];
203 $propertyValueIds = $propertyValues['PROPERTY_VALUE_ID'] ?? [];
204 $elementId = $propertyValues['IBLOCK_ELEMENT_ID'];
205 unset($propertyValues['IBLOCK_ELEMENT_ID'], $propertyValues['PROPERTY_VALUE_ID'], $propertyValues['DESCRIPTION']);
206
207 $entityFields = [];
208 // ToDo empty properties with false (?: '') or null?
209 foreach ($propertyValues as $id => $value)
210 {
211 $entityFields[$id] = [];
212 $description = $descriptions[$id] ?? null;
213
214 if ($value !== false || $description !== null)
215 {
216 if (is_array($value))
217 {
218 foreach ($value as $key => $item)
219 {
220 $fields = [
221 'VALUE' => $item ?? '',
222 'DESCRIPTION' => empty($description[$key]) ? null : $description[$key],
223 ];
224
225 if (isset($propertyValueIds[$id][$key]))
226 {
227 $fields['ID'] = $propertyValueIds[$id][$key];
228 }
229
230 $entityFields[$id][$key] = $fields;
231 }
232 }
233 else
234 {
235 $fields = [
236 'VALUE' => $value ?? '',
237 'DESCRIPTION' => empty($descriptions[$id]) ? null : $descriptions[$id],
238 ];
239
240 if (isset($propertyValueIds[$id]))
241 {
242 $fields['ID'] = $propertyValueIds[$id];
243 }
244
245 $entityFields[$id][] = $fields;
246 }
247 }
248 }
249
250 $result[$elementId] = $entityFields;
251 }
252
253 return $result;
254 }
255
257 {
258 return $this->factory->createCollection();
259 }
260
261 protected function loadCollection(array $entityFields, BaseIblockElementEntity $parent): PropertyCollection
262 {
263 $propertySettings = [];
264
265 if ($parent instanceof HasSectionCollection)
266 {
267 $linkedPropertyIds = $this->getLinkedPropertyIds($parent->getIblockId(), $parent);
268 if (!empty($linkedPropertyIds))
269 {
270 $propertySettings = $this->getPropertiesSettingsByFilter([
271 '@ID' => $linkedPropertyIds,
272 ]);
273 }
274 }
275 else
276 {
277 // variation properties don't use any section links right now
278 $propertySettings = $this->getPropertiesSettingsByFilter([
279 '=IBLOCK_ID' => $parent->getIblockId(),
280 ]);
281 }
282
283 $collection = $this->createCollection();
284
285 foreach ($propertySettings as $settings)
286 {
287 $fields = $entityFields[$settings['ID']] ?? [];
288 $settings['IBLOCK_ELEMENT_ID'] = $parent->getId();
289 $property = $this->createEntity($fields, $settings);
290
291 $collection->add($property);
292 }
293
294 return $collection;
295 }
296
297 protected function getLinkedPropertyIds(int $iblockId, HasSectionCollection $parent): array
298 {
299 $linkedPropertyIds = [$this->loadPropertyIdsWithoutAnyLink($iblockId)];
300
301 if ($parent->getSectionCollection()->isEmpty())
302 {
303 $linkedPropertyIds[] = array_keys(\CIBlockSectionPropertyLink::getArray($iblockId));
304 }
305
307 foreach ($parent->getSectionCollection() as $section)
308 {
309 $linkedPropertyIds[] = array_keys(\CIBlockSectionPropertyLink::getArray($iblockId, $section->getValue()));
310 }
311
312 if (!empty($linkedPropertyIds))
313 {
314 $linkedPropertyIds = array_merge(...$linkedPropertyIds);
315 Collection::normalizeArrayValuesByInt($linkedPropertyIds, false);
316 $linkedPropertyIds = array_unique($linkedPropertyIds);
317 }
318
319 return $linkedPropertyIds;
320 }
321
322 private function loadPropertyIdsWithoutAnyLink(int $iblockId): array
323 {
324 $propertyIds = PropertyTable::getList([
325 'select' => ['ID'],
326 'filter' => [
327 '=IBLOCK_ID' => $iblockId,
328 '==SECTION_LINK.SECTION_ID' => null,
329 ],
330 'runtime' => [
331 new ReferenceField(
332 'SECTION_LINK',
333 '\Bitrix\Iblock\SectionPropertyTable',
334 [
335 '=this.ID' => 'ref.PROPERTY_ID',
336 '=this.IBLOCK_ID' => 'ref.IBLOCK_ID',
337 ],
338 ['join_type' => 'LEFT']
339 ),
340 ],
341 ])
342 ->fetchAll()
343 ;
344
345 return array_column($propertyIds, 'ID');
346 }
347
348 public function getPropertiesSettingsByFilter(array $filter): array
349 {
350 $settings = PropertyTable::getList([
351 'select' => ['*'],
352 'filter' => $filter,
353 'order' => [
354 'SORT' => 'ASC',
355 'ID' => 'ASC',
356 ],
357 ])
358 ->fetchAll()
359 ;
360
361 return $this->loadEnumSettings($settings);
362 }
363
364 protected function prepareField(array $fields, array $settings): array
365 {
366 foreach ($fields as &$field)
367 {
368 if (!empty($settings['USER_TYPE']))
369 {
370 $userType = \CIBlockProperty::GetUserType($settings['USER_TYPE']);
371
372 if (isset($userType['ConvertFromDB']))
373 {
374 $field = call_user_func($userType['ConvertFromDB'], $settings, $field);
375 }
376 }
377 }
378
379 return $fields;
380 }
381
382 protected function prepareSettings(array $settings): array
383 {
384 if (isset($settings['USER_TYPE_SETTINGS_LIST']))
385 {
386 $settings['USER_TYPE_SETTINGS'] = $settings['USER_TYPE_SETTINGS_LIST'];
387 unset($settings['USER_TYPE_SETTINGS_LIST']);
388 }
389
390 return $settings;
391 }
392
393 public function createEntity(array $fields = [], array $settings = []): Property
394 {
395 $entity = $this->factory->createEntity();
396
397 if ($settings)
398 {
399 $settings = $this->prepareSettings($settings);
400 $fields = $this->prepareField($fields, $settings);
401 $entity->setSettings($settings);
402 }
403
404 $propertyValueCollection = $this->propertyValueFactory->createCollection();
405 $propertyValueCollection->initValues($fields);
406
407 $entity->setPropertyValueCollection($propertyValueCollection);
408
409 return $entity;
410 }
411
412 private function loadEnumSettings(array $settings): array
413 {
414 $enumIds = [];
415
416 foreach ($settings as $setting)
417 {
418 if ($setting['PROPERTY_TYPE'] === PropertyTable::TYPE_LIST)
419 {
420 $enumIds[] = $setting['ID'];
421 }
422 }
423
424 $enumSettings = PropertyEnumerationTable::getList([
425 'select' => ['ID', 'PROPERTY_ID'],
426 'filter' => [
427 'PROPERTY_ID' => $enumIds,
428 '=DEF' => 'Y',
429 ],
430 ])
431 ->fetchAll()
432 ;
433 $enumSettings = array_column($enumSettings, 'ID', 'PROPERTY_ID');
434
435 if (!empty($enumSettings))
436 {
437 foreach ($settings as &$setting)
438 {
439 if (isset($enumSettings[$setting['ID']]))
440 {
441 $setting['DEFAULT_VALUE'] = $enumSettings[$setting['ID']];
442 }
443 }
444 }
445
446 return $settings;
447 }
448}
getEntitiesBy($params, array $propertySettings=[])
createEntity(array $fields=[], array $settings=[])
getCollectionByParent(BaseIblockElementEntity $entity)
loadCollection(array $entityFields, BaseIblockElementEntity $parent)
__construct(PropertyFactory $factory, PropertyValueFactory $propertyValueFactory)
save(BaseEntity ... $entities)