Одним из способов продвижения бизнеса является заполнение и поддержка информации в сервисах карт. В данной статье расскажем о том, как можно без сторонних сервисов и решений улучшить отображение вашей организации на Яндекс картах и 2GIS, добавив для нее товарный фид. Данное руководство актуально, если сайт работает на CMS-системе WordPress (WP).
Начнем с небольшой теории. Товарный фид - файл с информацией о товарах или услугах. Там содержатся: названия, фотографии, цены и характеристики.
Яндекс товарному фиду в документации дает следующее определение: Товарный фид — это файл формата YML с информацией о магазине и товарах. YML (Yandex Market Language) — это собственный стандарт Яндекса, основанный на XML. В товарном фиде можно описать каталог магазина в формате, удобном для автоматической генерации.
Скорее всего, вы постоянно натыкаетесь на товарные фиды разных компаний, но для примера продемонстрируем несколько фидов наших клиентов:


Не можем не сказать, что функционал формирования товарных фидов является уникальным. В репозитории расширений практически любой CMS-системы будет формирование товарных фидов, а для WordPress тем более. Кстати, даже Яндекс, на странице, рассмотренной выше документации приводит несколько расширений, но с оговоркой, что к разработке отношения не имеет и ответственности за использование не несет.

В данном руководстве будет много PHP кода, который в рамках вашего сайта придется серьезно доработать, иначе ничего не получится. Материал подразумевает, что вы знакомы с основными функциями и классами WP такими как: get_terms(); WP_Query(); get_field(); get_post_thumbnail_id() и другими из представленного кода.
Перейдем к написанию собственного товарного фида. Файл с товарным фидом должен лежать в корневой папке сайта, за пределами папок WP.
В качестве примера мы рассмотрим товарный фид, который делали для СЗПК78, пошагово рассмотрим каждый этап создания. Обратите внимание, что в составлении фида мы будем опираться на документацию и примеры.
Шаг 1. Создаем файл и определяем базовые настройки
В корневой папке сайта "/" мы создаем файл yandex.php.
Данный код создаёт XML-документ с правильной кодировкой (UTF-8 + BOM для кириллицы), подключает WordPress для доступа к базе данных, формирует «шапку» YML-файла с названием магазина, URL и текущей датой генерации.
<?php
// Прописываем значения, чтобы файл читался и открывался как XML
header("Content-type: text/xml; charset=UTF-8");
print (pack('CCC', 0xef, 0xbb, 0xbf));
print
"<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE yml_catalog SYSTEM 'shops.dtd'>
<yml_catalog date='".date('Y-m-d H:i')."'>
<shop>
<name>СЗПК78</name>
<url>https://szpk78.ru/</url>
";
// Подключаем библиотеки CMS WordPress
define('WP_USE_THEMES', false);
require_once('wp-load.php');
Шаг 2. Добавим категории товаров
Создаём функцию, которая получает и выводит категории товаров из WordPress. Каждая категория выводится с указанием её ID и, если есть, родительской категории. Вызываем для каждой из категорий.
function printCategories($category) {
$args = array(
'taxonomy' => $category,
'hide_empty' => false,
);
$terms = get_terms($args);
foreach($terms as $item){
print "<category id='".$item->term_id."'";
if($item->parent != 0){
print " parentId='".$item->parent."'";
}
print ">".htmlspecialchars(trim($item->name))."</category>\n";
}
}
print "<categories>";
printCategories('garden_cat');
printCategories('package_cat');
printCategories('products_cat');
print "</categories>";
Шаг 3. Выводим товары
Получаем опубликованные товары определённого типа и формируем блоки <offer>
. В блоках указывается цена, ссылка, категория, изображение, название и описание товара. Если у товара нет цен, он пропускается.
function printOffers($post_type, $category) {
$args = array(
'post_type' => $post_type,
'posts_per_page' => -1,
'post_status' => 'publish',
);
$query = new WP_Query;
$allproducts = $query->query($args);
foreach($allproducts as $allproduct){
$pricesArr = get_field('block_price_product', $allproduct->ID);
if (empty($pricesArr)) continue;
// Определяем конечную категорию товара
$lastcateg = '';
if ($categorys = get_the_terms($allproduct->ID, $category)) {
if (count($categorys) > 1) {
$arrtemp = array();
foreach($categorys as $category){
$arrtemp[] = $category->term_id;
}
foreach($arrtemp as $arrtempz){
$termchildren = get_term_children($arrtempz, $category);
if ($termchildren == null) {
$lastcateg = $arrtempz;
break;
}
foreach($termchildren as $child){
if (!in_array($child, $arrtemp)) {
$lastcateg = $arrtempz;
break 2;
}
}
}
} else {
$lastcateg = $categorys[0]->term_id;
}
}
$product_img = wp_get_attachment_image_src(get_post_thumbnail_id($allproduct->ID), 'full');
foreach ($pricesArr['info_product'] as $price_key => $price) {
$offer_id = $allproduct->ID + $price_key;
print "<offer id='".$offer_id."' available='true'>";
print "<url>".get_permalink($allproduct->ID)."</url>";
print "<price>".$price['price_product']."</price>";
print "<currencyId>RUR</currencyId>";
print "<categoryId>".$lastcateg."</categoryId>";
print "<vendor>СЗПК78</vendor>";
if($product_img[0])
print "<picture>".$product_img[0]."</picture>";
$product_title = $allproduct->post_title . ' ' . $price['width_product'] . 'х' . $price['length_product'] . 'х' . $price['height_product'] . ' см';
$product_descr = $allproduct->post_title;
if (!empty($price['explanation'])) {
$product_title .= ' (' . $price['explanation'] . ')';
$product_descr .= ' (' . $price['explanation'] . ')';
}
$product_descr .= '. Размеры: ' . $price['width_product'] . ' см (ширина), ';
$product_descr .= $price['length_product'] . ' см (длина), ';
$product_descr .= $price['height_product'] . ' см (высота).';
print "<name>".htmlspecialchars($product_title)."</name>";
print "<description><![CDATA['".htmlspecialchars(strip_tags($product_descr))."]]></description>";
print "</offer>";
}
}
}
print "<offers>";
printOffers('garden', 'garden_cat');
printOffers('package', 'package_cat');
printOffers('products', 'products_cat');
print "</offers>";
Шаг 4. Завершаем генерацию
Закрываем все ранее открытые XML-теги </shop>
и </yml_catalog>
, завершив генерацию файла.
print "</shop>
</yml_catalog>
";
?>
Полный код для удобства:
<?php
header("Content-type: text/xml; charset=UTF-8");
print (pack('CCC', 0xef, 0xbb, 0xbf));
print "<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE yml_catalog SYSTEM 'shops.dtd'>
<yml_catalog date='".date('Y-m-d H:i')."'>
<shop>
<name>СЗПК78</name>
<url>https://szpk78.ru/</url>
";
define('WP_USE_THEMES', false);
require_once('wp-load.php');
print "<currencies><currency id='RUB' rate='1'/></currencies>";
function printCategories($category) {
$args=array(
'taxonomy' => $category,
'hide_empty' => false,
);
$terms=get_terms($args);
foreach($terms as $item){
print "<category id='".$item->term_id."'";
if($item->parent!=0){
print " parentId='".$item->parent."'";
}
print ">".htmlspecialchars(trim($item->name))."</category>
";
}
}
print "<categories>";
printCategories('garden_cat');
printCategories('package_cat');
printCategories('products_cat');
print "</categories>";
function printOffers($post_type, $category) {
$args=array(
'post_type' => $post_type,
'posts_per_page' => -1,
'post_status' => 'publish',
);
$query = new WP_Query;
$allproducts = $query->query($args);
foreach($allproducts as $allproduct){
$pricesArr = get_field('block_price_product', $allproduct->ID);
if (empty($pricesArr)) {
continue;
}
$lastcateg='';
if($categorys=get_the_terms($allproduct->ID,$category)){
if(count($categorys)>1){
$arrtemp=array();
foreach($categorys as $category){
$arrtemp[]=$category->term_id;
}
foreach($arrtemp as $arrtempz){
$termchildren=get_term_children($arrtempz,$category);
if($termchildren==null){
$lastcateg=$arrtempz;
break;
}
foreach($termchildren as $child){
if(!in_array($child,$arrtemp)){
$lastcateg=$arrtempz;
break 2;
}
}
}
}else{
$lastcateg=$categorys[0]->term_id;
}
}
$product_img=wp_get_attachment_image_src(get_post_thumbnail_id($allproduct->ID),'full');
foreach ($pricesArr['info_product'] as $price_key => $price) {
$offer_id = $allproduct->ID+$price_key;
print
"
<offer id='".$offer_id."' available='true'>
<url>".get_permalink($allproduct->ID)."</url>
<price>".$price['price_product']."</price>
<currencyId>RUR</currencyId>
<categoryId>".$lastcateg."</categoryId>
<vendor>СЗПК78</vendor>
";
if($product_img[0])
print "<picture>".$product_img[0]."</picture>
";
$product_title = $allproduct->post_title;
$product_title .= ' ' . $price['width_product'] . 'х' . $price['length_product'] . 'х' . $price['height_product'] . ' см';
$product_descr = $allproduct->post_title;
if (!empty($price['explanation'])) {
$product_title .= ' (' . $price['explanation'] . ')';
$product_descr .= ' (' . $price['explanation'] . ')';
}
$product_descr .= '. Размеры: ' . $price['width_product'] . ' см (ширина), ';
$product_descr .= $price['length_product'] . ' см (длина), ';
$product_descr .= $price['height_product'] . ' см (высота).';
print "<name>".htmlspecialchars($product_title)."</name>
<description><![CDATA['".htmlspecialchars(strip_tags($product_descr))."]]></description>
</offer>
";
}
}
}
print "<offers>";
printOffers('garden', 'garden_cat');
printOffers('package', 'package_cat');
printOffers('products', 'products_cat');
print "</offers>";
print "</shop>
</yml_catalog>
";
?>
Проверим работу нашего фида
Чтобы проверить, как прошла генерация нам нужно в браузере перейти по адресу: доменное имя/название файла. В нашем случае szpk78.ru/yandex.php. Если все сделано верно, то на странице вы увидите xml файл с информацией о продукции.

Публикация фида в сервисы карты
Яндекс карты
На момент написания данного материала Яндекс принимает файл в расширении xml. Чтобы получить файл нам нужно сохранить страницу, это можно сделать сочетанием клавиш "ctrl(command)+s" или "ctrl(command)+shift+s". Будет скачан файл с расширением php, вручную меняем расширение в xml.
Если на прошлых этапах проблем не возникло, то переходим к отправке фида в сервисы карт. Для того, чтобы опубликовать товары в яндекс картах нужно авторизоваться в яндекс бизнесе с аккаунта, с которым связана ваша организация. Затем, открыть вкладку "О компании" и там выбрать "товары и услуги". В открывшемся окне жмем на кнопку "Загрузить XLS/YML" и добавить наш файл.

После недолгой модерации товары будут загружены или отобразится ошибка.

2GIS
Для загрузки на 2GIS требуется также авторизация, обратите внимание, что авторизироваться нужно через кнопку "Вход для бизнеса", разумеется, с того аккаунта, к которому вас привязана организация для которой вы сделали фид.
После авторизации переходим по "Моя компания/Товары и услуги". Дальнейшие шаги схожи с Яндекс картами, но отметим, что тут мы можем передать урл нашего фида. Таким образом, обновляя данные в файле, он автоматически будет обновляться и в 2GIS, примерно раз в сутки.

Если у вас не было проблем с публикацией в Яндексе, то не должно быть и тут. После модерации должны появиться товары:

В данной статье мы разобрались как создать товарный фид для сервисов карт без использования сторонних решений и сервисов, а затем разместить в Яндексе и 2GIS.