mail@vecdev.ru

Формирование прайс-листа из торговых предложений XML для E-catalog

В этой статье мы создадим выгрузку товаров в формат 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, файл рассматривают специалисты, после чего связываются примерно с таким текстом:

E-catalog


Отправляем реквизиты, оплачиваем и запускаемся! Если есть вопросы, пишите в комменты!

Частный разработчик сайтов Vector Dev
Комментарии