В этой статье мы создадим выгрузку товаров в формат XML, для подключения интернет-магазина к сервису E-catalog и magazilla.ru.
Немного усложним себе задачу и формировать фид будем не из простых товаров, а из товаров с торговыми предложениями.
Сам шаблон их фида выглядит так:
<?xml version="1.0" encoding="utf-8"?>
<yml_catalog date="2022-01-21 21:41:30">
<shop>
<name>...</name>
<url>...</url>
<currencies>
<currency id="USD" rate="75"/>
<currency id="RUR" rate="1"/>
</currencies>
<catalog>
<category id="...">...</category>
<category id="..." parentId="...">...</category>
</catalog>
<offers>
<offer id="...">
<name>...</name>
<url>...</url>
<price>...</price>
<categoryId>...</categoryId>
<brand>...</brand>
<image>...</image>
<description>...</description>
</offer>
<offer id="...">
<name>...</name>
<url>...</url>
<price>...</price>
<categoryId>...</categoryId>
<brand>...</brand>
<image>...</image>
<description>...</description>
</offer>
</offers>
</shop>
</yml_catalog>
Требования E-catalog к параметрам XML фида товаров
Есть несколько обязательных параметров:
<vendor>…</vendor>
- производитель<name>…</name>
- модель<price>…</price>
- цена<url>…</url>
- ссылка на позицию на сайте<categoryId>…</categoryId>
- ссылку на категорию, к которой относится товар<merchant>…</merchant>
- если предложение нет от “оригинального” магазина в составе маркетплейса
Ну и они настоятельно рекомендуют, чтобы мы прописали дополнительные параметры к каждому товару:
<image>…</image>
- обязательно использование в URL ссылок https, переадресация для этого тега невозможна.-
<description>…</description>
- краткое описание характеристик товара (только текст, без использования тэгов). -
<manufacturer_warranty> true </manufacturer_warranty>
- указание гарантии на товар, описание параметров в подробном описании тегов. <stock>под заказ</stock>
- если ваше предложение заказное, описание параметров в подробном описании тегов.
Для начала нам нужно собрать массив разделов каталога для участка фида:<catalog><category id="...">...</category></catalog>
, для этого делаем выборку из разделов инфоблока товаров с ID = 5.
Забрать нам нужно только ID и название раздела и каждого подраздела.
<?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/prolog_before.php");
if (CModule::IncludeModule("sale") && CModule::IncludeModule("catalog")) {
if(CModule::IncludeModule('iblock')) {
$IBLOCK_ITEMS = 5;//ID инфоблока товаров
$IBLOCK_SKU = 6;//ID инфоблока торговых предложений
//Получаю структуру разделов каталога
$arParentSection = array(); // массив для сбора данных разделов
$arSelect = Array("ID", "NAME", "IBLOCK_SECTION_ID"); //мне нужны только название, ID раздела и ID родителя
$arFilter = array('IBLOCK_ID' => $IBLOCK_ITEMS, 'ACTIVE' => 'Y'); //ищу только в активных разделах
$res = CIBlockSection::GetList(array('sort','asc'), $arFilter, false, $arSelect);
$flag = 0;
while($ob = $res->GetNextElement()) {
$arFields = $ob->GetFields(); //получаю все поля
$arParentSection[$flag]["ID"] =$arFields["ID"];
$arParentSection[$flag]["PARENT_SECTION"] = $arFields["IBLOCK_SECTION_ID"];
$arParentSection[$flag]["NAME"] =$arFields["NAME"];
$flag++;
}
?>
В результате у нас будет формирован массив данных, где у разделов перового уровня [PARENT_SECTION] будет пустой, а у разделов второго и т.д. элементу массива будет присвоено значение ID родительского раздела.
Array
(
[0] => Array
(
[ID] => 58 //ID раздела
[PARENT_SECTION] => //ванны - раздел первого уровня
[NAME] => Ванны//название раздела
)
[1] => Array
(
[ID] => 112 //ID раздела
[PARENT_SECTION] => 58 //у прямоугольных ванн родитель - ванны
[NAME] => Прямоугольные ванны //название раздела 2го уровня
)
)
Во второй части кода мы делаем выборку товаров и торговых предложений. В первом цикле, с помощью метода CIBlockElement::GetList мы обращаемся к самим товарам и забираем только их ID, ID раздела в котором они находятся и ссылку на детальную карточку товара.
Далее внутри этого цикла зная ID товара с помощью метода CCatalogSKU::getOffersList мы обращаемся к торговому предложению товара и забираем уже его наименование, состояние "в наличии (CATALOG_AVAILABLE и ACTIVE)", картинку товара, описание (оно должно быть без тегов). Описание, вообще, идеальней всего было бы составить с помощью цикла, перебрав все характеристики и записав их через ";", но мне было лень этим заниматься, пишите в комменты если не сможете сделать это самостоятельно.
// получаю данные по товарам
$arResult = array(); // итоговый массив для товаров
$arInfo = CCatalogSKU::GetInfoByProductIBlock($IBLOCK_ITEMS); // проверка, является ли инфоблок торговым, если да, то вернутся массив товаров, если нет - false
$arSelect = Array("ID", "IBLOCK_SECTION_ID", 'DETAIL_PAGE_URL'); // забираем ID, ID раздела, ссылку на товар
$arFilter = array('IBLOCK_ID' => $IBLOCK_ITEMS); // ищем в инфоблоке с ID=5
if (is_array($arInfo)) { //если инфоблок все-таки торговый
$res = CIBlockElement::GetList(array('sort','asc'), $arFilter, false, false, $arSelect); //то делаем выборку
}
while($ob = $res->GetNextElement()) {
$arFields = $ob->GetFields(); //получаю все поля
//получаю торговые предложения товара методом CCatalogSKU::getOffersList
// мне надо забрать ID, название, автивность и доступность к продаже, картинку и описание
$skuFields = array('IBLOCK_ID', 'ID', 'PARENT_ID', 'NAME', 'ACTIVE', 'CATALOG_AVAILABLE', 'DETAIL_PICTURE', 'PREVIEW_TEXT');
$sku_res = CCatalogSKU::getOffersList($arFields, $iblockID = $IBLOCK_ITEMS, $skuFilter = array(), $skuFields, $propertyFilter = array());
foreach ($sku_res as $sku_item){
foreach ($sku_item as $sku){
$arResult[$sku["ID"]]["ID"] = $sku["ID"]; //пишу в массив ID торгового предложения
$arResult[$sku["ID"]]["SECTION_ID"] = $arFields["IBLOCK_SECTION_ID"]; //пишу ID раздела товара из 1го цикла
$arResult[$sku["ID"]]["PARENT_ID"] = $sku["PARENT_ID"]; //пишу ID товара, которому принадлежит торг. предложение (необязательно)
$arResult[$sku["ID"]]["ACTIVE"] = $sku["ACTIVE"]; //пишу активность
$arResult[$sku["ID"]]["CATALOG_AVAILABLE"] = $sku["CATALOG_AVAILABLE"]; //доступность к продаже
// записываю цену в евро (хз зачем, пусть будет, e-katalog может забирать несколько цен, но пока не буду в XML евро отдавать)
$arPrice = CPrice::GetBasePrice($sku["ID"], false, false); //получаю базовую цену (у нас базовая в ЕВРО), у вас наверное будет в рублях
$arResult[$sku["ID"]]["PRICE"]["EUR"]["CURRENCY"] = $arPrice["CURRENCY"]; // тип валюты (у нас EUR, у вас RUB)
$arResult[$sku["ID"]]["PRICE"]["EUR"]["PRICE"] = $arPrice["PRICE"]; // сама цена торг. предл.
//конвертация валюты из евро в рубли согласно курса, который указывается в админке (раздел валюты)
if($arPrice["CURRENCY"] == "EUR"){ //если у нас ЕВРО базовая цена
$arResult[$sku["ID"]]["PRICE"]["RUR"]["CURRENCY"] = "RUR"; //валюта рубли
$arResult[$sku["ID"]]["PRICE"]["RUR"]["PRICE"] = CCurrencyRates::ConvertCurrency($arPrice["PRICE"], "EUR", "RUR"); //обмениваем валюту, прям как в банке =)
}
//получаю картинку
$sku_picture = CFile::GetFileArray($sku['DETAIL_PICTURE']); //зная только ID картинки, обращаюсь чтобы получить массив данных по ней
$arResult[$sku["ID"]]["PICTURE"] = "https://site.ru".$sku_picture["SRC"]; //пишу адрес к картинке в массив
$arResult[$sku["ID"]]["BRAND"] = 'Ravak'; // если у вас мультибренд, то тут надо указать свойство бренда
$arResult[$sku["ID"]]["NAME"] = $sku["NAME"]; //название торгового предложения
$arResult[$sku["ID"]]["DETAIL_PAGE_URL"] = "https://site.ru".$arFields["DETAIL_PAGE_URL"]; //ссылка на товар на вашем сайте
$arResult[$sku["ID"]]["DESCRIPTION"] = $sku["PREVIEW_TEXT"]; //текст описания, взял из превью, так как нужно отдавать без тегов чистым текстом
}
}
}
} // закрытие if (CModule::IncludeModule("sale") && CModule::IncludeModule("catalog"))
}//закрытие if(CModule::IncludeModule('iblock'))
В итоге у нас получится массив следующего вида:
[5421] => Array (
[ID] => 5421 // ID торгового предложения
[SECTION_ID] => 13 //ID раздела
[PARENT_ID] => 5420 // ID товара
[ACTIVE] => Y //активность
[CATALOG_AVAILABLE] => Y //доступность
[PRICE] => Array //цены
(
[EUR] => Array
(
[CURRENCY] => EUR
[PRICE] => 489.00
)
[RUR] => Array
(
[CURRENCY] => RUR
[PRICE] => 44988
)
)
[PICTURE] => https://site.ru/upload/iblock/953/scqnp22jkzvtveq4tu2953vfusn4b5o6.jpg //катинка
[BRAND] => Ravak //бренд
[NAME] => Набор BLIX (Кабина, поддон и сифон Ravak)
[DETAIL_PAGE_URL] => https://site.ru/catalog/nabor-blix-kabina-poddon-i-sifon-ravak-/ //ссылка
[DESCRIPTION] => Я описание товара, дратути! //описание
)
// ну и второй товар такой же по структуре
[5416] => Array
(
[ID] => 5416
[SECTION_ID] => 65
[PARENT_ID] => 5415
[ACTIVE] => Y
[CATALOG_AVAILABLE] => Y
[PRICE] => Array
(
[EUR] => Array
(
[CURRENCY] => EUR
[PRICE] => 486.00
)
[RUR] => Array
(
[CURRENCY] => RUR
[PRICE] => 44712
)
)
[PICTURE] => https://site.ru
[BRAND] => Ravak
[NAME] => Комплект крепления Ravak Brilliant B SET BSD3 120 левый
[DETAIL_PAGE_URL] => https://site.ru/catalog/aksessuary/derzhateli/komplekt-krepleniya-ravak-brilliant-b-set-bsd3-120-levyy-/
[DESCRIPTION] =>
)
Итак, на выходе мы имеем 2 массива данных: первый - со всеми данными по разделам каталога товаров, второй - со всеми товарами. Теперь всё что нам надо это сформировать XML товаров для E-catalog.
$dom = new domDocument("1.0", "utf-8"); // Создаём XML-документ версии 1.0 с кодировкой utf-8
$root = $dom->createElement("yml_catalog"); // Создаём корневой элемент с названием как требует E-catalog
$root->setAttribute("date", date('Y-m-d h:i:s')); //даем тегу атрибут с текущей датой
$shop = $dom->createElement("shop"); // создаем тег шоп
//базовые настройки
$name = $dom->createElement("name", "Сантехника-Ravak"); //название магазина
$url = $dom->createElement("url", "https://site.ru"); //его адрес
Базовые настройки готовы, мы инициировали формирование XML документа для E-catalog, теперь нужно добавить в него используемые валюты.
//валюты
$currencies = $dom->createElement("currencies"); // создаем элемент валюты, как требует E-catalog
$currEur = $dom->createElement("currency"); // элемент для евро
$currRub = $dom->createElement("currency"); // для рубля
$currEur->setAttribute("id", "EUR"); //даем атрибут типа валюты
$currEur->setAttribute("rate", "91"); //пишем курс евро к рублю 91 к 1
$currRub->setAttribute("id", "RUR");
$currRub->setAttribute("rate", "1"); //курс рубль у рублю 1 к 1
$currencies->appendChild($currEur); //записываем элемент евро в валюты
$currencies->appendChild($currRub); // тоже самое с рублем
Выше мы добавили валюты в XML документ согласно шаблона фида товаров, который требует E-catalog. Теперь необходимо добавить категории товаров из первого массива данных. Порядок сортировки внутри массива абсолютно не важен, главное отдать все данные.
//категории
$categories = $dom->createElement("categories"); // создаю элемент категорий
foreach ($arParentSection as $section){ // перебираем наш массив разделов
// создаем элемент XML с названием раздела пишем в него значение названия из массива
$category = $dom->createElement("category", $section["NAME"]);
$category->setAttribute("id", $section["ID"]); // указываем атрибут ID
// если ID родительского раздела не пустое, то указываем его
if($section["PARENT_SECTION"] != ''){
$category->setAttribute("parentID", $section["PARENT_SECTION"]);
}
$categories->appendChild($category); //записываем элемент XML "категория" в раздел "категории"
}
Разделы собраны, осталось только добавить в XML наши торговые предложения, тут тоже всё просто.
//создаем элемент offers для XML документа и перебираем массив
$offers = $dom->createElement("offers");
foreach ($arResult as $item){
$offer = $dom->createElement("offer"); // создаем элемент XML offer, как того требует E-catalog
$offer->appendChild($dom->createElement("url", $item["DETAIL_PAGE_URL"])); //пишем адрес
$offer->appendChild($dom->createElement("price", $item["PRICE"]["RUR"]["PRICE"])); //пишем цену
$offer->appendChild($dom->createElement("currencyId", $item["PRICE"]["RUR"]["CURRENCY"])); //пишем тип валюты (рубли), евро не пишу в XML, потому что не особо надо
$offer->appendChild($dom->createElement("categoryId", $item["SECTION_ID"])); //ID раздела, которому принадлежит торговое предложение
$offer->appendChild($dom->createElement("picture", $item["PICTURE"])); //картинка
$offer->appendChild($dom->createElement("brand", "RAVAK")); //бренд
$offer->appendChild($dom->createElement("name", $item["NAME"])); //название
$offer->appendChild($dom->createElement("manufacturer_warranty", "true"));
$offer->appendChild($dom->createElement("description", $item["DESCRIPTION"])); //описание
$offers->appendChild($offer); //записываю торговое предложение в элемент XML offers
}
После выполнения цикла, наш фид товаров для E-catalog полностью сформирован, согласно всех требований. Осталось только записать все элементы XML в родительский и сохранить сам документ. Мудрить особо не буду и сделаю так, чтобы документ появлялся и перезаписывался рядом с нашим php файлом обработчика.
$shop->appendChild($name);
$shop->appendChild($url);
$shop->appendChild($currencies);
$shop->appendChild($categories);
$shop->appendChild($offers);
$root->appendChild($shop);
$dom->appendChild($root);
$dom->save("e-katalog.xml"); // Сохраняем полученный XML-документ в файл
Теперь можно поставить выполнение файла на агентах или на сам cron, кстати читайте нашу статью как настроить агенты на Cron и systemd
Ну и всё, отправляем ссылку на наши товары в сервис nadavi, файл рассматривают специалисты, после чего связываются примерно с таким текстом:
Отправляем реквизиты, оплачиваем и запускаемся! Если есть вопросы, пишите в комменты!