кому интересно нашел решение, в файле /public_html/catalog/controller/extension/feed/multiyml.php
после закрывающей скобки } и перед строкой $this->setOffer($data); нужно дописать код
$attribute_groups = $this->model_catalog_product->getProductAttributes($product['product_id']);
if ($attribute_groups) {
$data['param'] = array();
foreach ($attribute_groups as $attribute_group) {
foreach ($attribute_group['attribute'] as $attribute) {
$data['param'][] = array(
'name' => $attribute['name'],
'value' => $attribute['text']
);
}
}
}
вот этот код, между скобкой закрывающей и $this->setOffer($data)
Решение проблемы с тем что яндекс ругается на parent_id , строка
$this->setCategory($category['name'], $category['category_id'], $category['parent_id']);
удаляем парент ид чтобы получилось вот так
$this->setCategory($category['name'], $category['category_id']);
Кому лень возиться, вот полный код, можете полностью заменить.
<?php
class ControllerExtensionFeedMultiyml extends Controller {
private $shop = array();
private $currencies = array();
private $categories = array();
private $offers = array();
private $from_charset = 'utf-8';
private $eol = "\n";
public function index() {
if (isset($this->request->get['feed']) && $this->request->get['feed']) {
$feed_num = $this->request->get['feed'];
} else {
$feed_num = 0;
}
$multiyml_feeds = $this->config->get('feed_multiyml_feeds');
if (isset($multiyml_feeds[$feed_num]) && $multiyml_feeds[$feed_num]['multiyml_status']) {
$multiyml_feed = $multiyml_feeds[$feed_num];
if (!($allowed_categories = implode(",",$multiyml_feed['multiyml_categories']))) exit();
$this->load->model('export/multiyml');
$this->load->model('localisation/currency');
$this->load->model('tool/image');
$this->setShop('name', $multiyml_feed['multiyml_shopname']);
$this->setShop('company', $multiyml_feed['multiyml_company']);
if ($this->request->server['HTTPS']) {
$this->setShop('url', HTTPS_SERVER);
} else {
$this->setShop('url', HTTP_SERVER);
}
$this->setShop('phone', $this->config->get('config_telephone'));
$this->setShop('platform', 'Opencart');
$this->setShop('version', VERSION);
$offers_currency = $multiyml_feed['multiyml_currency'];
if (!$this->currency->has($offers_currency)) exit();
$decimal_place = $this->currency->getDecimalPlace($offers_currency);
$shop_currency = $this->config->get('config_currency');
$this->setCurrency($offers_currency, 1);
$currencies = $this->model_localisation_currency->getCurrencies();
$supported_currencies = array('RUR', 'RUB', 'USD', 'BYR', 'KZT', 'EUR', 'UAH');
$currencies = array_intersect_key($currencies, array_flip($supported_currencies));
foreach ($currencies as $currency) {
if ($currency['code'] != $offers_currency && $currency['status'] == 1) {
$this->setCurrency($currency['code'], number_format(1/$this->currency->convert($currency['value'], $offers_currency, $shop_currency), 4, '.', ''));
}
}
// Категории
$categories = $this->model_export_multiyml->getCategory();
foreach ($categories as $category) {
$this->setCategory($category['name'], $category['category_id']);
}
// Товары
$in_stock_id = $multiyml_feed['multiyml_in_stock'];
$out_of_stock_id = $multiyml_feed['multiyml_out_of_stock'];
$vendor_required = false;
$products = $this->model_export_multiyml->getProduct($allowed_categories, $out_of_stock_id, $vendor_required);
foreach ($products as $product) {
$data = array();
// Атрибуты товара
$data['id'] = $product['product_id'];
$data['available'] = ($product['quantity'] > 0 || $product['stock_status_id'] == $in_stock_id);
// Параметры товара
$data['url'] = $this->url->link('product/product', 'path=' . $this->getPath($product['category_id']) . '&product_id=' . $product['product_id']);
$data['price'] = number_format($this->currency->convert($this->tax->calculate($product['price'], $product['tax_class_id']), $shop_currency, $offers_currency), $decimal_place, '.', '');
$data['currencyId'] = $offers_currency;
$data['categoryId'] = $product['category_id'];
$data['delivery'] = 'true';
$data['name'] = $product['name'];
$data['vendor'] = $product['manufacturer'];
$data['vendorCode'] = $product['model'];
$data['model'] = $product['name'];
$data['model'] = $product['model'];
$data['description'] = $product['description'];
if ($product['image']) {
$data['picture'] = $this->model_tool_image->resize($product['image'], 100, 100);
}
$attribute_groups = $this->model_catalog_product->getProductAttributes($product['product_id']);
if ($attribute_groups) {
$data['param'] = array();
foreach ($attribute_groups as $attribute_group) {
foreach ($attribute_group['attribute'] as $attribute) {
$data['param'][] = array(
'name' => $attribute['name'],
'value' => $attribute['text']
);
}
}
}
$this->setOffer($data);
}
$this->categories = array_filter($this->categories, array($this, "filterCategory"));
Header('Content-Type: application/xml');
if (isset($this->request->get['export'])) {
header('Content-Disposition: attachment; filename="yandex.xml"');
}
echo($this->getYml());
}
}
private function setShop($name, $value) {
$allowed = array('name', 'company', 'url', 'phone', 'platform', 'version', 'agency', 'email');
if (in_array($name, $allowed)) {
$this->shop[$name] = $this->prepareField($value);
}
}
private function setCurrency($id, $rate = 'CBRF', $plus = 0) {
$allow_id = array('RUR', 'RUB', 'USD', 'BYR', 'KZT', 'EUR', 'UAH');
if (!in_array($id, $allow_id)) {
return false;
}
$allow_rate = array('CBRF', 'NBU', 'NBK', 'CB');
if (in_array($rate, $allow_rate)) {
$plus = str_replace(',', '.', $plus);
if (is_numeric($plus) && $plus > 0) {
$this->currencies[] = array(
'id'=>$this->prepareField(strtoupper($id)),
'rate'=>$rate,
'plus'=>(float)$plus
);
} else {
$this->currencies[] = array(
'id'=>$this->prepareField(strtoupper($id)),
'rate'=>$rate
);
}
} else {
$rate = str_replace(',', '.', $rate);
if (!(is_numeric($rate) && $rate > 0)) {
return false;
}
$this->currencies[] = array(
'id'=>$this->prepareField(strtoupper($id)),
'rate'=>(float)$rate
);
}
return true;
}
private function setCategory($name, $id, $parent_id = 0) {
$id = (int)$id;
if ($id < 1 || trim($name) == '') {
return false;
}
if ((int)$parent_id > 0) {
$this->categories[$id] = array(
'id'=>$id,
'parentId'=>(int)$parent_id,
'name'=>$this->prepareField($name)
);
} else {
$this->categories[$id] = array(
'id'=>$id,
'name'=>$this->prepareField($name)
);
}
return true;
}
private function setOffer($data) {
$offer = array();
$attributes = array('id', 'type', 'available', 'bid', 'cbid', 'param');
$attributes = array_intersect_key($data, array_flip($attributes));
foreach ($attributes as $key => $value) {
switch ($key)
{
case 'id':
case 'bid':
case 'cbid':
$value = (int)$value;
if ($value > 0) {
$offer[$key] = $value;
}
break;
case 'type':
if (in_array($value, array('vendor.model', 'book', 'audiobook', 'artist.title', 'tour', 'ticket', 'event-ticket'))) {
$offer['type'] = $value;
}
break;
case 'available':
$offer['available'] = ($value ? 'true' : 'false');
break;
case 'param':
if (is_array($value)) {
$offer['param'] = $value;
}
break;
default:
break;
}
}
$type = isset($offer['type']) ? $offer['type'] : '';
$allowed_tags = array('url'=>0, 'buyurl'=>0, 'price'=>1, 'wprice'=>0, 'currencyId'=>1, 'xCategory'=>0, 'categoryId'=>1, 'picture'=>0, 'store'=>0, 'pickup'=>0, 'delivery'=>0, 'deliveryIncluded'=>0, 'local_delivery_cost'=>0, 'orderingTime'=>0);
switch ($type) {
case 'vendor.model':
$allowed_tags = array_merge($allowed_tags, array('typePrefix'=>0, 'vendor'=>1, 'vendorCode'=>0, 'model'=>1, 'provider'=>0, 'tarifplan'=>0));
break;
case 'book':
$allowed_tags = array_merge($allowed_tags, array('author'=>0, 'name'=>1, 'publisher'=>0, 'series'=>0, 'year'=>0, 'ISBN'=>0, 'volume'=>0, 'part'=>0, 'language'=>0, 'binding'=>0, 'page_extent'=>0, 'table_of_contents'=>0));
break;
case 'audiobook':
$allowed_tags = array_merge($allowed_tags, array('author'=>0, 'name'=>1, 'publisher'=>0, 'series'=>0, 'year'=>0, 'ISBN'=>0, 'volume'=>0, 'part'=>0, 'language'=>0, 'table_of_contents'=>0, 'performed_by'=>0, 'performance_type'=>0, 'storage'=>0, 'format'=>0, 'recording_length'=>0));
break;
case 'artist.title':
$allowed_tags = array_merge($allowed_tags, array('artist'=>0, 'title'=>1, 'year'=>0, 'media'=>0, 'starring'=>0, 'director'=>0, 'originalName'=>0, 'country'=>0));
break;
case 'tour':
$allowed_tags = array_merge($allowed_tags, array('worldRegion'=>0, 'country'=>0, 'region'=>0, 'days'=>1, 'dataTour'=>0, 'name'=>1, 'hotel_stars'=>0, 'room'=>0, 'meal'=>0, 'included'=>1, 'transport'=>1, 'price_min'=>0, 'price_max'=>0, 'options'=>0));
break;
case 'event-ticket':
$allowed_tags = array_merge($allowed_tags, array('name'=>1, 'place'=>1, 'hall'=>0, 'hall_part'=>0, 'date'=>1, 'is_premiere'=>0, 'is_kids'=>0));
break;
default:
$allowed_tags = array_merge($allowed_tags, array('name'=>1, 'vendor'=>0, 'model'=>1, 'sales_notes'=>1));
break;
}
$allowed_tags = array_merge($allowed_tags, array('aliases'=>0, 'additional'=>0, 'description'=>0, 'sales_notes'=>0, 'promo'=>0, 'manufacturer_warranty'=>0, 'country_of_origin'=>0, 'downloadable'=>0, 'adult'=>0, 'barcode'=>0));
$required_tags = array_filter($allowed_tags);
if (sizeof(array_intersect_key($data, $required_tags)) != sizeof($required_tags)) {
return;
}
$data = array_intersect_key($data, $allowed_tags);
$allowed_tags = array_intersect_key($allowed_tags, $data);
$offer['data'] = array();
foreach ($allowed_tags as $key => $value) {
$offer['data'][$key] = $this->prepareField($data[$key]);
}
$this->offers[] = $offer;
}
private function getYml() {
$yml = '<?xml version="1.0" encoding="windows-1251"?>' . $this->eol;
$yml .= '<!DOCTYPE yml_catalog SYSTEM "shops.dtd">' . $this->eol;
$yml .= '<yml_catalog date="' . date('Y-m-d H:i') . '">' . $this->eol;
$yml .= '<shop>' . $this->eol;
// информация о магазине
$yml .= $this->array2Tag($this->shop);
// валюты
$yml .= '<currencies>' . $this->eol;
foreach ($this->currencies as $currency) {
$yml .= $this->getElement($currency, 'currency');
}
$yml .= '</currencies>' . $this->eol;
// категории
$yml .= '<categories>' . $this->eol;
foreach ($this->categories as $category) {
$category_name = $category['name'];
unset($category['name'], $category['export']);
$yml .= $this->getElement($category, 'category', $category_name);
}
$yml .= '</categories>' . $this->eol;
// товары
$yml .= '<offers>' . $this->eol;
foreach ($this->offers as $offer) {
$tags = $this->array2Tag($offer['data']);
unset($offer['data']);
if (isset($offer['param'])) {
$tags .= $this->array2Param($offer['param']);
unset($offer['param']);
}
$yml .= $this->getElement($offer, 'offer', $tags);
}
$yml .= '</offers>' . $this->eol;
$yml .= '</shop>';
$yml .= '</yml_catalog>';
return $yml;
}
private function getElement($attributes, $element_name, $element_value = '') {
$retval = '<' . $element_name . ' ';
foreach ($attributes as $key => $value) {
$retval .= $key . '="' . $value . '" ';
}
$retval .= $element_value ? '>' . $this->eol . $element_value . '</' . $element_name . '>' : '/>';
$retval .= $this->eol;
return $retval;
}
private function array2Tag($tags) {
$retval = '';
foreach ($tags as $key => $value) {
$retval .= '<' . $key . '>' . $value . '</' . $key . '>' . $this->eol;
}
return $retval;
}
private function array2Param($params) {
$retval = '';
foreach ($params as $param) {
$retval .= '<param name="' . $this->prepareField($param['name']);
if (isset($param['unit'])) {
$retval .= '" unit="' . $this->prepareField($param['unit']);
}
$retval .= '">' . $this->prepareField($param['value']) . '</param>' . $this->eol;
}
return $retval;
}
private function prepareField($field) {
$field = htmlspecialchars_decode($field);
$field = strip_tags($field);
$from = array('"', '&', '>', '<', '\'');
$to = array('"', '&', '>', '<', ''');
$field = str_replace($from, $to, $field);
if ($this->from_charset != 'windows-1251') {
$field = iconv($this->from_charset, 'windows-1251//TRANSLIT//IGNORE', $field);
}
$field = preg_replace('#[\x00-\x08\x0B-\x0C\x0E-\x1F]+#is', ' ', $field);
return trim($field);
}
protected function getPath($category_id, $current_path = '') {
if (isset($this->categories[$category_id])) {
$this->categories[$category_id]['export'] = 1;
if (!$current_path) {
$new_path = $this->categories[$category_id]['id'];
} else {
$new_path = $this->categories[$category_id]['id'] . '_' . $current_path;
}
if (isset($this->categories[$category_id]['parentId'])) {
return $this->getPath($this->categories[$category_id]['parentId'], $new_path);
} else {
return $new_path;
}
}
}
function filterCategory($category) {
return isset($category['export']);
}
}
?>