Понадобилось на одном из проектов рекурсивно вывести древовидную структуру разделов каталога, а также все вложенные элементы в csv файл.
Заказчик просил именно древовидную структуру и именно Excel. Поэтому вложенные разделы находятся на следующей от родительской колонки, также как и вложенные элементы
Наглядность немного страдает, когда есть вложенные разделы и товары одновременно, но заказчкика это устроило. Также, чтобы их различать у товаров вначале я добавил символ "-"
Вообще вывод можно допилить как угодно, главное иметь основную подготовку разделов и товаров к выводу.
Я использовал наработки Бедросовой Юлии и Поповича Алексея, немного адаптировав их под свои нужды получился такой скрипт:
global $arSectElements, $maxDepth;
$maxDepth = 0; // максимальная глубина каталога
$map = array();
$arParams["IBLOCK_MAP_ID"] = 1; // - ID инфоблока
// собираем товары, ключ - айдишник раздела
$resEl = CIBlockElement::GetList(array(),array("IBLOCK_ID"=>37, "ACTIVE"=>"Y"),false,false,array("ID","NAME","IBLOCK_SECTION_ID"));
while($ob = $resEl->Fetch())
{
$ob["NAME"] = str_replace(";","",$ob["NAME"]); // у меня были в некоторых товарах ; поэтому пришлось их вырезать
$arSectElements[$ob["IBLOCK_SECTION_ID"]][] = $ob;
}
// получаем все разделы и смотрим максимальный уровень вложенности
if(isset($arParams["IBLOCK_MAP_ID"])){
$arFilter = Array(
'IBLOCK_ID'=>$arParams["IBLOCK_MAP_ID"],
'GLOBAL_ACTIVE'=>'Y'
);
$db_list = CIBlockSection::GetList(Array("left_margin"=>"asc"), $arFilter, true);
while($ar_result = $db_list->GetNext()) {
$map[$ar_result['ID']]=$ar_result;
if($maxDepth < $ar_result["DEPTH_LEVEL"])
$maxDepth = $ar_result["DEPTH_LEVEL"];
}
}
$maxDepth += 1; // добавляем уровень вложенности т.к. еще товары выводятся
// Подготавливаем массив для удобной рекурсии
$map_sec = array();
foreach ($map as $key => $val) {
if ($val['IBLOCK_SECTION_ID'] > 0) {
$parent_id = $map[$val['IBLOCK_SECTION_ID']]['ID'];
if(!isset($map_sec[$parent_id])){
$map_sec[$parent_id]['ID'] = $map[$parent_id]['ID'];
$map_sec[$parent_id]['CODE'] = $map[$parent_id]['CODE'];
$map_sec[$parent_id]['NAME'] = $map[$parent_id]['NAME'];
$map_sec[$parent_id]['DEPTH_LEVEL'] = $map[$parent_id]['DEPTH_LEVEL'];
$map_sec[$parent_id]['IBLOCK_SECTION_ID'] = $map[$parent_id]['IBLOCK_SECTION_ID'];
}
$map_sec[$parent_id]['CHILDS'][$val['ID']] = array(
"ID" => $val['ID'],
"CODE" => $val['CODE'],
"NAME" => $val['NAME'],
"DEPTH_LEVEL" => $val['DEPTH_LEVEL'],
"IBLOCK_SECTION_ID" => $val['IBLOCK_SECTION_ID'],
);
} else {
if(!isset($map_sec[$val['ID']])){
$map_sec[$val['ID']] = array(
"ID" => $val['ID'],
"CODE" => $val['CODE'],
"NAME" => $val['NAME'],
"DEPTH_LEVEL" => $val['DEPTH_LEVEL'],
"IBLOCK_SECTION_ID" => $val['IBLOCK_SECTION_ID'],
);
}
}
}
// Сама рекурсивная функция вывода
function ShowElementsTree($category_arr, $parent_id, $handle)
{
global $arSectElements, $maxDepth; // чтобы были тут видны (можно через параметры функции передавать)
if (intval($parent_id) > 0)
{
if (isset($category_arr[$parent_id]))
{
foreach ($category_arr[$parent_id]['CHILDS'] as $value) // Обходим
{
fwrite($handle, str_repeat(";", $value['DEPTH_LEVEL'] - 1) . $value['NAME'] . str_repeat(";", $maxDepth - $value['DEPTH_LEVEL'])."\r\n");
if (count($category_arr[$value["ID"]]['CHILDS']) > 0)
{
//Рекурсивно вызываем эту же функцию, но с новым $parent_id
ShowElementsTree($category_arr, $value["ID"], $handle);
}
else {}
// товары
if(!empty($arSectElements[$value["ID"]]))
{
foreach($arSectElements[$value["ID"]] as $elem)
{
fwrite($handle, str_repeat(";", $value['DEPTH_LEVEL']) . " - ".$elem['NAME'] . str_repeat(";", ($maxDepth-$value['DEPTH_LEVEL'] + 1))."\r\n");
}
}
}
}
}
else
{
foreach ($category_arr as $parent_id => $arItems)
{
if (isset($category_arr[$parent_id]))
{
$value = $category_arr[$parent_id];
fwrite($handle, $value['NAME'] . str_repeat(";", ($maxDepth-$value['DEPTH_LEVEL']))."\r\n");
foreach ($category_arr[$parent_id]['CHILDS'] as $value) //Обходим
{
fwrite($handle, str_repeat(";", $value['DEPTH_LEVEL'] - 1) . $value['NAME'] . str_repeat(";", ($maxDepth-$value['DEPTH_LEVEL']))."\r\n");
if (count($category_arr[$value["ID"]]['CHILDS']) > 0) {
// Рекурсивно вызываем эту же функцию, но с новым $parent_id
ShowElementsTree($category_arr, $value["ID"], $handle);
}
else {}
// товары
if(!empty($arSectElements[$value["ID"]]))
{
foreach($arSectElements[$value["ID"]] as $elem)
{
fwrite($handle, str_repeat(";", $value['DEPTH_LEVEL'] ) . " - ".$elem['NAME'] . str_repeat(";", ($maxDepth-$value['DEPTH_LEVEL'] + 1))."\r\n");
}
}
}
// товары
if(!empty($arSectElements[$value["ID"]]))
{
foreach($arSectElements[$value["ID"]] as $elem)
{
fwrite($handle, str_repeat(";", $value['DEPTH_LEVEL'] ). " - ".$elem['NAME'] . str_repeat(";", ($maxDepth-$value['DEPTH_LEVEL']+1))."\r\n");
}
}
}
}
}
return $csv_str;
}
$filename = '/home/bitrix/ПУТЬ_ДО_ФАЙЛА/pnx.csv';
// Вначале давайте убедимся, что файл существует и доступен для записи.
if (is_writable($filename)) {
if (!$handle = fopen($filename, 'w')) {
echo "Не могу открыть файл ($filename)";
exit;
}
$csv = ShowElementsTree($map_sec,false, $handle);
// Записываем $somecontent в наш открытый файл.
if (fwrite($handle, $somecontent) === FALSE) {
echo "Не могу произвести запись в файл ($filename)";
exit;
}
fclose($handle);
} else {
echo "Файл $filename недоступен для записи";
}