Замена ID категории в товарах YML-прайса ее названием + добавление цепочки родителей
В одной из предыдущих статей я подробно рассказал о том, каким образом можно получить полный список всех цепочек категорий из YML-файла. Сегодня же, на основе всей конструкции, описанной в той статье, мы решим немного другую задачу, а именно – заменим ID категории в товаре полной соответствующей цепочкой категорий.
Для начала посмотрим, как выглядит простой пример прайса с YML-разметкой:
<?xml version="1.0" encoding="UTF-8"?>
<yml_catalog date="2019-06-17 04:35">
<shop>
<name>Canon</name>
<company>Canon</company>
<url>http://canon.ru</url>
<currencies>
<currency id="RUR" rate="1" />
</currencies>
<categories>
<category id="1">Электроника</category>
<category id="10" parentId="1">Фото</category>
<category id="100" parentId="10">Компактные фотоаппараты</category>
<category id="101" parentId="10">Зеркальные фотоаппараты</category>
<category id="102" parentId="1">Видеокамеры</category>
<category id="2">Хранение данных</category>
<category id="11" parentId="2">Карты памяти</category>
<category id="12" parentId="2">USB-flash накопители</category>
<category id="13" parentId="2">Переносные жесткие диски</category>
</categories>
<offers>
...
<offer id="1" available="true">
<url>http://canon.ru/eos-60d</url>
<price>55000</price>
<currencyId>RUR</currencyId>
<categoryId>101</categoryId>
<delivery>true</delivery>
<name>EOS 60D Body</name>
<vendor>Canon</vendor>
<vendorCode>4460B007</vendorCode>
<description>Зеркальная фотокамера любительского уровня.</description>
<param name="Вес" unit="г">750</param>
<param name="Байонет">Canon EF/EF-S</param>
<param name="Тип матрицы">CMOS</param>
</offer>
...
</offers>
</shop>
</yml_catalog>
Что мы здесь видим? Знакомый из первой статьи блок категорий:
<categories>
<category id="1">Электроника</category>
<category id="10" parentId="1">Фото</category>
<category id="100" parentId="10">Компактные фотоаппараты</category>
<category id="101" parentId="10">Зеркальные фотоаппараты</category>
<category id="102" parentId="1">Видеокамеры</category>
<category id="2">Хранение данных</category>
<category id="11" parentId="2">Карты памяти</category>
<category id="12" parentId="2">USB-flash накопители</category>
<category id="13" parentId="2">Переносные жесткие диски</category>
</categories>
И, соответственно, перечисление товаров:
<offer id="1" available="true">
<url>http://canon.ru/eos-60d</url>
<price>55000</price>
<currencyId>RUR</currencyId>
<categoryId>101</categoryId>
<delivery>true</delivery>
<name>EOS 60D Body</name>
<vendor>Canon</vendor>
<vendorCode>4460B007</vendorCode>
<description>Зеркальная фотокамера любительского уровня.</description>
<param name="Вес" unit="г">750</param>
<param name="Байонет">Canon EF/EF-S</param>
<param name="Тип матрицы">CMOS</param>
</offer>
В нашем случае – один товар. Как можно заметить из разметки выше, категория товара указывается в YML в виде ее ID:
<categoryId>101</categoryId>
«Яндекс.Маркет», например, такой вид разметки распознает без проблем, но другой импортер может неправильно прочесть данные, поэтому наша задача сегодня – это замена данного ID соответствующей цепочкой категорий с нужным нам разделителем и сохранение всего прайса в новом файле.
Весь код на примере предложенного выше прайса такой:
<?php
header("Content-Type: text/html; charset=utf-8");
$url_file = simplexml_load_file("./shop-price.xml"); // Путь до обрабатываемого файла
$name_new_file = "my_shop.xml"; // Название файла для сохранения результата
$separator = " > "; // Разделитель категорий
$category_list = []; // Массив всех категорий в виде array(id => "", parent_id => "", name => "")
$category_list_id = []; // Массив ID категорий
$tree["0"] = ["id" => "", "parent_id" => "", "name" => ""]; // Начало дерева вложенных категорий
@unlink($name_new_file);
function get_parents($tree, $search_key, &$parents) { // Функция определения всех потомков где $tree - массив, $search_key - ключ, $parents - цепочка потомков
if(is_array($tree)) {
foreach($tree as $key => $value) {
if($key == $search_key) {
$parents[] = $value["name"];
return true;
} elseif($value && get_parents($value, $search_key, $parents)) {
$parents[] = $value["name"];
return true;
}
}
return false;
}
}
foreach($url_file->shop->categories->category as $category_item) {
if(!isset($category_item["parentId"])) { // Если категория главная - указываем потомка 0
$category_item["parentId"] = 0;
}
$category_list[] = ["id" => (String)$category_item["id"], "parent_id" => (String)$category_item["parentId"], "name" => (String)$category_item[0]];
$category_list_id[] = (String)$category_item["id"];
}
// Делаем ID категории ее ключом, все остальное отправляем в значение
foreach($category_list as $category) {
$tree[$category["id"]] = $category;
unset($tree[$category["id"]]["id"]);
}
// Формируем дерево категорий согласно их иерархии
foreach($tree as $category_id => $category_value) {
if(isset($tree[$category_value["parent_id"]])) {
$tree[$category_value["parent_id"]][$category_id] = &$tree[$category_id];
}
}
// Переобходим товары, меняем их структуру и указываем читаемые категории
$content = <<<HTML
<?xml version="1.0" encoding="utf-8"?>
<data>
<products>
HTML;
file_put_contents($name_new_file, $content."\r\n", FILE_APPEND);
foreach($url_file->shop->offers->offer as $items) {
$parents = [];
get_parents($tree[0], $items->categoryId, $parents);
$parents = array_reverse($parents, true);
$price = $items->price; // Цена
$cat_name = implode($separator, $parents); // Цепочка категорий
$name = $items->name; // Название
$vendor = $items->vendor; // Производитель
$description = $items->description; // Описание
$content = <<<HTML
<product>
<name><![CDATA[{$name}]]></name>
<price>{$price}</price>
<vendor>{$vendor}</vendor>
<description><![CDATA[{$description}]]></description>
<cat_name>{$cat_name}</cat_name>
</product>
HTML;
file_put_contents($name_new_file, $content."\r\n", FILE_APPEND);
}
$content = <<<HTML
</products>
</data>
HTML;
file_put_contents($name_new_file, $content, FILE_APPEND);
?>
На что здесь стоит обратить внимание?
$url_file – ссылка на файл прайса в любом формате (у меня это был формат XML), $name_new_file – название файла, в котором будут сохранены новые данные (сам выходной файл при этом будет сохранен в том месте, где находится PHP-скрипт), $separator – разделитель категорий.
Обратите внимание, что название тегов в исходном YML-прайсе, как и их количество, может (и, вероятнее всего, будет) отличаться от предложенного мною варианта. Изменяйте и адаптируйте код под свою структуру, главное здесь – это показать вам самую идею.
Кстати говоря, итоговый вариант на примере описанного мною прайса будет выглядеть следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<data>
<products>
...
<product>
<name><![CDATA[EOS 60D Body]]></name>
<price>55000</price>
<vendor>Canon</vendor>
<description><![CDATA[Зеркальная фотокамера любительского уровня.]]></description>
<cat_name>Электроника > Фото > Зеркальные фотоаппараты</cat_name>
</product>
...
</products>
</data>
Как вы могли заметить, здесь кардинально поменялась структура прайса и вместо ID добавилась цепочка категорий, к которой и относится товар.
На этом все, что я хотел рассказать вам по поводу преобразования категорий в YML-разметке. Если вы столкнулись с какими-то трудностями в использовании предложенных скриптов – не стесняйтесь и задавайте свои вопросы в комментариях под данной статьей.