Перейти к содержимому

Массовая правка товаров - обновление свойств скриптом

Обновляем свойства у тысяч товаров скриптом так, чтобы он дошёл до конца и не переписал заодно то, чего трогать не собирались.

Решение

Обходим каталог порциями:

$lastId = 0;
while (true) {
$res = CIBlockElement::GetList(['ID' => 'ASC'], ['IBLOCK_ID' => 5, '>ID' => $lastId],
false, ['nTopCount' => 500], ['ID', 'NAME', 'PROPERTY_BRAND']);
$rows = 0;
while ($row = $res->Fetch()) { $lastId = $row['ID']; $rows++; /* правка ниже */ }
if (!$rows) { break; }
}

Обход идёт порциями по возрастанию идентификатора, а не постраничной навигацией. Смещение на большой таблице заставляет базу перечитывать всё с начала, и к десятой странице скрипт заметно замедляется.

Пишем только то, что меняем:

CIBlockElement::SetPropertyValuesEx($row['ID'], 5, ['BRAND' => $brandId]);
// метод с окончанием Ex меняет только переданные свойства, остальные не трогает
// запись свойства не трогает поля элемента и не поднимает события сохранения
$el = new CIBlockElement();
$el->Update($row['ID'], ['SORT' => 100]); // а вот это уже полное обновление

Отдельная запись свойств не трогает поля элемента и не поднимает событий сохранения. Полное обновление тяжелее и перезаписывает всё поданное, поэтому им пользуются только там, где действительно меняются поля.

Держим скрипт в границах ресурсов:

if ($rows % 100 === 0) {
printf("обработано=%d память=%d МБ\n", $processed, memory_get_usage(true) >> 20);
}
// длинную правку запускают из консоли, а не из браузера
// в браузере она упирается во время выполнения задолго до конца

Массовую правку каталога запускают именно из консоли. Запуск из браузера упирается в предел времени выполнения на середине каталога, и понять, докуда скрипт дошёл, потом почти невозможно.

Приводим витрину в порядок после правки:

CIBlock::CleanCache(5); // кэш инфоблока
BXClearCache(true, '/catalog/'); // кэш страниц каталога
$indexer = new \Bitrix\Iblock\PropertyIndex\Indexer(5);
$indexer->startIndex(); // фасетный индекс под фильтр

Правка через прямые вызовы не сбрасывает кэш витрины и не пересобирает индекс фильтра. Пропущенный шаг даёт знакомую картину: в админке значения новые, на сайте старые, а фильтр показывает прежние количества.

Правку стоит делать по признаку отбора, а не по списку идентификаторов. Условие отбора переживает повторный запуск и добавленные позиции, а собранный руками список устаревает к следующему обмену с учётной системой.

Пробный прогон на десяти позициях стоит заметно дешевле последующего разбора последствий. Условие отбора проверяют обычной выборкой без записи, и только потом включают правку.

Резервная копия базы перед массовой правкой каталога обязательна. Ошибка в условии отбора переписывает свойства у всего каталога за минуту, а восстановление вручную занимает дни.

Типичные проблемы

Скрипт обрывается на середине каталога.

Запуск идёт из браузера с ограничением времени выполнения запроса. Массовые правки выполняют из консоли самого сервера, где такого жёсткого предела нет.

Правка съела всю память процесса.

Выборка забирается целиком в память вместо обхода порциями. Порция в несколько сотен позиций за раз снимает этот вопрос полностью.

В админке значения новые, на витрине старые.

Кэш инфоблока и страниц каталога не сброшен после этих прямых вызовов. Массовая правка через прямую запись не сбрасывает его сама.

Фильтр показывает прежние количества.

Фасетный индекс не пересобран после массовой правки свойств. Он обновляется при обычном сохранении элемента, а не при прямой записи свойств.

Пропали заполненные вручную поля.

Использовано полное обновление элемента с неполным набором полей. Свойства правят отдельным вызовом, вовсе не трогая прочие поля самого элемента.

Частые вопросы

Как обновить свойство у миллиона товаров?

Порциями и в несколько запусков, с сохранением последнего обработанного идентификатора. Один непрерывный проход на такой размер не рассчитан.

Нужно ли отключать события на время правки?

Прямая запись свойств их и так не поднимает. Полное обновление события вызывает, и на массовой правке это заметно замедляет работу.

Можно ли делать это средствами админки?

Групповые действия в списке подходят для десятков позиций. На тысячах они упираются в то же ограничение времени.

Что делать, если правка пошла не так?

Останавливать скрипт и восстанавливать данные из копии. Обратной операции у массовой правки нет.

Смежное

Первоисточник