Массовая правка товаров - обновление свойств скриптом
Обновляем свойства у тысяч товаров скриптом так, чтобы он дошёл до конца и не переписал заодно то, чего трогать не собирались.
Решение
Обходим каталог порциями:
$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(); // фасетный индекс под фильтрПравка через прямые вызовы не сбрасывает кэш витрины и не пересобирает индекс фильтра. Пропущенный шаг даёт знакомую картину: в админке значения новые, на сайте старые, а фильтр показывает прежние количества.
Правку стоит делать по признаку отбора, а не по списку идентификаторов. Условие отбора переживает повторный запуск и добавленные позиции, а собранный руками список устаревает к следующему обмену с учётной системой.
Пробный прогон на десяти позициях стоит заметно дешевле последующего разбора последствий. Условие отбора проверяют обычной выборкой без записи, и только потом включают правку.
Резервная копия базы перед массовой правкой каталога обязательна. Ошибка в условии отбора переписывает свойства у всего каталога за минуту, а восстановление вручную занимает дни.
Типичные проблемы
Скрипт обрывается на середине каталога.
Запуск идёт из браузера с ограничением времени выполнения запроса. Массовые правки выполняют из консоли самого сервера, где такого жёсткого предела нет.
Правка съела всю память процесса.
Выборка забирается целиком в память вместо обхода порциями. Порция в несколько сотен позиций за раз снимает этот вопрос полностью.
В админке значения новые, на витрине старые.
Кэш инфоблока и страниц каталога не сброшен после этих прямых вызовов. Массовая правка через прямую запись не сбрасывает его сама.
Фильтр показывает прежние количества.
Фасетный индекс не пересобран после массовой правки свойств. Он обновляется при обычном сохранении элемента, а не при прямой записи свойств.
Пропали заполненные вручную поля.
Использовано полное обновление элемента с неполным набором полей. Свойства правят отдельным вызовом, вовсе не трогая прочие поля самого элемента.
Частые вопросы
Как обновить свойство у миллиона товаров?
Порциями и в несколько запусков, с сохранением последнего обработанного идентификатора. Один непрерывный проход на такой размер не рассчитан.
Нужно ли отключать события на время правки?
Прямая запись свойств их и так не поднимает. Полное обновление события вызывает, и на массовой правке это заметно замедляет работу.
Можно ли делать это средствами админки?
Групповые действия в списке подходят для десятков позиций. На тысячах они упираются в то же ограничение времени.
Что делать, если правка пошла не так?
Останавливать скрипт и восстанавливать данные из копии. Обратной операции у массовой правки нет.
Смежное
-
Каталог товаров - оглавление подтемы
-
Долгая операция шагами: порции, состояние, прогресс - как выполнить такую правку порциями
-
Значение свойства не сохраняется: разбор причин - разбор потерянных значений при записи
-
Свойства инфоблока: чтение, запись и фильтрация по значению - работа со свойствами по одному
-
Количество товаров в фильтре: фасетный индекс и пустые значения - что пересобирают после правки
-
Allowed memory size exhausted: причины по убыванию частоты - когда скрипт падает по памяти
-
Выборки из инфоблоков: GetList, ORM и разделы - как устроен сам обход
-
Каталог и продажи - устройство магазина целиком
-
Выгрузка каталога в файл: прайс-лист, отбор, расписание - тот же обход, но с записью в файл
-
Поиск не находит товары: индекс, переиндексация, выдача - что делать с индексом после правки
-
Фасетный индекс: зачем нужен и когда пересобирать - что пересобрать после правки
-
Обработчик меняет данные: рекурсия, флаг, массовые операции - события при массовой правке
-
Создание торговых предложений из кода: привязка, цены, остатки - массовое заведение вариантов товара
-
Аудит каталога: товары без цены, картинок, дубли артикулов - как найти товары, которые надо править
-
Дублирование товара: копия со свойствами, картинками, предложениями - создание копий тем же циклом
-
Разовый скрипт на боевом: запуск, защита, журнал, откат - как безопасно запустить такую правку
-
Товар создан, но не продаётся: разбор причин - если скрипт завёл элементы мимо каталога
-
Массовая смена цен: наценка, округление, откат - то же самое, но для цен
-
Символьный код элемента и раздела: генерация, уникальность - массовое проставление кодов скриптом