Налаштування масового переміщення товарів між розділами у 1С-Бітрікс
Реструктуризація каталогу: розділ «Смартфони» розбивається на «Смартфони Apple», «Смартфони Samsung», «Смартфони Xiaomi». 800 товарів потрібно розподілити за новими розділами. Або поставщик змінив структуру категорій, і 400 позицій потрібно перенести з одного розділу в інший.
Структура розділів та прив'язка товарів
Розділи інфоблока зберігаються в b_iblock_section. Прив'язка елемента до розділу — поле IBLOCK_SECTION_ID у b_iblock_element (основний розділ). Додаткова прив'язка до кількох розділів — таблиця b_iblock_section_element: IBLOCK_ELEMENT_ID, IBLOCK_SECTION_ID, ADDITIONAL_PROPERTY_ID.
При переміщенні товару потрібно оновити обидва місця:
-
IBLOCK_SECTION_IDуb_iblock_element— основний розділ. - Запис у
b_iblock_section_element— для коректної роботи фільтрів.
Переміщення через CIBlockElement::Update
Стандартний метод оновлення зі зміною розділу:
\CIBlockElement::Update($elementId, false, [
'IBLOCK_SECTION_ID' => $newSectionId,
]);
Після Update() Бітрікс автоматично оновлює b_iblock_section_element. Але метод повільний при масовому застосуванні — кожен вызов проходить через події, кеш, права доступу.
Пряме SQL-оновлення для швидкості:
global $DB;
$elementIds = implode(',', array_map('intval', $productIds));
$newSection = (int)$newSectionId;
$DB->Query("
UPDATE b_iblock_element
SET IBLOCK_SECTION_ID = {$newSection}
WHERE ID IN ({$elementIds})
");
$DB->Query("
DELETE FROM b_iblock_section_element
WHERE IBLOCK_ELEMENT_ID IN ({$elementIds})
AND ADDITIONAL_PROPERTY_ID IS NULL
");
foreach ($productIds as $id) {
$DB->Query("
INSERT INTO b_iblock_section_element
(IBLOCK_ELEMENT_ID, IBLOCK_SECTION_ID)
VALUES
({$id}, {$newSection})
");
}
Прямий SQL працює в 50-100 разів швидше за CIBlockElement::Update() для масових операцій, але вимагає ручного скидання кешу.
Багаторозділова прив'язка
Якщо товар повинен відображатися в кількох розділах одночасно (наприклад, «Смартфони» та «Акції»), у b_iblock_section_element додається кілька рядків для одного IBLOCK_ELEMENT_ID:
// Видалити старі прив'язки до розділів
\Bitrix\Iblock\SectionElementTable::deleteByFilter([
'IBLOCK_ELEMENT_ID' => $elementId,
'ADDITIONAL_PROPERTY_ID' => false,
]);
// Додати нові
foreach ($sectionIds as $secId) {
\Bitrix\Iblock\SectionElementTable::add([
'IBLOCK_ELEMENT_ID' => $elementId,
'IBLOCK_SECTION_ID' => $secId,
]);
}
Основний розділ (IBLOCK_SECTION_ID у b_iblock_element) при цьому залишається один — той, який вважається «головним» для хлібних крошок та URL.
Скидання кешу після переміщення
Після масового переміщення кеш компонентів каталогу протухає не відразу. Примусовий скидання:
\Bitrix\Main\Application::getInstance()->getTaggedCache()->clearByTag('iblock_id_' . $iblockId);
// Для конкретних розділів
foreach (array_unique(array_merge($oldSectionIds, [$newSectionId])) as $secId) {
\Bitrix\Main\Application::getInstance()->getTaggedCache()
->clearByTag('iblock_section_' . $secId);
}
Переміщення за умовою
Для автоматичного розподілу товарів за розділами на основі властивостей — наприклад, за брендом:
$brandSectionMap = [
'Apple' => 125,
'Samsung' => 126,
'Xiaomi' => 127,
];
$res = \CIBlockElement::GetList(
[],
['IBLOCK_ID' => $iblockId, 'IBLOCK_SECTION_ID' => $sourceSection],
false,
false,
['ID', 'PROPERTY_BRAND']
);
while ($item = $res->GetNext()) {
$brand = $item['PROPERTY_BRAND_VALUE'];
$targetId = $brandSectionMap[$brand] ?? null;
if ($targetId) {
\CIBlockElement::Update($item['ID'], false, ['IBLOCK_SECTION_ID' => $targetId]);
}
}
При великих обсягах цей скрипт запускається як агент Бітрікса пакетами по 100-200 елементів з збереженням прогресу в b_option.







