dmilyin.ru AI-разработка · Нижний Новгород · обновлено 2026-07-09


Кейсы Клиентский проект

Импорт 58 000 товаров в CS-Cart из CSV: каталог, характеристики, фильтры — программно

Магазин сантехники на CS-Cart, ~58 000 товаров. Штатный импорт не тянул логику характеристик, фильтров и вложенных категорий — собрал свой скрипт на API ядра: повторяемо и без дублей.

Обложка кейса: Импорт 58 000 товаров в CS-Cart из CSV: каталог, характеристики, фильтры — программно

Задача

Интернет-магазин сантехники на CS-Cart, каталог ~58 000 товаров. Данные приходили одной CSV-выгрузкой: название, описание, цена, несколько фото на товар, плоское поле характеристик и трёхуровневая категория в текстовых колонках. Нужно было не просто залить строки, а собрать из них рабочий каталог — с вложенными категориями, характеристиками в карточке и в фильтрах, и так, чтобы повторная выгрузка обновляла товары, а не плодила дубли.

Это типичная ситуация для магазина, который переезжает с парсинга или другой платформы: штатный импортёр CS-Cart хорошо берёт простые поля, но на сложной логике — характеристики, которые должны стать фильтрами по уровням категорий, идемпотентный повторный импорт, тысячи изображений — либо спотыкается, либо требует ручной доводки на каждой итерации. Я решал это скриптом на API ядра, а не кликами в админке.

Решение

Импорт — это не разовая заливка, а повторяемый прогон: источник обновлялся, и скрипт должен был накатываться поверх без дублей. Поэтому я написал PHP-скрипт с бутстрапом init.php — он поднимает окружение CS-Cart и даёт доступ к функциям ядра, вместо записи в таблицы напрямую с риском рассинхрона кеша и связанных сущностей.

Категории. Каждая строка несёт путь из трёх уровней (category_level_1/2/3) в текстовом виде. Скрипт резолвит путь сверху вниз, создаёт недостающие узлы с правильным parent_id и переиспользует существующие. Каталог донора допускал несколько категорий на один товар — структуру привёл к этой же логике, чтобы товар находился во всех релевантных разделах.

Товары — upsert по артикулу. Перед записью товар ищется по product_code (SKU): есть — обновляется, нет — создаётся, через fn_update_product. Это и делает прогон идемпотентным — повторная выгрузка не плодит дубли, а догоняет изменения. Описания нормализую при импорте: переносы строк вместо литеральных \n, иначе текст карточки склеивается в сплошное полотно.

Характеристики и фильтры — главная сложность. Поле характеристик плоское: Название: Значение, часть помечена как фильтруемая. Для каждой характеристики скрипт проверяет, есть ли она уже (fn_get_product_features), при необходимости создаёт (fn_update_product_feature) и связывает значение с товаром (fn_update_product_features_values).

Отдельное решение — на каком уровне категорий вешать фильтры. Фильтруемые характеристики я привязал к категории 2-го уровня: первый уровень («Сантехника») оставил без фильтров — там они бессмысленны, а третий уровень использую только для точного определения подкатегории товара. CS-Cart сам скрывает фильтры без значений в конкретном разделе, поэтому покупатель в каждой подкатегории видит только релевантные фильтры, а не свалку из всех характеристик каталога. Привязка — через product_features_categories.

Изображения. Поле с картинками — список путей через ;. Скрипт разбирает его и привязывает изображения к товару (add_images_to_product), пропуская уже существующие при повторном прогоне. ~180 товаров пришли без фото — я зафиксировал их отдельным списком в отчёте, а не уронил импорт на отсутствующем файле.

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

Конвейер импорта: строка CSV проходит парсер, резолв категорий, upsert по SKU, характеристики, фильтры и изображения — идемпотентно

Стек

CS-Cart 4.18.3 · PHP 8.1 · MySQL 8.0 (MyISAM) · CSV · API ядра CS-Cart (fn_update_product, fn_update_product_feature, fn_update_product_features_values, add_images_to_product, product_features_categories)

Результат

  • ~58 000 товаров в каталоге: название, нормализованные описания, цена, фото, характеристики.
  • Характеристики работают и в карточке, и как фильтры — привязаны к категориям 2-го уровня, в каждой подкатегории показываются только релевантные.
  • Вложенная структура категорий собрана из текстовых путей, товар доступен во всех своих разделах.
  • ~180 товаров без изображений вынесены в отчёт, а не потеряны.
  • Прогон идемпотентен: повторная выгрузка обновляет товары и характеристики без дублей — импорт можно гонять итеративно по мере обновления источника.

Витрина подкатегории с фасетными фильтрами, собранными из характеристик CSV: бренд, размеры, материал, объём

Подкатегория «Отдельностоящие акриловые ванны»: характеристики из CSV стали фасетными фильтрами — Бренд, Высота, Гарантия, Длина, Коллекция, Материал ванны, Назначение, Объём, Размер, — релевантными именно этой подкатегории.

Главное здесь не «залил N строк», а то, что каталог такого размера наполняется и переналивается повторяемым скриптом, а не вручную и не разовой загрузкой, которую страшно повторить.

Ограничение, честно. Скрипт заточен под конкретную схему этой выгрузки — набор колонок и формат поля характеристик. Под другой источник парсер и маппинг нужно перенастраивать: это инструмент проекта, а не универсальный импортёр «на все случаи». Зато внутри своего проекта он повторяемый и предсказуемый, что для каталога на 58k важнее универсальности.