Налаштування масового видалення товарів у 1С-Бітрікс
Стара колекція припинена — 1200 SKU потрібно видалити з каталогу. Або після імпорту прайсу виявилися дублікати — 300 зайвих записів. Видалення через стандартний інтерфейс по 20 штук за раз займе годину. При цьому кожне видалення тягне за собою каскад операцій, який легко падає якщо робити це без розуміння архітектури.
Що відбувається при видаленні товара
CIBlockElement::Delete($id) видаляє:
- Запис з
b_iblock_element - Властивості з
b_iblock_element_property - Прив'язки до розділів з
b_iblock_section_element - Ціни з
b_catalog_price - Дані каталогу з
b_catalog_product - Штрихкоди з
b_catalog_product_barcode - Файли через
CFile::Delete()— фізично з диска та зb_file - Кеш елемента
При кожному видаленні спрацьовують події OnBeforeIBlockElementDelete та OnAfterIBlockElementDelete. Якщо на них підписані модулі CRM, пошуку або інші — кожне видалення обробляється цими обробниками.
Масове видалення без перевантаження сервера
Видалення 1000 елементів в одному запросі створює пік навантаження. Правильний підхід — пакетне видалення з паузами:
$toDelete = [1001, 1002, /* ... 1000 id */];
$batchSize = 20;
foreach (array_chunk($toDelete, $batchSize) as $batch) {
foreach ($batch as $id) {
\CIBlockElement::Delete($id);
}
sleep(1); // Пауза між пакетами
}
Для дуже великих обсягів (10000+) операція запускається як агент з збереженням прогресу:
// Агент записує оставшиеся ID в b_option и перезапускает себя
$remaining = unserialize(\Bitrix\Main\Config\Option::get('mymodule', 'delete_queue'));
$batch = array_splice($remaining, 0, 20);
foreach ($batch as $id) {
\CIBlockElement::Delete($id);
}
\Bitrix\Main\Config\Option::set('mymodule', 'delete_queue', serialize($remaining));
Деактивація замість видалення
Фізичне видалення необоротне. Для товарів, які можуть повернутися (сезонні, тимчасово зняті), правильніше деактивація — ACTIVE = 'N'. Видалення виправдане лише для дублікатів або помилково створених записів.
Перед видаленням варто перевірити, чи немає у товара активних замовлень. Якщо товар присутній в b_sale_basket або b_sale_order_basket — видалення порушить історичну цілісність:
SELECT COUNT(*)
FROM b_sale_order_basket sob
WHERE sob.PRODUCT_ID IN (1001, 1002, 1003)
AND sob.ORDER_ID IN (
SELECT ID FROM b_sale_order WHERE STATUS_ID NOT IN ('F', 'C')
);
Якщо запрос повернув ненульове значення — видаляти ці товари не можна, лише деактивувати.
Видалення торгових пропозицій (SKU)
Для товарів з торговими пропозиціями (SEO-тип S) спочатку видаліть усі пропозиції (b_iblock_element з інфоблока пропозицій), потім основний товар. Порядок важливий: при видаленні товара Бітрікс не видаляє пов'язані пропозиції автоматично — вони залишаються висіти сиротами.
// Отримати пропозиції товара
$offers = \CCatalogSKU::getOffersList(
[$productId],
$catalogIblockId,
[],
['ID'],
[]
);
if (!empty($offers[$productId])) {
foreach ($offers[$productId] as $offer) {
\CIBlockElement::Delete($offer['ID']);
}
}
// Видалити основний товар
\CIBlockElement::Delete($productId);
Очищення файлів
Після масового видалення через прямий SQL (якщо хтось обходив CIBlockElement::Delete()) файли в /upload/ залишаються на диску. Для їх очищення потрібно знайти ID файлів у b_file записах, які більше не референсуються з b_iblock_element_property:
SELECT f.ID, f.SUBDIR, f.FILE_NAME
FROM b_file f
LEFT JOIN b_iblock_element_property p ON p.VALUE = CAST(f.ID AS CHAR)
WHERE p.ID IS NULL
AND f.MODULE_ID = 'iblock'
AND f.DATE_CREATE < NOW() - INTERVAL '7 days';
Файли з результату безпечно видаляти через \CFile::Delete($fileId).







