Skip to content

Instantly share code, notes, and snippets.

@bochsdbg
Created August 3, 2019 15:36
Show Gist options
  • Save bochsdbg/ba5f7aa1de8d71323fe85d2d68d594dd to your computer and use it in GitHub Desktop.
Save bochsdbg/ba5f7aa1de8d71323fe85d2d68d594dd to your computer and use it in GitHub Desktop.
yml -> opencart import
<?php
namespace YA;
use \XMLReader;
use \stdClass;
class YmlToOC {
/**
* @var XMLReader
*/
private $reader = null;
private $output = null;
private $table_prefix = 'oc_';
private $ch = null;
private $attributes = [];
private $categories_parents = [];
private $categories_items_count = [];
const CURRENCIES_TAG = 'currencies';
const CURRENCY_TAG = 'currency';
const CATEGORIES_TAG = 'categories';
const CATEGORY_TAG = 'category';
const OFFER_TAG = 'offer';
const CURRENCIES = ['RUB' => 1, 'USD' => 2, 'EUR' => 3, 'UAH' => 4];
function __construct($file_name)
{
$this->reader = new XMLReader;
if (!$this->reader->open($file_name, null, LIBXML_NOBLANKS | LIBXML_NOCDATA)) {
throw new \RuntimeException("File not found: `$file_name`");
}
$this->output = fopen('1.sql', 'w');
$this->ch = curl_init();
curl_setopt_array($this->ch, [
CURLOPT_RETURNTRANSFER => 1,
]);
}
function processCurrency()
{
}
function processCategory()
{
$id = (int)$this->reader->getAttribute('id');
$parent_id = (int)$this->reader->getAttribute('parentId');
$name = $this->reader->readInnerXml();
$this->categories_parents[$id] = $parent_id;
$sql = $this->genUpsert($this->table_prefix . 'category', [
'category_id' => $id,
'image' => '',
'parent_id' => $parent_id,
'top' => 1,
'column' => 1,
'sort_order' => 1,
'status' => 0,
'#date_added' => 'NOW()',
'#date_modified' => 'NOW()',
'noindex' => 1,
], ['category_id', 'date_added', 'status']);
fwrite($this->output, $sql . ";\n");
$sql = $this->genUpsert($this->table_prefix . 'category_to_store', [
'category_id' => $id,
'store_id' => 0, // change
], ['category_id']);
fwrite($this->output, $sql . ";\n");
$sql = $this->genUpsert($this->table_prefix . 'category_description', [
'category_id' => $id,
'language_id' => 1, // change
'name' => $name,
'description' => '',
'meta_title' => '',
'meta_description' => '',
'meta_keyword' => '',
'meta_h1' => '',
], ['category_id', 'language_id']);
fwrite($this->output, $sql . ";\n");
}
function processOffer()
{
$offer = new \stdClass;
$offer->id = self::getNumber($this->reader->getAttribute('id'));
$offer->available = self::getBool($this->reader->getAttribute('available'));
$offer->type = self::getString($this->reader->getAttribute('type'));
$offer->bid = self::getNumber($this->reader->getAttribute('bid'));
$offer->group_id = self::getNumber($this->reader->getAttribute('group_id'));
$offer->pictures = [];
$offer->params = [];
static $CNTR = 0;
echo ++$CNTR, ' ';
while ($this->reader->read()) {
if ($this->reader->nodeType === XMLReader::END_ELEMENT && $this->reader->name === 'offer') break;
if ($this->reader->nodeType !== XMLReader::ELEMENT) continue;
if (in_array($this->reader->name, ['url', 'name', 'description', 'vendor', 'vendorCode', 'country_of_origin', 'sales_notes'])) { // string keys
$key = $this->reader->name;
$offer->$key = $this->reader->readString();
} else if (in_array($this->reader->name, ['price', 'categoryId'])) { // number keys
$key = $this->reader->name;
$offer->$key = self::getNumber($this->reader->readString());
} else if (in_array($this->reader->name, ['pickup', 'delivery', 'adult'])) { // bool keys
$key = $this->reader->name;
$offer->$key = self::getBool($this->reader->readString());
} else if ($this->reader->name === 'currencyId') {
$currencyCode = self::getString($this->reader->readString());
if (isset(self::CURRENCIES[$currencyCode])) {
$offer->currencyId = self::CURRENCIES[$currencyCode];
} else {
echo '[ERROR] offer id: ', $offer->id, ' unknown currency code: ', $currencyCode, "\n";
return;
}
} else if ($this->reader->name === 'picture') {
$img_url = self::getString($this->reader->readString());
$img_local_path = $this->getCachedOrDownloadImage($img_url);
$offer->pictures[] = $img_local_path;
} else if ($this->reader->name === 'param') {
$param = new stdClass;
$param->name = self::getString($this->reader->getAttribute('name'));
$param->unit = self::getString($this->reader->getAttribute('unit'));
$param->value = self::getString($this->reader->readString());
$offer->params[] = $param;
} else {
echo '[WARN] offer id: ', $offer->id, ' unknown tag: ', $this->reader->name, "\n";
}
}
if (!empty($offer->categoryId)) {
if (!isset($this->categories_items_count[$offer->categoryId])) {
$this->categories_items_count[$offer->categoryId] = 0;
}
$this->categories_items_count[$offer->categoryId]++;
}
if (!empty($offer->vendorCode) && !empty($offer->vendor)) {
$offer->vendorCode = ltrim($offer->vendorCode, '0');
$vendorCode = +$offer->vendorCode;
if (''.$vendorCode !== $offer->vendorCode) {
echo "[WARN] vendor code is not integer. offer id: {$offer->id} vendorCode: `{$offer->vendorCode}` vendor: `{$offer->vendor}`\n'";
$offer->vendorCode = 0;
} else {
$offer->vendorCode = $vendorCode;
$this->addUpsert('manufacturer', [
'manufacturer_id' => $offer->vendorCode,
'name' => $offer->vendor,
'image' => '',
'sort_order' => 0,
'noindex' => 1,
], ['manufacturer_id']);
$this->addUpsert('manufacturer_description', [
'manufacturer_id' => $offer->vendorCode,
'language_id' => 1, // change
'description' => '',
'description3' => '',
'meta_description' => '',
'meta_keyword' => '',
'meta_title' => '',
'meta_h1' => '',
], ['manufacturer_id', 'language_id']);
$this->addUpsert('manufacturer_to_store', [
'manufacturer_id' => $offer->vendorCode,
'store_id' => 0, // change
], ['manufacturer_id']);
}
} else {
$offer->vendor = '';
$offer->vendorCode = 0;
}
$this->addUpsert('product', [
'product_id' => $offer->id,
'model' => $offer->id,
'sku' => '',
'upc' => '',
'ean' => '',
'jan' => '',
'isbn' => '',
'mpn' => '',
'location' => '',
'quantity' => 100,
'stock_status_id' => 8, // change
'image' => $offer->pictures[0],
'manufacturer_id' => $offer->vendorCode,
'shipping' => 0,
'price' => $offer->price,
'points' => 0,
'tax_class_id' => 9, // change
'#date_available' => 'CURDATE()',
'weight' => 0,
'weight_class_id' => 1,
'length' => 0,
'width' => 0,
'height' => 0,
'length_class_id' => 1,
'subtract' => 0,
'minimum' => 1,
'sort_order' => 0,
'status' => 1,
'viewed' => 0,
'#date_added' => 'NOW()',
'#date_modified' => 'NOW()',
'noindex' => 1,
], ['product_id', 'date_added', 'date_available']);
$this->addUpsert('product_description', [
'product_id' => $offer->id,
'language_id' => 1,
'name' => $offer->name,
'description' => $offer->description,
'tag' => '',
'meta_title' => '',
'meta_description' => '',
'meta_keyword' => '',
'meta_h1' => '',
], ['product_id', 'language_id']);
$this->addUpsert('product_to_category', [
'product_id' => $offer->id,
'category_id' => $offer->categoryId,
'main_category' => 1,
], ['product_id', 'category_id']);
$this->addUpsert('product_to_store', [
'product_id' => $offer->id,
'store_id' => 0,
], ['product_id']);
foreach ($offer->pictures as $k => $v) {
if ($k === 0) continue;
$this->addUpsert('product_image', [
'product_image_id' => $this->getImageId($offer, $k),
'product_id' => $offer->id,
'image' => $v,
'sort_order' => 0,
], ['product_image_id']);
}
foreach ($offer->params as $k => $v) {
$attr_id = $this->addAttribute($offer, $k, $v);
$value = $v->value;
if (!empty($v->unit)) {
$value .= ' ' . $v->unit;
}
$this->addUpsert('product_attribute', [
'product_id' => $offer->id,
'attribute_id' => $attr_id,
'language_id' => 1, // change
'text' => $value,
], ['product_id', 'attribute_id', 'language_id']);
}
if ($CNTR % 100 === 50) {
$this->addCommit();
$this->addStartTransaction();
}
}
function findNotEmptyCategories()
{
$ids = [];
foreach ($this->categories_items_count as $cid => $cnt) {
if ($cnt > 4) {
do {
$ids[$cid] = 1;
if (!isset($this->categories_parents[$cid])) break;
$cid = $this->categories_parents[$cid];
} while ($cid !== 0);
}
}
return array_keys($ids);
}
function addAttribute($offer, $param_idx, $param)
{
$id = crc32($param->name) % 2000000000;
if (isset($this->attributes[$param->name])) {
return $id;
}
$this->addUpsert('attribute', [
'attribute_id' => $id,
'attribute_group_id' => 7,
'sort_order' => 0,
], ['attribute_id']);
$this->addUpsert('attribute_description', [
'attribute_id' => $id,
'language_id' => 1, // change
'name' => $param->name,
], ['attribute_id', 'language_id']);
$this->attributes[$param->name] = $id;
return $id;
}
function getImageId($order, $image_idx)
{
$file_name = $order->pictures[$image_idx];
$basename = basename($file_name);
$id = intval($basename);
if ($id > 100000) {
return $id;
}
return $order->id * 100 + $image_idx;
}
function getCachedOrDownloadImage($url)
{
$hash = md5($url);
$basename = basename($url);
$dir = "catalog/products/{$hash[0]}{$hash[1]}/{$hash[2]}{$hash[3]}/";
$dir_prefix = __DIR__ . '/public/image/';
$local_name_real = "{$dir_prefix}{$dir}{$basename}";
$local_name_db = "{$dir}{$basename}";
if (is_file($local_name_real) && filesize($local_name_real) > 100) return $local_name_db;
echo '[DEBUG] Downloading ' . $url . ' ... ';
curl_setopt($this->ch, CURLOPT_URL, $url);
$img_data = curl_exec($this->ch);
if (!is_dir("{$dir_prefix}{$dir}")) {
mkdir("{$dir_prefix}{$dir}", 0777, true);
}
$info = @getimagesizefromstring($img_data);
if (empty($info)) {
echo "error\n";
return '';
} else {
echo "ok, ";
echo ' file size: ' . strlen($img_data), "\n";
file_put_contents($local_name_real, $img_data);
return $local_name_db;
}
}
function toSql()
{
fwrite($this->output, "SET AUTOCOMMIT = 0;\n");
while ($this->reader->read()) {
if ($this->reader->nodeType !== XMLReader::ELEMENT) continue;
switch ($this->reader->name) {
case self::CURRENCY_TAG:
do {
$this->processCurrency();
$this->reader->next();
} while ($this->reader->nodeType === XMLReader::ELEMENT && $this->reader->name === self::CURRENCY_TAG);
break;
case self::CATEGORY_TAG:
$this->addStartTransaction();
do {
$this->processCategory();
$this->reader->next();
} while ($this->reader->nodeType === XMLReader::ELEMENT && $this->reader->name === self::CATEGORY_TAG);
$this->addCommit();
break;
case self::OFFER_TAG:
$this->addStartTransaction();
do {
$this->processOffer();
$this->reader->read();
} while ($this->reader->nodeType === XMLReader::ELEMENT && $this->reader->name === self::OFFER_TAG);
$this->addCommit();
break;
}
}
$categ_ids = $this->findNotEmptyCategories();
echo count($categ_ids), "\n";
asort($this->categories_items_count);
print_r($this->categories_items_count);
echo 'UPDATE ' . $this->table_prefix . 'category SET status=1 WHERE category_id IN (' . join(',', $categ_ids) . ");\n";
fwrite($this->output, 'UPDATE ' . $this->table_prefix . 'category SET status=1 WHERE category_id IN (' . join(',', $categ_ids) . ");\n");
fwrite($this->output, "SET AUTOCOMMIT = 1;\n");
}
function __destruct()
{
if ($this->reader->isValid()) {
$this->reader->close();
}
fclose($this->output);
}
function addStartTransaction()
{
fwrite($this->output, "START TRANSACTION;\n");
}
function addCommit()
{
fwrite($this->output, "COMMIT;\n");
}
function addUpsert($table_name, array $fields, array $exclude_from_update = ['id'])
{
$sql = $this->genUpsert($this->table_prefix . $table_name, $fields, $exclude_from_update);
fwrite($this->output, "$sql;\n");
}
function genUpsert($table_name, array $fields, array $exclude_from_update = ['id'])
{
$field_names_escaped = [];
$values_escaped = [];
$update_stmts = [];
foreach ($fields as $k => $v) {
$v_esc = null;
if ($k[0] === '#') {
$k = substr($k, 1);
$v_esc = $v;
} else {
$v_esc = "'" . $this->escapeValue($v) . "'";
}
$k_esc = "`$k`";
$field_names_escaped[] = $k_esc;
$values_escaped[] = $v_esc;
if (!in_array($k, $exclude_from_update)) {
$update_stmts[] = "$k_esc = $v_esc";
}
}
$sql = "INSERT IGNORE INTO `$table_name` (" . join(',', $field_names_escaped) . ') VALUES (' . join(',', $values_escaped) . ')';
// $sql .= ' ON DUPLICATE KEY UPDATE ' . join(',', $update_stmts);
return $sql;
}
private function escapeValue($val)
{
return addslashes($val);
}
static function getNumber($val)
{
return $val === null ? null : +$val;
}
static function getBool($val)
{
return $val === null ? null : +($val[0] === 't');
}
static function getString($val)
{
return $val === null ? null : $val;
}
}
$file_name = '/home/me/work/shopvam/yandex_market.xml';
$yml = new YmlToOC($file_name);
$yml->toSql();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment