При работе с интернет-магазинами на WordPress часто возникает необходимость создания YML-фида для загрузки товаров в Яндекс.Маркет. Если товары в проекте представлены кастомными постами, а их характеристики и вариации хранятся в полях Advanced Custom Fields (ACF), то можно создать YML-фид программно. В этой статье подробно разберём, как это сделать, с примерами кода и рекомендациями.
Прежде чем приступить к генерации YML-фида, необходимо убедиться, что все товары содержат необходимые данные. Для этого в ACF должны быть настроены соответствующие поля, например:
Также стоит проверить, какие таксономии используются в теме WordPress. Это можно сделать в файле /wp-content/themes/[название темы]/functions.php
или в подключённых к нему модулях. Например, если кастомный тип записей — catalog, а таксономия для категорий — category_catalog, то их регистрация может выглядеть так:
// Регистрация нового типа записей "catalog" (пользовательский post type).
add_action( 'init', function() {
register_post_type( 'catalog', array(
// ...
)
});
// Создание таксономии "category_catalog" и привязка её к новому типу записей
add_action('init', function () {
register_taxonomy('category_catalog', array('catalog'), array(
// ...
));
});
Чтобы определить, какие ACF-поля используются для хранения характеристик товаров, нужно открыть ACF → Группы полей в админке WordPress. Найти соответствующие группы и запомните их системные имена. Например, если есть "configurator" (с полями "power" и "price") и "custom_parametrs" (с полями "nazvanie", "znachenie" и "czena"), то их можно использовать в фиде.
Для создания фида нужно создать PHP-скрипт, который будет формировать XML-документ с данными о товарах для специального робота.
Далее нужно создать файл, например, "yml-feed.php" в корне сайта:
<?php
require_once __DIR__ . '/wp-load.php';
header("Content-Type: application/xml; charset=UTF-8");
echo '<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL;
// Специальный класс для товара
class Offer
{
public $id;
public $name;
public $url;
public $price;
public $oldprice;
public $currencyId;
public $categoryId;
public $picture;
public $params = [];
// Добавить свободные параметры в товары
public function addParam($name, $value)
{
if (!empty(trim($value))) {
$this->params[] = [
'name' => trim($name),
'value' => trim($value),
];
}
}
// Вывести товар в XML формате
public function toXml()
{
$xml = "<offer id=\"\">" . PHP_EOL;
$xml .= "<name>" . lh_esc_xml($this->name) . "</name>" . PHP_EOL;
$xml .= "<url>" . lh_esc_xml($this->url) . "</url>" . PHP_EOL;
$xml .= "<price>" . lh_esc_xml($this->price) . "</price>" . PHP_EOL;
if (!empty($this->oldprice)) {
$xml .= "<oldprice>" . lh_esc_xml($this->oldprice) . "</oldprice>" . PHP_EOL;
}
$xml .= "<currencyId>" . lh_esc_xml($this->currencyId) . "</currencyId>" . PHP_EOL;
$xml .= "<categoryId></categoryId>" . PHP_EOL;
if (!empty($this->picture)) {
$xml .= "<picture>" . lh_esc_xml($this->picture) . "</picture>" . PHP_EOL;
}
foreach ($this->params as $param) {
$xml .= "<param name=\"" . lh_esc_xml($param['name']) . "\">" . lh_esc_xml($param['value']) . "</param>" . PHP_EOL;
}
$xml .= "</offer>" . PHP_EOL;
return $xml;
}
}
// Массив со всеми товарами для вывода
$offers = [];
// Начало вывода самого файла
?>
<yml_catalog date="<?= date('Y-m-d H:i') ?>">
<shop>
<name>Название магазина</name>
<company>Название компании</company>
<url><?= lh_esc_xml(get_site_url()) ?></url>
<currencies>
<currency id="RUB" rate="1"/>
<currency id="USD" rate="2"/>
</currencies>
// Список всех категорий
<categories>
<?php
$categories = get_terms(['taxonomy' => 'category_catalog', 'hide_empty' => false]);
foreach ($categories as $category) {
echo '<category id="' . $category->term_id . '">' . lh_esc_xml($category->name) . '</category>' . PHP_EOL;
}
?>
</categories>
// Список товаров
<offers>
<?php
$args = [
'post_type' => 'catalog',
'posts_per_page' => -1,
'post_status' => 'publish'
];
$query = new WP_Query($args);
while ($query->have_posts()) {
$query->the_post();
$id = get_the_ID();
$title = get_the_title();
$link = get_permalink();
$category_id = wp_get_post_terms($id, 'category_catalog', ['fields' => 'ids'])[0] ?? 0;
$image = get_the_post_thumbnail_url($id, 'full');
$product_info = get_field('product_main_info', $id);
$model = $product_info['model'] ?? '';
$brand = $product_info['brand'] ?? '';
$main_price = lh_format_price($product_info['main_price'] ?? '');
$currencyIdM = lh_detect_currency($product_info['main_price'] ?? '');
$sale_price = lh_format_price($product_info['sale_price'] ?? '');
// Если у базового товара есть цена, то добавить в список товаров для вывода
if (!empty($main_price)) {
$offer = new Offer();
$offer->id = $id;
$offer->name = $title;
$offer->url = $link;
$offer->price = $main_price;
$offer->oldprice = (!empty($sale_price)) ? $sale_price : $main_price;
$offer->currencyId = $currencyIdM;
$offer->categoryId = $category_id;
$offer->picture = $image;
$offer->addParam('Модель', $model);
$offer->addParam('Производитель', $brand);
$offers[] = $offer;
}
// Создать товары с модификаторами из "custom_parametrs"
$variations = get_field('custom_parametrs', $id);
if (!empty($variations) && is_array($variations)) {
foreach ($variations as $index => $item) {
$param_name = $item['nazvanie'] ?? '';
if ($param_name == '') {
$param_name = 'Размер';
}
$value = $item['znachenie'] ?? '';
$price = lh_format_price($item['czena'] ?? '');
$currencyId = lh_detect_currency($item['czena'] ?? '');
if (empty($param_name) || empty($value) || empty($price)) {
continue;
}
$offer = new Offer();
$offer->id = $id . '-' . ($index + 1);
$offer->name = "$title ($value)";
$offer->url = $link;
$offer->price = $price;
$offer->oldprice = (!empty($sale_price)) ? $main_price : '';
$offer->currencyId = $currencyId;
$offer->categoryId = $category_id;
$offer->picture = $image;
$offer->addParam('Модель', $model);
$offer->addParam('Производитель', $brand);
$offer->addParam($param_name, $value);
$offers[] = $offer;
}
}
// Создать товары с модификаторами из "configurator"
$configurator = get_field('configurator', $id);
if (!empty($configurator) && is_array($configurator) && !empty($configurator['configurator'])) {
foreach ($configurator['configurator'] as $index => $config) {
$power = $config['power'] ?? '';
$price = lh_format_price($config['price'] ?? '');
$currencyId = lh_detect_currency($config['price'] ?? '');
if (empty($power) || empty($price)) {
continue;
}
$offer = new Offer();
$offer->id = $id . '-cfg-' . ($index + 1);
$offer->name = "$title (Мощность: $power)";
$offer->url = $link;
$offer->price = $price;
$offer->currencyId = $currencyId;
$offer->categoryId = $category_id;
$offer->picture = $image;
$offer->addParam('Модель', $model);
$offer->addParam('Производитель', $brand);
$offer->addParam('Мощность', $power);
$offers[] = $offer;
}
}
}
wp_reset_postdata();
// Вывести товары
foreach ($offers as $offer) {
echo $offer->toXml();
}
?>
</offers>
</shop>
</yml_catalog>
<?php
// Функция экранирования XML
function lh_esc_xml($string) {
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
// Функция очистки цены от "USD"
function lh_format_price($price) {
return preg_replace('/[^0-9,.]/', '', $price);
}
// Определение валюты по цене
function lh_detect_currency($price) {
return (stripos($price, 'USD') !== false) ? 'USD' : 'RUB';
}
?>
В этом посте рассмотрено создание YML-фида для Яндекс.Маркета на WordPress, используя плагин ACF для хранения параметров товаров. Такой метод позволяет гибко настраивать товарные позиции и легко адаптировать фид под требования маркетплейса. Это удобное решение для автоматического обновления товарного каталога и интеграции с Яндекс.Маркетом.