Created
August 3, 2019 15:36
-
-
Save bochsdbg/ba5f7aa1de8d71323fe85d2d68d594dd to your computer and use it in GitHub Desktop.
yml -> opencart import
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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