Как вывести древовидную структуру разделов каталога с элементами из Битрикса в csv?
Понадобилось на одном из проектов рекурсивно вывести древовидную структуру разделов каталога, а также все вложенные элементы в 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
Источник
CIBlockSection
CIBlockSection — класс для работы с разделами (группами) информационных блоков.
Методы класса
Метод | Описание | С версии |
---|---|---|
GetList | Возвращает список разделов по фильтру. | 3.0.6 |
GetMixedList | Создаёт список разделов и элементов. | 5.1.0 |
GetByID | Возвращает параметры раздела по его коду ID. | 3.0.4 |
Add | Добавляет в информационный блок новый раздел. | 3.0.6 |
Update | Изменяет параметры существующего раздела. | 3.0.6 |
Delete | Удаляет раздел из информационного блока. | 3.0.4 |
GetCount | Возвращает количество подразделов. | 3.0.6 |
GetSectionElementsCount | Возвращает количество элементов в разделе. | 3.2.1 |
GetTreeList | Возвращает разделы отсортированные в порядке «полностью развернутого дерева» | 3.0.4 |
GetNavChain | Возвращает путь от заданного раздела до корневого. | 3.0.4 |
ReSort | Пересчет левой и правой границ. | 3.0.4 |
createMnemonicCode | * Метод создания символьного кода. | 21.300.100 |
generateMnemonicCode | * Метод генерации символьного кода. | 21.300.100 |
isExistsMnemonicCode | * Метод проверки существования символьного кода. | 21.300.100 |
* — Методы работы с символьными кодами. Методы работают, только если в настройках инфоблока включена опция Транслитерировать из названия при добавлении раздела для поля Символьный код раздела.
Параметры транслитерации берутся из настроек инфоблока, но могут быть переопределены в момент использования. Исключение — опция Использовать внешний сервис для перевода. В этом случае методы не работают, возвращают null.
Язык, с которого осуществляется транслитерация, выбирается из настроек сайта, к которому привязан инфоблок. Может быть переопределен в момент использования. Если сайты, к которым привязан инфоблок, имеют различные языки, то язык транслитерации ОБЯЗАТЕЛЬНО необходимо указать при вызове методов.
Источник
Получение иерархии разделов
Приведу небольшой примерчик как одним запросом и одним циклом получить иерархию разделов в виде:
Array ( [ROOT] => Array ( [CHILD] => Array ( [12] => Array ( [ID] => 12 [~ID] => 12 [NAME] => Раздел с ид 12 [~NAME] => Раздел с ид 12 [DEPTH_LEVEL] => 1 [~DEPTH_LEVEL] => 1 [CHILD] => Array ( [63] => Array ( [ID] => 63 [~ID] => 63 . [CHILD] => Array ( . ) ) . [63] => Array ( [ID] => 63 [~ID] => 63 . ) . ) ) . ) ) )
На мой взгляд, с такой структурой работать в большинстве случаев удобнее, чем со списком, отсортированном по LEFT_MARGIN
$arFilter = array( 'ACTIVE' => 'Y', 'IBLOCK_ID' => $arParams['IBLOCK_ID'], 'GLOBAL_ACTIVE'=>'Y', ); $arSelect = array('IBLOCK_ID','ID','NAME','DEPTH_LEVEL','IBLOCK_SECTION_ID'); $arOrder = array('DEPTH_LEVEL'=>'ASC','SORT'=>'ASC'); $rsSections = CIBlockSection::GetList($arOrder, $arFilter, false, $arSelect); $sectionLinc = array(); $arResult['ROOT'] = array(); $sectionLinc[0] = &$arResult['ROOT']; while($arSection = $rsSections->GetNext()) < $sectionLinc[intval($arSection['IBLOCK_SECTION_ID'])]['CHILD'][$arSection['ID']] = $arSection; $sectionLinc[$arSection['ID']] = &$sectionLinc[intval($arSection['IBLOCK_SECTION_ID'])]['CHILD'][$arSection['ID']]; >unset($sectionLinc);
Если мы получаем только активные элементы, то важно указать в фильтре ‘GLOBAL_ACTIVE’=>’Y’, иначе мы можем получить активный элемент с неактивным предком, и его некуда будет определить в иерархию.
В $arSelect нужно не забыть указать IBLOCK_SECTION_ID, иначе иерархию построить не получится
Первое поле в сортировке $arOrder должно быть ‘DEPTH_LEVEL’=>’ASC’, так как иерархия строится от предков к потомкам
Построение иерархии происходит через массив ссылок $sectionLinc
Источник