Артём Мáлков

Замена ID категории в товарах YML-прайса ее названием + добавление цепочки родителей

18 июн2 комм

В одной из предыдущих статей я подробно рассказал о том, каким образом можно получить полный список всех цепочек категорий из 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-разметке. Если вы столкнулись с какими-то трудностями в использовании предложенных скриптов – не стесняйтесь и задавайте свои вопросы в комментариях под данной статьей.

Рекомендуем к просмотру
Получение полного развернутого списка категорий из YML-файла в TXT на PHP
Модули и скрипты
Создание и вывод дополнительных параметров в категориях и товарах Webasyst
Статьи и советы
Как настроить вывод нужных категорий в календарь DLE?
Хаки
2
комментария
Форма комментирования этого поста скрыта. Авторизуйтесь, чтобы расширить привилегии гостевого посещения и получить необходимую помощь от сообщества Pandoge.
    • 1
    2
      •  Пользователь
    21 мар в 12:58

    Получилось для нескольких категорий) Это конечно решение на коленке но работает. Прописал для нескольких категорий

    get_parents($tree[0], $items->categoryId[0], $parents1); //потомок 1 категории

    get_parents($tree[0], $items->categoryId[1], $parents2); //потомок 2 категории

    $parents1 = array_reverse($parents1, true); // разворачиваем массив 1

    $parents2 = array_reverse($parents2, true); // разворачиваем массив 2

    $cat_name1 = implode($separator, $parents1); // Цепочка категорий 1

    $cat_name2 = implode($separator, $parents2); // Цепочка категорий 2

    И потом в шаблоне вывода <cat_name>{$cat_name1}|{$cat_name2}</cat_name>

    Получаем <cat_name>Хранение данных > Переносные жесткие диски|Электроника > Фото > Зеркальные фотоаппараты</cat_name>

    • 1
    2
      •  Пользователь
    21 мар в 12:15

    Спасибо! Замечательный скрипт. Иногда товар входит в несколько категорий, подскажите пожалуйста как в таком случае внести изменения чтобы строило две цепочки? Например в товаре указано

    <categoryId>101</categoryId>

    <categoryId>13</categoryId>

    На выходе получали <cat_name>Электроника > Фото > Зеркальные фотоаппараты|Хранение данных > Переносные жесткие диски</cat_name>

    Т.е. ввести дополнительній разделитель цепочек | и перебрать в массиве товара массив categoryId, только знаний в php не хватает как этот перебор прописать в нужную строку.

    я так понимаю внести изменения в get_parents($tree[0], $items->categoryId, $parents);

Подняться наверх
«Pandoge» - помощник вебмастера