OpenCart Product Import/Export Setup
Manual catalog entry in OpenCart is a dead end with any SKU count over several hundred. Setting up import/export pipeline solves three tasks simultaneously: initial catalog population, regular price and inventory updates, synchronization with external systems (1C, MoyaSklad, ERP).
Built-in OpenCart Capabilities
Standard OpenCart contains basic CSV import via admin > Catalog > Import. Format is rigid: fixed columns without support for product variants, attributes, SEO fields, and multilingual content. Suitable only for simplest cases.
Tables affected during complete import:
| Table | Content |
|---|---|
oc_product |
Basic fields: price, weight, model, status |
oc_product_description |
Texts by languages |
oc_product_to_category |
Category binding |
oc_product_to_store |
Store binding |
oc_product_attribute |
Attribute values |
oc_product_option |
Options (color, size) |
oc_product_option_value |
Option values with inventory |
oc_product_image |
Additional images |
oc_product_to_layout |
SEO layout |
Import/Export Modules
Journal3 / QuickAdmin — built into some themes, sufficient for simple catalogs.
Spreadsheet Price Manager (SPM) — works with Google Sheets directly via API, convenient for non-technical teams.
Import/Export Pro (iSenseLabs / Opencart.com) — most complete official option. Supports XML, CSV, XLS, JSON. Column mapping via UI, cron schedule, image processing by URL.
D-Import — alternative emphasizing performance; uses bulk INSERT with disabled indexes during load.
Custom Import via Script
For maximum flexibility — CLI script using OpenCart framework directly:
<?php
// tools/import_products.php
define('DIR_APPLICATION', __DIR__ . '/../admin/');
define('DIR_SYSTEM', __DIR__ . '/../system/');
// ... rest of constants
require_once(DIR_SYSTEM . 'startup.php');
require_once(DIR_APPLICATION . 'config.php');
$registry = new Registry();
// DB, Config, Load initialization...
$csv = new SplFileObject('/tmp/products_feed.csv');
$csv->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY);
$csv->setCsvControl(';', '"');
$headers = $csv->current(); // first line — headers
$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;
// Check existence by 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 + description + categories
// ...
}
}
$db->query("SET foreign_key_checks = 1");
$db->query("SET unique_checks = 1");
Run via cron: 0 6 * * * php /var/www/opencart/tools/import_products.php >> /var/log/oc_import.log 2>&1
Source Formats
CSV from supplier — most common. Issues: encoding (windows-1251 vs UTF-8), delimiter (; vs ,), non-standard booleans (yes/no, 1/0, true/false).
Normalize before import:
# Encoding conversion
iconv -f windows-1251 -t utf-8 feed_supplier.csv > feed_utf8.csv
# Delimiter replacement
sed 's/;/,/g' feed_utf8.csv > feed_normalized.csv
XML (YML — Yandex.Market format) — standard for Russian market:
<offer id="SKU-001" available="true">
<name>Coffee Machine 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="Power">1450 W</param>
<param name="Tank Volume">1.8 L</param>
</offer>
Parse via SimpleXML or XMLReader (for files >50 MB).
REST API supplier — get data via HTTP with pagination:
$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) {
// process product
}
$page++;
} while ($response['meta']['current_page'] < $response['meta']['last_page']);
Export for External Systems
Export to Google Merchant Center (GMC format):
// Generate RSS/Atom feed for 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>';
Image Processing
Image download by URL — bottleneck in mass import. Optimal approach: queue + parallel download via 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;
}
Validation and Logging
Each import should have report: how many created, updated, skipped, with errors. Minimal log structure:
[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
On row errors — record in separate import_errors.csv with line number and problem description.
Timeline
Setup ready module with mapping to specific supplier format: 1–2 days. Custom script with XML/API support, image download, logging: 3–5 days. Full two-way sync with 1C or ERP: 1–3 weeks depending on data mapping complexity.







