Получение полного развернутого списка категорий из YML-файла в TXT на PHP
YML (полная расшифровка - Yandex Market Language) – стандарт разметки товарных предложений, который разработан компанией «Яндекс» и используется ею для корректного импорта данных в свои сервисы, такие как «Яндекс.Недвижимость», «Яндекс.Маркет» и другие.
YML-прайсы пользуются весьма большой популярностью у крупных интернет-магазинов. Так и я столкнулся с одной задачей, целью которой было преобразование такого YML-файла в свой, необходимый для дальнейшего импорта на сайт клиента.
Поскольку вы читаете эту статью, я смею предположить, что вы представляете себе, каким образом строится структура прайса с такой разметкой, а именно – блок его категорий. Приведу простой пример:
<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>
Что мы тут видим?
«parentId» указывает нам на предыдущую родительскую категорию (при отсутствии этого атрибута считается, что категория является корневой (главной)), «id», соответственно, уникальный ID той или иной категории.
По логике вещей (на основе вышеописанного примера) можно сделать вывод, что вложенность, например, категории «Зеркальные фотоаппараты» будет такая:
Электроника > Фото > Зеркальные фотоаппараты
Вроде все понятно, но что если таковых категорий 100 или 500? Ручное составление всех цепочек (полных наборов категорий) может занять очень долгое время, и вы рискуете пропустить ту или иную категорию за счет человеческого фактора (простой невнимательности).
Чтобы решить этот вопрос, я написал небольшой PHP-скрипт, который в автоматическом режиме находит и собирает все возможные (правильные по вложенности и содержанию) цепочки категорий из YML-файла и сохраняет их в текстовом файле.
Выглядит скрипт так:
<?php
header("Content-Type: text/html; charset=utf-8");
$url_file = simplexml_load_file("./shop-price.xml"); // Путь до обрабатываемого файла
$name_txt_file = "category_list.txt"; // Название текстового файла для сохранения результата
$separator = " > "; // Разделитель категорий
$category_list = []; // Массив всех категорий в виде array(id => "", parent_id => "", name => "")
$category_list_id = []; // Массив ID категорий
$tree["0"] = ["id" => "", "parent_id" => "", "name" => ""]; // Начало дерева вложенных категорий
@unlink($name_txt_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];
}
}
// Переобходим категории, составляем их цепочки и сохраняем результат
foreach($category_list_id as $key) {
$parents = [];
get_parents($tree[0], $key, $parents);
$parents = array_reverse($parents, true);
file_put_contents($name_txt_file, implode($separator, $parents)."\r\n", FILE_APPEND);
}
?>
На что здесь стоит обратить внимание?
$url_file – ссылка на файл прайса в любом формате (у меня это был формат XML), $name_txt_file – название текстового файла, в котором будут сохранены все категории (сам выходной файл при этом будет сохранен в том месте, где находится PHP-скрипт), $separator – разделитель категорий.
По ресурсоемкости скрипт должен отработать на любом хостинге. Я же свои 1 000+ категорий (с итоговыми комбинациями в 1 430+ штук) успешно обработал на хостинге Beget.
Кстати говоря, описанный в самом начале статьи блок категорий на выходе после обработки данным скриптом получает следующий набор категорий:
Электроника
Электроника > Фото
Электроника > Фото > Компактные фотоаппараты
Электроника > Фото > Зеркальные фотоаппараты
Электроника > Видеокамеры
Хранение данных
Хранение данных > Карты памяти
Хранение данных > USB-flash накопители
Хранение данных > Переносные жесткие диски
Как-то так. В следующей статье читайте о том, как не просто сохранить эти категории в файл, а прописать их в каждом товарном предложении.