Настройка импорта/экспорта товаров OpenCart
Ручное заведение каталога в OpenCart — это тупик при любом количестве SKU свыше нескольких сотен. Настройка пайплайна импорта/экспорта решает три задачи одновременно: первичное наполнение каталога, регулярное обновление цен и остатков, синхронизация с внешними системами (1C, МойСклад, ERP).
Встроенные возможности OpenCart
Стандартный OpenCart содержит базовый CSV-импорт через admin > Catalog > Import. Формат жёсткий: фиксированные колонки без поддержки вариантов товара, атрибутов, SEO-полей и мультиязычности. Подходит только для простейших случаев.
Таблицы, затрагиваемые при полном импорте:
| Таблица | Содержимое |
|---|---|
oc_product |
Основные поля: цена, вес, модель, статус |
oc_product_description |
Тексты по языкам |
oc_product_to_category |
Привязка к категориям |
oc_product_to_store |
Привязка к магазинам |
oc_product_attribute |
Значения атрибутов |
oc_product_option |
Опции (цвет, размер) |
oc_product_option_value |
Значения опций с остатком |
oc_product_image |
Дополнительные изображения |
oc_product_to_layout |
SEO layout |
Модули для импорта/экспорта
Журнал3 / QuickAdmin — встроены в некоторые темы, достаточно для простых каталогов.
Spreadsheet Price Manager (SPM) — работает с Google Sheets напрямую через API, удобен для команд без технических навыков.
Import/Export Pro (iSenseLabs / Opencart.com) — наиболее полный официальный вариант. Поддерживает XML, CSV, XLS, JSON. Маппинг колонок через UI, расписание по cron, обработка изображений по URL.
D-Import — альтернатива с акцентом на производительность; использует bulk INSERT с отключением индексов во время загрузки.
Кастомный импорт через скрипт
Для максимальной гибкости — CLI-скрипт, использующий OpenCart framework напрямую:
<?php
// tools/import_products.php
define('DIR_APPLICATION', __DIR__ . '/../admin/');
define('DIR_SYSTEM', __DIR__ . '/../system/');
// ... остальные константы
require_once(DIR_SYSTEM . 'startup.php');
require_once(DIR_APPLICATION . 'config.php');
$registry = new Registry();
// инициализация DB, Config, Load...
$csv = new SplFileObject('/tmp/products_feed.csv');
$csv->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY);
$csv->setCsvControl(';', '"');
$headers = $csv->current(); // первая строка — заголовки
$csv->next();
$db->query("SET foreign_key_checks = 0");
$db->query("SET unique_checks = 0");
while (!$csv->eof()) {
$row = array_combine($headers, $csv->current());
$csv->next();
if (empty($row['model'])) continue;
// Проверяем существование по model
$existing = $db->query(
"SELECT product_id FROM oc_product WHERE model = '" . $db->escape($row['model']) . "' LIMIT 1"
);
if ($existing->num_rows) {
$product_id = $existing->row['product_id'];
// UPDATE
$db->query("UPDATE oc_product SET
price = '" . (float)$row['price'] . "',
quantity = '" . (int)$row['quantity'] . "',
date_modified = NOW()
WHERE product_id = $product_id"
);
} else {
// INSERT + описание + категории
// ...
}
}
$db->query("SET foreign_key_checks = 1");
$db->query("SET unique_checks = 1");
Такой скрипт запускается по cron: 0 6 * * * php /var/www/opencart/tools/import_products.php >> /var/log/oc_import.log 2>&1
Форматы источников
CSV от поставщика — самый распространённый. Проблемы: кодировка (windows-1251 vs UTF-8), разделитель (; vs ,), нестандартные булевы значения (да/нет, 1/0, true/false).
Нормализация перед импортом:
# Конвертация кодировки
iconv -f windows-1251 -t utf-8 feed_supplier.csv > feed_utf8.csv
# Замена разделителя
sed 's/;/,/g' feed_utf8.csv > feed_normalized.csv
XML (YML — Яндекс.Маркет формат) — стандарт для российского рынка:
<offer id="SKU-001" available="true">
<name>Кофемашина DeLonghi ECAM 22.110</name>
<price>45990</price>
<currencyId>RUB</currencyId>
<categoryId>15</categoryId>
<picture>https://cdn.supplier.ru/img/ecam22110.jpg</picture>
<param name="Мощность">1450 Вт</param>
<param name="Объём бака">1.8 л</param>
</offer>
Парсинг через SimpleXML или XMLReader (для больших файлов > 50 МБ).
REST API поставщика — получение данных по HTTP с пагинацией:
$page = 1;
do {
$response = json_decode(file_get_contents(
"https://api.supplier.ru/v2/products?page={$page}&per_page=200",
false,
stream_context_create(['http' => ['header' => "Authorization: Bearer {$token}\r\n"]])
), true);
foreach ($response['data'] as $item) {
// обработка товара
}
$page++;
} while ($response['meta']['current_page'] < $response['meta']['last_page']);
Экспорт для внешних систем
Выгрузка в Google Merchant Center (формат GMC):
// Генерация RSS/Atom фида для GMC
header('Content-Type: application/xml; charset=utf-8');
echo '<?xml version="1.0" encoding="UTF-8"?>';
echo '<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">';
echo '<channel>';
$products = $db->query("
SELECT p.*, pd.name, pd.description
FROM oc_product p
JOIN oc_product_description pd ON pd.product_id = p.product_id
WHERE p.status = 1 AND pd.language_id = 1
LIMIT 5000
");
foreach ($products->rows as $p) {
echo '<item>';
echo '<g:id>' . $p['model'] . '</g:id>';
echo '<g:title><![CDATA[' . $p['name'] . ']]></g:title>';
echo '<g:price>' . number_format($p['price'], 2, '.', '') . ' RUB</g:price>';
echo '<g:availability>' . ($p['quantity'] > 0 ? 'in_stock' : 'out_of_stock') . '</g:availability>';
echo '</item>';
}
echo '</channel></rss>';
Обработка изображений
Загрузка изображений по URL — узкое место при массовом импорте. Оптимальный подход: очередь + параллельная загрузка через curl_multi:
function downloadImagesParallel(array $urls, string $targetDir, int $concurrency = 10): array {
$mh = curl_multi_init();
$handles = [];
$results = [];
foreach (array_slice($urls, 0, $concurrency) as $url) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_FOLLOWLOCATION => true,
]);
curl_multi_add_handle($mh, $ch);
$handles[] = ['ch' => $ch, 'url' => $url];
}
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh);
} while ($running > 0);
foreach ($handles as $item) {
$content = curl_multi_getcontent($item['ch']);
$filename = $targetDir . '/' . basename(parse_url($item['url'], PHP_URL_PATH));
file_put_contents($filename, $content);
$results[$item['url']] = $filename;
curl_multi_remove_handle($mh, $item['ch']);
}
curl_multi_close($mh);
return $results;
}
Валидация и логирование
Каждый импорт должен иметь отчёт: сколько создано, обновлено, пропущено, с ошибками. Минимальная структура лога:
[2025-01-15 06:00:12] Import started: feed_2025-01-15.csv (4821 rows)
[2025-01-15 06:02:44] Created: 12, Updated: 4791, Skipped: 18, Errors: 0
[2025-01-15 06:02:44] Import finished in 152s
При ошибках в строке — запись в отдельный файл import_errors.csv с номером строки и описанием проблемы.
Сроки
Настройка готового модуля с маппингом под конкретный формат поставщика: 1–2 дня. Кастомный скрипт с поддержкой XML/API, загрузкой изображений и логированием: 3–5 дней. Полноценная двусторонняя синхронизация с 1C или ERP: 1–3 недели в зависимости от сложности маппинга данных.







