Настройка массового перемещения товаров между разделами 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.







