Понадобилось на одном из проектов рекурсивно вывести древовидную структуру разделов каталога, а также все вложенные элементы в 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 недоступен для записи"; }
Хостинг - FastVPS