Реалізація експорту товарів з сайту у CSV/Excel/XML/JSON
Нещодавно до нас прийшов клієнт: інтернет-магазин із 50 000 товарів. Маркетплейси вимагали XML та CSV у різних форматах, бухгалтерія — Excel для 1С, а аналітикам потрібен був JSON. Щоразу розробники писали новий скрипт, витрачаючи дні. Помилки в мапінгу полів призводили до того, що на Wildberries їхала ціна без ПДВ. Це типова ситуація для бізнесу, що зростає, коли відділи запитують різні формати, а єдиного рішення немає.
Ми запропонували єдину архітектуру — паттерн Builder + Writer. Вона закриває всі сценарії без дублювання коду. Тепер клієнт вибирає шаблон експорту з адмінки, натискає «Завантажити» та отримує файл потрібного формату за секунди.
Чому гнучкий експорт — це складно?
- Різні формати: CSV не підтримує вкладені дані, XML — стандарт для 1С та маркетплейсів, JSON зручний для API, але при розмірі >50 МБ краще використовувати NDJSON.
- Кожен отримувач хоче свій набір полів: для «Яндекс.Маркета» потрібні артикул, назва, ціна, залишок, для Instagram — лише фото та посилання.
- Обсяг даних: каталог може зростати до сотень тисяч позицій. Простий
json_encodeу пам'яті «повалить» сервер.
Коли потрібен асинхронний експорт?
Для великих каталогів експорт запускається у фоні через чергу. Job отримує конфігурацію шаблону, ітерує товари чанками по 1000 записів та передає їх у Writer. Користувач отримує сповіщення про готовність файлу. Це дозволяє не блокувати сайт та вивантажувати навіть мільйони товарів.
Як ми будуємо експорт: паттерн Builder + Writer
Ми розділили логіку формування даних (Builder) та запису у файл (Writer). Builder отримує ланцюжок методів для налаштування полів та фільтрів, потім запускає ітерацію по БД чанками та передає рядки у Writer.
interface ExportWriterInterface { public function open(string $filePath): void; public function writeHeader(array $columns): void; public function writeRow(array $row): void; public function close(): string; // повертає шлях до файлу } Builder зберігає конфіг та вміє вирішувати пов'язані поля (категорія, зображення, атрибути) через match-вираз. Приклад для CSV: додаємо BOM для Excel, використовуємо fputcsv із роздільником ;.
class CsvWriter implements ExportWriterInterface { private $handle; public function open(string $filePath): void { $this->handle = fopen($filePath, 'w'); fwrite($this->handle, "\xEF\xBB\xBF"); } public function writeHeader(array $columns): void { fputcsv($this->handle, $columns, ';', '"'); } public function writeRow(array $row): void { fputcsv($this->handle, $row, ';', '"'); } public function close(): string { fclose($this->handle); return $this->filePath; } } Для Excel використовуємо потоковий OpenSpout — він пише рядки безпосередньо в ZIP-потік, споживаючи ~10 МБ пам'яті при будь-якому розмірі файлу. JSON для великих обсягів — через NDJSON (по об'єкту в рядку). XML — через XMLWriter з елементами <product>.
Переваги потокового експорту
- Мінімальне споживання пам'яті (близько 10 МБ) незалежно від розміру каталогу.
- Можливість експортувати мільйони товарів без тайм-аутів.
- Легке додавання нових форматів — достатньо реалізувати Writer.
Таблиця порівняння форматів
| Формат | Продуктивність | Розмір файлу | Вкладеність | Рекомендований обсяг |
|---|---|---|---|---|
| CSV | висока | маленький | ні | до 100 000 рядків |
| XLSX | середня | середній | ні | до 1 000 000 рядків (з OpenSpout) |
| XML | низька | великий | так | до 50 000 товарів |
| JSON/NDJSON | висока | середній | так | до 500 000 об'єктів (NDJSON) |
Порівняння ручного та автоматичного експорту
| Параметр | Ручний (щоразу новий скрипт) | Автоматичний (Builder + Writer) |
|---|---|---|
| Час на розробку нового формату | 1-2 дні | 0 (вибір шаблону) |
| Помилки мапінгу | часті | виключені (єдиний конфіг) |
| Продуктивність при великому обсязі | повільно, падіння пам'яті | потоково, ~10 МБ |
| Підтримка нових вимог | повне переписування | додавання поля в Builder |
Приклад конфігурації Builder
$builder = new ProductExportBuilder(); $builder->addField('sku', function ($product) { return $product->sku; }) ->addField('name', function ($product) { return $product->name; }) ->addField('price', function ($product) { return round($product->price, 2); }) ->addField('category', function ($product) { return $product->category->name; }) ->filterBy('price', '>', 100) ->chunkSize(1000); Що входить у реалізацію
- Розробка Builder'а з підтримкою кастомних полів, фільтрів та чанкування (1000 записів).
- Набір Writer'ів для CSV (з BOM), XLSX (OpenSpout), XML, JSON (включаючи NDJSON).
- Асинхронний експорт для великих файлів: товари вивантажуються у фоні через чергу, користувач отримує сповіщення.
- Шаблони експорту — зберігаються в БД, можуть включати трансформації (округлення цін, обрізка зображень).
- Готова сторінка завантаження з контролем доступу та MIME-типами.
- Документація з налаштування шаблонів та розгортання.
Скільки часу займає?
- Базова версія (CSV + Excel + Builder з чанкуванням) — 2 дні.
- Додавання XML, JSON, NDJSON та асинхронного Job — +1 день.
- Шаблони, трансформації, завантаження — +1 день.
Підсумкова вартість розраховується індивідуально після аналізу обсягу та складності каталогу. Зв'яжіться з нами — оцінимо проект протягом одного робочого дня.
Наші інженери мають 10+ років досвіду в Laravel та PHP. Ми реалізували експорт для магазинів із каталогами до 500 000 товарів. Гарантуємо, що файли будуть сумісні з 1С, вашими маркетплейсами та BI-системами. Замовте реалізацію під ключ — отримайте гнучкий експорт, який зекономить години ваших розробників.







