1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
PropertyRepository.php
См. документацию.
1<?php
2
3namespace Bitrix\Catalog\v2\Property;
4
5use Bitrix\Catalog\v2\BaseEntity;
6use Bitrix\Catalog\v2\BaseIblockElementEntity;
7use Bitrix\Catalog\v2\PropertyValue\PropertyValueFactory;
8use Bitrix\Catalog\v2\Section\HasSectionCollection;
9use Bitrix\Iblock\PropertyEnumerationTable;
10use Bitrix\Iblock\PropertyTable;
11use Bitrix\Main\Entity\ReferenceField;
12use Bitrix\Main\Error;
13use Bitrix\Main\Result;
14use Bitrix\Main\Type\Collection;
15
25{
27 private PropertyValueFactory $propertyValueFactory;
28
29 private array $propertyWithoutLink = [];
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 (isset($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 $elementId = $parentEntity->getId();
157 $element = new \CIBlockElement();
158 $res = $element->update(
159 $elementId,
160 [
161 'PROPERTY_VALUES' => $props,
162 ]
163 );
164 if (!$res)
165 {
166 $result->addError(new Error($element->getLastError()));
167 }
168 else
169 {
170 $ipropValues = new \Bitrix\Iblock\InheritedProperty\ElementValues(
171 \CIBlockElement::GetIBlockByID($elementId),
172 $elementId
173 );
174 $ipropValues->clearValues();
175 unset($ipropValues);
176 }
177 }
178
179 return $result;
180 }
181
182 public function delete(BaseEntity ...$entities): Result
183 {
184 return new Result();
185 }
186
187 private function getPropertyIteratorForEntity(BaseIblockElementEntity $entity, array $params = []): \Generator
188 {
189 $disabledPropertyIds = $params['filter']['!PROPERTY_ID'] ?? [];
190 unset($params['filter']['!PROPERTY_ID']);
191
192 if ($entity->isNew())
193 {
194 $entityFields = [];
195 }
196 else
197 {
198 $params['filter']['IBLOCK_ID'] = $entity->getIblockId();
199 $params['filter']['ID'] = $entity->getId();
200 $result = $this->getList($params);
201 $entityFields = $result[$entity->getId()] ?? [];
202 }
203
204 yield from $this->loadCollection($entityFields, $entity, $disabledPropertyIds);
205 }
206
208 {
209 $callback = function (array $params) use ($entity) {
210 yield from $this->getPropertyIteratorForEntity($entity, $params);
211 };
212
213 return
214 $this->factory
215 ->createCollection()
216 ->setIteratorCallback($callback)
217 ;
218 }
219
220 protected function getList(array $params): array
221 {
222 $result = [];
223
224 $filter = $params['filter'] ?? [];
225 $propertyFilter = [];
226
227 if (isset($filter['PROPERTY_ID']))
228 {
229 $propertyFilter['ID'] = $filter['PROPERTY_ID'];
230 unset($filter['PROPERTY_ID']);
231 }
232 if (isset($filter['PROPERTY_CODE']))
233 {
234 $propertyFilter['ID'] = $this->getPropertyIdByCode($filter['IBLOCK_ID'], $filter['PROPERTY_CODE']);
235 if (!$propertyFilter['ID'])
236 {
237 return [];
238 }
239 unset($filter['PROPERTY_CODE']);
240 }
241
242 $propertyValuesIterator = \CIBlockElement::getPropertyValues($filter['IBLOCK_ID'], $filter, true, $propertyFilter);
243 while ($propertyValues = $propertyValuesIterator->fetch())
244 {
245 $descriptions = $propertyValues['DESCRIPTION'] ?? [];
246 $propertyValueIds = $propertyValues['PROPERTY_VALUE_ID'] ?? [];
247 $elementId = $propertyValues['IBLOCK_ELEMENT_ID'];
248 unset($propertyValues['IBLOCK_ELEMENT_ID'], $propertyValues['PROPERTY_VALUE_ID'], $propertyValues['DESCRIPTION']);
249
250 $entityFields = [];
251 // ToDo empty properties with false (?: '') or null?
252 foreach ($propertyValues as $id => $value)
253 {
254 $entityFields[$id] = [];
255 $description = $descriptions[$id] ?? null;
256
257 if ($value !== false || $description !== null)
258 {
259 if (is_array($value))
260 {
261 foreach ($value as $key => $item)
262 {
263 $fields = [
264 'VALUE' => $item ?? '',
265 'DESCRIPTION' => empty($description[$key]) ? null : $description[$key],
266 ];
267
268 if (isset($propertyValueIds[$id][$key]))
269 {
270 $fields['ID'] = $propertyValueIds[$id][$key];
271 }
272
273 $entityFields[$id][$key] = $fields;
274 }
275 }
276 else
277 {
278 $fields = [
279 'VALUE' => $value ?? '',
280 'DESCRIPTION' => empty($descriptions[$id]) ? null : $descriptions[$id],
281 ];
282
283 if (isset($propertyValueIds[$id]))
284 {
285 $fields['ID'] = $propertyValueIds[$id];
286 }
287
288 $entityFields[$id][] = $fields;
289 }
290 }
291 }
292
293 $result[$elementId] = $entityFields;
294 }
295
296 return $result;
297 }
298
299 private function getPropertyIdByCode(int $iblockId, string $code): ?int
300 {
302 'select' => ['ID'],
303 'filter' => [
304 '=CODE' => $code,
305 '=IBLOCK_ID' => $iblockId,
306 ],
307 'cache' => [
308 'ttl' => 86400,
309 ],
310 ]);
311
312 return isset($propertyData['ID']) ? (int)$propertyData['ID'] : null;
313 }
314
316 {
317 return $this->factory->createCollection();
318 }
319
320 protected function loadCollection(array $entityFields, BaseIblockElementEntity $parent, array $disabledPropertyIds): \Generator
321 {
322 $propertySettings = [];
323
324 if ($parent instanceof HasSectionCollection)
325 {
326 $linkedPropertyIds = $this->getLinkedPropertyIds($parent->getIblockId(), $parent);
327 if (!empty($linkedPropertyIds))
328 {
329 $linkedPropertyIds = array_diff($linkedPropertyIds, $disabledPropertyIds);
330 if (!empty($linkedPropertyIds))
331 {
332 $propertySettings = $this->getPropertiesSettingsByFilter([
333 '@ID' => $linkedPropertyIds,
334 ]);
335 }
336 }
337 }
338 else
339 {
340 // variation properties don't use any section links right now
341 $propertySettings = $this->getPropertiesSettingsByFilter([
342 '=IBLOCK_ID' => $parent->getIblockId(),
343 '!ID' => $disabledPropertyIds,
344 ]);
345 }
346
347 foreach ($propertySettings as $settings)
348 {
349 $fields = $entityFields[$settings['ID']] ?? [];
350 $settings['IBLOCK_ELEMENT_ID'] = $parent->getId();
351
352 yield $this->createEntity($fields, $settings);
353 }
354 }
355
356 protected function getLinkedPropertyIds(int $iblockId, HasSectionCollection $parent): array
357 {
358 $linkedPropertyIds = [$this->loadPropertyIdsWithoutAnyLink($iblockId)];
359
360 if ($parent->getSectionCollection()->isEmpty())
361 {
362 $linkedPropertyIds[] = array_keys(\CIBlockSectionPropertyLink::getArray($iblockId));
363 }
364
366 foreach ($parent->getSectionCollection() as $section)
367 {
368 $linkedPropertyIds[] = array_keys(\CIBlockSectionPropertyLink::getArray($iblockId, $section->getValue()));
369 }
370
371 if (!empty($linkedPropertyIds))
372 {
373 $linkedPropertyIds = array_merge(...$linkedPropertyIds);
374 Collection::normalizeArrayValuesByInt($linkedPropertyIds, false);
375 $linkedPropertyIds = array_unique($linkedPropertyIds);
376 }
377
378 return $linkedPropertyIds;
379 }
380
381 private function loadPropertyIdsWithoutAnyLink(int $iblockId): array
382 {
383 if (!isset($this->propertyWithoutLink[$iblockId]))
384 {
385 $this->propertyWithoutLink[$iblockId] = [];
386 $iterator = PropertyTable::getList([
387 'select' => ['ID'],
388 'filter' => [
389 '=IBLOCK_ID' => $iblockId,
390 '==SECTION_LINK.SECTION_ID' => null,
391 ],
392 'runtime' => [
393 new ReferenceField(
394 'SECTION_LINK',
395 '\Bitrix\Iblock\SectionPropertyTable',
396 [
397 '=this.ID' => 'ref.PROPERTY_ID',
398 '=this.IBLOCK_ID' => 'ref.IBLOCK_ID',
399 ],
400 ['join_type' => 'LEFT']
401 ),
402 ],
403 ]);
404 while ($row = $iterator->fetch())
405 {
406 $this->propertyWithoutLink[$iblockId][] = (int)$row['ID'];
407 }
408 unset($row, $iterator);
409 }
410
411 return $this->propertyWithoutLink[$iblockId];
412 }
413
415 {
416 $settings = PropertyTable::getList([
417 'select' => ['*'],
418 'filter' => $filter,
419 'order' => [
420 'SORT' => 'ASC',
421 'ID' => 'ASC',
422 ],
423 ])
424 ->fetchAll()
425 ;
426
427 return $this->loadEnumSettings($settings);
428 }
429
431 {
432 foreach ($fields as &$field)
433 {
434 if (!empty($settings['USER_TYPE']))
435 {
436 $userType = \CIBlockProperty::GetUserType($settings['USER_TYPE']);
437
438 if (isset($userType['ConvertFromDB']))
439 {
440 $field = call_user_func($userType['ConvertFromDB'], $settings, $field);
441 }
442 }
443 }
444
445 return $fields;
446 }
447
449 {
450 if (isset($settings['USER_TYPE_SETTINGS_LIST']))
451 {
452 $settings['USER_TYPE_SETTINGS'] = $settings['USER_TYPE_SETTINGS_LIST'];
453 unset($settings['USER_TYPE_SETTINGS_LIST']);
454 }
455
456 return $settings;
457 }
458
459 public function createEntity(array $fields = [], array $settings = []): Property
460 {
461 $entity = $this->factory->createEntity();
462
463 if ($settings)
464 {
467 $entity->setSettings($settings);
468 }
469
470 $propertyValueCollection = $this->propertyValueFactory->createCollection();
471 $propertyValueCollection->initValues($fields);
472
473 $entity->setPropertyValueCollection($propertyValueCollection);
474
475 return $entity;
476 }
477
478 private function loadEnumSettings(array $settings): array
479 {
480 if (empty($settings))
481 {
482 return $settings;
483 }
484
485 $enumIds = [];
486 foreach ($settings as $setting)
487 {
488 if ($setting['PROPERTY_TYPE'] === PropertyTable::TYPE_LIST)
489 {
490 $enumIds[] = $setting['ID'];
491 }
492 }
493 if (empty($enumIds))
494 {
495 return $settings;
496 }
497
498 $enumSettings = PropertyEnumerationTable::getList([
499 'select' => [
500 'ID',
501 'PROPERTY_ID',
502 ],
503 'filter' => [
504 '@PROPERTY_ID' => $enumIds,
505 '=DEF' => 'Y',
506 ],
507 ])
508 ->fetchAll()
509 ;
510 $enumSettings = array_column($enumSettings, 'ID', 'PROPERTY_ID');
511
512 if (!empty($enumSettings))
513 {
514 foreach ($settings as &$setting)
515 {
516 if (isset($enumSettings[$setting['ID']]))
517 {
518 $setting['DEFAULT_VALUE'] = $enumSettings[$setting['ID']];
519 }
520 }
521 }
522
523 return $settings;
524 }
525}
setIteratorCallback(\Closure $iteratorCallback)
Определения BaseCollection.php:301
getEntitiesBy($params, array $propertySettings=[])
Определения PropertyRepository.php:53
createEntity(array $fields=[], array $settings=[])
Определения PropertyRepository.php:459
prepareField(array $fields, array $settings)
Определения PropertyRepository.php:430
getCollectionByParent(BaseIblockElementEntity $entity)
Определения PropertyRepository.php:207
__construct(PropertyFactory $factory, PropertyValueFactory $propertyValueFactory)
Определения PropertyRepository.php:31
loadCollection(array $entityFields, BaseIblockElementEntity $parent, array $disabledPropertyIds)
Определения PropertyRepository.php:320
const TYPE_FILE
Определения propertytable.php:67
const TYPE_LIST
Определения propertytable.php:70
Определения error.php:15
static getRow(array $parameters)
Определения datamanager.php:398
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$res
Определения filter_act.php:7
$result
Определения get_property_values.php:14
$entity
if(Loader::includeModule( 'bitrix24')) elseif(Loader::includeModule('intranet') &&CIntranetUtils::getPortalZone() !=='ru') $description
Определения .description.php:24
$iblockId
Определения iblock_catalog_edit.php:30
$filter
Определения iblock_catalog_list.php:54
save(BaseEntity ... $entities)
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
trait Error
Определения error.php:11
$settings
Определения product_settings.php:43
if(empty($signedUserToken)) $key
Определения quickway.php:257
$props
Определения template.php:269
if($inWords) echo htmlspecialcharsbx(Number2Word_Rus(roundEx($totalVatSum $params['CURRENCY']
Определения template.php:799
$iterator
Определения yandex_run.php:610
$fields
Определения yandex_run.php:501