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

Импорт каталога из файла - CSV, повторный запуск, свои поля

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

Решение

Читаем файл построчно и сопоставляем колонки:

$handle = fopen('/home/bitrix/import/catalog.csv', 'r');
$header = fgetcsv($handle, 0, ';');
while (($row = fgetcsv($handle, 0, ';')) !== false) {
$data = array_combine($header, $row);
// ключ связи: артикул или внешний код, но не название товара
$code = trim($data['article']);
}
fclose($handle);

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

Ищем существующий товар перед созданием:

$found = CIBlockElement::GetList([], ['IBLOCK_ID' => 5, 'XML_ID' => $code],
false, ['nTopCount' => 1], ['ID']);
$existing = $found->Fetch();

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

Создаём или обновляем элемент:

$el = new CIBlockElement();
$fields = [
'IBLOCK_ID' => 5,
'NAME' => $data['name'],
'XML_ID' => $code,
'ACTIVE' => 'Y',
'PROPERTY_VALUES' => ['MATERIAL' => $data['material']],
];
$id = $existing ? ($el->Update($existing['ID'], $fields) ? $existing['ID'] : 0)
: $el->Add($fields);
if (!$id) { echo $el->LAST_ERROR, "\n"; }

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

Записываем цену отдельно от элемента:

CPrice::SetBasePrice($id, (float)$data['price'], 'RUB');
CCatalogProduct::Add(['ID' => $id, 'QUANTITY' => (int)$data['quantity']]);
// цена и количество живут в торговом каталоге, а не в инфоблоке

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

Картинки в таблице задают либо путями на сервере, либо адресами. Файл при этом должен лежать на сервере или быть доступным по сети, иначе поле останется пустым - молча, без ошибки в журнале.

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

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

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

После второго запуска каталог удвоился.

Не задан ключ связи либо он не уникален в файле. Импорт в этом случае создаёт новую позицию на каждую строку.

Часть строк не импортировалась молча.

В скрипте импорта не печатается причина отказа, и ошибка теряется молча. Метод создания возвращает ложь, а текст ошибки лежит в свойстве объекта.

Товары есть, а цен и остатков нет.

Для элемента не создана запись в торговом каталоге, а без неё цены не будет. Элемент инфоблока сам по себе товаром не становится и цену не получает.

В названиях знаки вопроса.

Кодировка выгруженного файла расходится с кодировкой самого сайта, и текст ломается. Файл перекодируют до запуска импорта, а не после.

Картинки не загрузились.

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

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

Когда импорт из файла лучше обмена с 1С?

При разовой загрузке и при переносе с другой площадки. Постоянный каталог держат на обмене: он сам следит за изменениями и удалениями.

Как загрузить товары в нужный раздел?

Колонкой с кодом раздела: перед созданием элемента находят раздел по этому коду. Названия разделов ключом не берут - они повторяются.

Что делать с большим файлом?

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

Как обновить только цены?

Тем же импортом с ключом связи, но записывая только цену. Остальные поля при этом не трогают.

Смежное

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