Выгрузка товаров в Яндекс Маркет

ym.xml
<?php
header('Content-Type: text/xml');
$locationPath = '/';
$offsetPath = '';
 
require('lib/mysql.php'); // класс работы с mysql
require('lib/yml.php'); // класс работы с Yandex Market
 
// замена спецсимволов, удаление, etc
function prepareTextField($text) {
  $text = html_entity_decode($text, null, 'UTF-8');
  $text = strip_tags($text);
  $text = preg_replace('/\s+/', ' ', trim($text));
  $text = htmlspecialchars($text, null, 'UTF-8');
  return $text;
}
 
//config
$site = 'Sitename';
$siteUri = 'http://site.ru/';
$path = dirname(__FILE__).'/';
 
$db = MysqlDB::GetDBO();
$db->Query('SET NAMES UTF8');
 
$query = "
SELECT it.*, cat.name AS nameCatalog, it.picture_ym AS big_pic, it.description AS it_descr, f.name AS f_name, iv.volume AS iv_volume, iv.price AS iv_price
FROM items AS it
LEFT JOIN catalog AS cat ON (cat.id=it.catalog_id AND cat.is_active=1)
LEFT JOIN fabrics AS f ON (it.fabrics_id=f.id)
LEFT JOIN items_volumes AS iv ON (it.id = iv.item_id)
WHERE it.is_ym='1' AND it.is_active='1'";
 
$items = $db->SelectSet($query);
 
$yml = new yml();
$yml->set_shop($site, $site, $siteUri);
$yml->add_currency('RUR', 1);
if (count($items) > 0) {
$cats = $db->SelectSet("SELECT id, parent_id, name FROM catalog WHERE is_active = 1;", 'id');
 
$parents = array();
$subCats = array();
foreach($items as $id => $item) {
  $cat = $cats[$item['catalog_id']];
  while($cat['parent_id']) {
    $subCats[$cat['id']] = array('name' => $cat['name'], 'parent_id' => $cat['parent_id']);
    $cat = $cats[$cat['parent_id']];
  }
$parents[$cat['id']] = $cat['name'];
}
 
if (count($parents) > 0) foreach ($parents as $idG => $group) $yml->add_category(strip_tags($group), $idG);
$subgroups = $db->SelectSet("SELECT DISTINCT it.catalog_id, cat.name as catName, cat.parent_id FROM items it LEFT JOIN catalog cat ON cat.id=it.catalog_id AND cat.is_active=1 WHERE it.is_ym='1' AND it.is_active='1' AND cat.parent_id > 0");
if (count($subCats) > 0) foreach ($subCats as $idS => $subgroup) $yml->add_category(htmlspecialchars(strip_tags($subgroup['name'])), $idS, $subgroup['parent_id']);
foreach ($items as $id => $item) {
  $description = prepareTextField($item['it_descr']);
  $vendor = prepareTextField($item['f_name']);
  $name = prepareTextField($item['name']);
 
  $severalVolumes = !empty($item['iv_price']) && !empty($item['iv_volume']); // является ли продукт одной из форм выпуска с разным объемом?
  $price = $severalVolumes ? $item['iv_price'] : $item['price'];
  $volume = $severalVolumes ? $item['iv_volume'] : $item['volume'];
  $url = !empty($item['url']) ? $item['url'] : 'item_'.$item['id'].'.htm';
  if (!empty($price) && $price > 0 )
  $yml->add_offer($id, array(
    'url' => $siteUri.$url.'?utm_source=YandexMarket&amp;utm_medium=cpc&amp;utm_campaign=YandexMarket',
    'price' => number_format($price, 0, '.', ''),
    'currencyId' => 'RUR',
    'categoryId' => $item['catalog_id'],
    'picture' => $item['big_pic'] ? $siteUri.ltrim($item['big_pic'], '/') : '',
    'name' => $name.(!empty($volume) ? ' ('.$volume.' мл)' : null),
    'sales_notes' => 'Минимальная сумма заказа 500 рублей.',
    'description' => $description,
    'vendor' => $vendor,
    ), true);
  }
}
 
$xml = $yml->get_xml();
echo $xml;
die();
 
?>
yml.php
<?php
####################################################
# class for generation YML (yandex market language)
####################################################

class yml
{
  var $from_charset = 'utf-8';
  var $shop = array('name'=>'', 'company'=>'', 'url'=>'');
  var $currencies = array();
  var $categories = array();
  var $offers = array();
 
  # конструктор
  function yml($from_charset = 'utf-8')
  {
    $this->from_charset = trim(strtolower($from_charset));
  }
 
  # преобразование массива в тег
  function convert_array_to_tag($arr)
  {
    $s = '';
    foreach($arr as $tag=>$val)
    {
      $s .= '<'.$tag.'>'.$val.'</'.$tag.'>';
    }
    $s .= "\r\n";
    return $s;
  }
 
  # преобразование массива в атрибуты
  function convert_array_to_attr($arr, $tagname, $tagvalue = '')
  {
    $s = '<'.$tagname.' ';
    foreach($arr as $attrname=>$attrval)
    {
      $s .= $attrname . '="'.$attrval.'" ';
    }
    $s .= ($tagvalue!='') ? '>'.$tagvalue.'</'.$tagname.'>' : '/>';
    $s .= "\r\n";
    return $s;
  }
 
  # подготовка текстового поля в соответствии с требованиями Яндекса
  function prepare_field($s)
  {
    $from = array('"', '&', '>', '<', '\'');
    $to = array('&quot;', '&amp;', '&gt;', '&lt;', '&apos;');
    $s = str_replace($from, $to, $s);
    if ($this->from_charset!='utf-8') $s = iconv($this->from_charset, 'utf-8//IGNORE//TRANSLIT', $s);
    $s = preg_replace('#[\x00-\x08\x0B-\x0C\x0E-\x1F]+#is', ' ', $s);
    return trim($s);
  }
 
  # указать данные магазина
  # @name - название интернет-магазина
  # @company - официальное название компании
  # @url - адрес сайта
  function set_shop($name, $company, $url)
  {
    $this->shop['name'] = $this->prepare_field($name);
    $this->shop['name'] = substr($this->shop['name'], 0, 20);
    $this->shop['company'] = $this->prepare_field($company);
    $this->shop['url'] = $this->prepare_field($url);
  }
 
  # добавить валюту магазина
  # @id - код валюты (RUR, USD, EUR...)
  # @rate - CBRF или свой курс
  # @plus учитывается только в случае rate = CBRF и означает насколько увеличить курс в процентах от ЦБ РФ
  function add_currency($id, $rate = 'CBRF', $plus = 0)
  {
    $rate = strtoupper($rate);
    $plus = str_replace(',', '.', $plus);
    if ($rate=='CBRF' && $plus>0)
      $this->currencies[] = array('id'=>$this->prepare_field(strtoupper($id)), 'rate'=>'CBRF', 'plus'=>(float)$plus);
    else
    {
      $rate = str_replace(',', '.', $rate);
      $this->currencies[] = array('id'=>$this->prepare_field(strtoupper($id)), 'rate'=>(float)$rate);
    }
    return true;
  }
 
  # добавление категории товаров
  # @id - id рубрики
  # @parent_id - id родительской рубрики, если нет, то -1
  # @name - название рубрики
  function add_category($name, $id, $parent_id = -1)
  {
    if ((int)$id<1||trim($name)=='') return false;
    if ((int)$parent_id>0)
      $this->categories[] = array('id'=>(int)$id, 'parentId'=>(int)$parent_id, 'name'=>$this->prepare_field($name));
    else
      $this->categories[] = array('id'=>(int)$id, 'name'=>$this->prepare_field($name));
    return true;
  }
 
  # добавление позиции
  # @id - id товара
  # @available - товар доступен сейчас (true) или на заказ (false)
  # @data - массив остальных параметров (звездочкой помечены обязательные)
  # *url - URL-адрес страницы товара
  # *price - цена товара
  # *currencyId - идентификатор валюты товара (RUR, USD, UAH...)значением цены.
  # *categoryId - идентификатор категории товара (целое число не более 18 знаков). Товарное предложение может принадлежать только одной категории
  # picture - Ссылка на картинку соответствующего товарного предложения. Недопустимо давать ссылку на "заглушку", т.е. на картинку где написано "картинка отсутствует" или на логотип магазина
  # *delivery - элемент, обозначающий возможность доставить соответствующий товар. "false" данный товар не может быть доставлен("самовывоз"). "true" товар доставляется на условиях, которые указываются в партнерском интерфейсе http://partner.market.yandex.ru на странице "редактирование".
  # *name - наименование товарного предложения
  # vendor - производитель
  # vendorCode - Код товара (указывается код производителя)
  # *description - Описание товарного предложения
  # country_of_origin - Элемент предназначен для указания страны производства товара.
  # downloadable - Элемент предназначен обозначения товара, который можно скачать.
  function add_offer($id, $data, $available = true)
  {
    $allowed = array('url', 'price', 'currencyId', 'categoryId', 'picture', 'delivery', 'name', 'vendor', 'vendorCode', 'description', 'sales_notes', 'country_of_origin', 'downloadable');
 
    foreach($data as $k=>$v)
    {
      if (!in_array($k, $allowed)) unset($data[$k]);
      $data[$k] = strip_tags($this->prepare_field($v));
    }
    $tmp = $data;
    unset($data);
    foreach($allowed as $key)
    {
      if (isset($tmp[$key])) $data[$key] = $tmp[$key]; # Порядок важен для Я.Маркета!!!
    }
    $this->offers[] = array('id'=>(int)$id, 'data'=>$data, 'available'=>($available)?'true':'false');
  }
 
  # шапка документа
  function get_xml_header()
  {
    return '<?xml version="1.0" encoding="utf-8"?><!DOCTYPE yml_catalog SYSTEM "shops.dtd"><yml_catalog date="'.date('Y-m-d H:i').'">';
  }
 
  # тело документа
  function get_xml_shop()
  {
    $s = '<shop>' . "\r\n";
 
      # shop info
      $s .= $this->convert_array_to_tag($this->shop);
 
      # currencies
      $s .= '<currencies>' . "\r\n";
      foreach($this->currencies as $currency)
      {
        $s .= $this->convert_array_to_attr($currency, 'currency');
      }
      $s .= '</currencies>' . "\r\n";
 
      # categories
      $s .= '<categories>' . "\r\n";
      foreach($this->categories as $category)
      {
        $category_name = $category['name'];
        unset($category['name']);
        $s .= $this->convert_array_to_attr($category, 'category', $category_name);
      }
      $s .= '</categories>' . "\r\n";
 
      # offers
      $s .= '<offers>' . "\r\n";
      foreach($this->offers as $offer)
      {
        $data = $offer['data'];
        unset($offer['data']);
        $s .= $this->convert_array_to_attr($offer, 'offer', $this->convert_array_to_tag($data));
      }
      $s .= '</offers>' . "\r\n";
 
    $s .= '</shop>';
    return $s;
  }
 
  # футер документа
  function get_xml_footer()
  {
    return '</yml_catalog>';
  }
 
  # получить весь XML код
  function get_xml()
  {
    $xml = $this->get_xml_header();
    $xml .= $this->get_xml_shop();
    $xml .= $this->get_xml_footer();
    return $xml;
  }
}