Что такое список разделов в информационном блоке, это данные структурированные в виде дерева.
Есть несколько вариантов обхода дерева, вот что говорит wikipedia
Пошаговый перебор элементов дерева по связям между узлами-предками и узлами-потомками называется обходом дерева. Зачастую, операция может быть выполнена переходом указателя по отдельным узлам. Обход, при котором каждый узел-предок просматривается прежде его потомков называется предупорядоченным обходом или обходом в прямом порядке (pre-order walk), а когда просматриваются сначала потомки, а потом предки, то обход называется поступорядоченным обходом или обходом в обратном порядке (post-order walk). Существует также симметричный обход, при котором посещается сначала левое поддерево, затем узел, затем — правое поддерево, и обход в ширину, при котором узлы посещаются уровень за уровнем (N-й уровень дерева — множество узлов с высотой N). Каждый уровень обходится слева направо.
По умолчанию используется, обход в ширину.
$rs_Section = CIBlockSection::GetList(array('left_margin' => 'asc'), array('IBLOCK_ID' => 8)); while ( $ar_Section = $rs_Section->Fetch() ) { $ar_Result[] = array( 'ID' => $ar_Section['ID'], 'NAME' => $ar_Section['NAME'], 'IBLOCK_SECTION_ID' => $ar_Section['IBLOCK_SECTION_ID'], 'IBLOCK_SECTION_ID' => $ar_Section['IBLOCK_SECTION_ID'], 'LEFT_MARGIN' => $ar_Section['LEFT_MARGIN'], 'RIGHT_MARGIN' => $ar_Section['RIGHT_MARGIN'], 'DEPTH_LEVEL' => $ar_Section['DEPTH_LEVEL'], ); }
Возвращает массив вида
Array ( [0] => Array ( [ID] => 5335 [NAME] => USB провода [IBLOCK_SECTION_ID] => [LEFT_MARGIN] => 1 [RIGHT_MARGIN] => 2 [DEPTH_LEVEL] => 1 ) [1] => Array ( [ID] => 5171 [NAME] => Аккумуляторы по моделям [IBLOCK_SECTION_ID] => [LEFT_MARGIN] => 3 [RIGHT_MARGIN] => 48 [DEPTH_LEVEL] => 1 ) [2] => Array ( [ID] => 5172 [NAME] => Для Acer [IBLOCK_SECTION_ID] => 5171 [LEFT_MARGIN] => 4 [RIGHT_MARGIN] => 5 [DEPTH_LEVEL] => 2 ) [3] => Array ( [ID] => 5173 [NAME] => Для Advent [IBLOCK_SECTION_ID] => 5171 [LEFT_MARGIN] => 6 [RIGHT_MARGIN] => 7 [DEPTH_LEVEL] => 2 ) [4] => Array ( [ID] => 5174 [NAME] => Для Apple [IBLOCK_SECTION_ID] => 5171 [LEFT_MARGIN] => 8 [RIGHT_MARGIN] => 9 [DEPTH_LEVEL] => 2 ) ... )
Видно что у раздела Аккумуляторы по моделям есть подразделы и они находятся сразу под своим родителем, в этом и заключается особенность такой выборки, мы получаем ассоциативный массив с отсортированный структурой дерева. Для вывода на сайте это вполне удобно, в основном это применяется в выводе в тэге <select> для фильтра по категории или просто ссылками
Код выводящий select
<select> <option> - Выбрать - </option> <? foreach( $ar_Result as $ar_Value ) { $s = ''; for($i = 1; $i <= $ar_Value['DEPTH_LEVEL']; $i++ ) $s .= '.'; $s .= ' '.$ar_Value['NAME']; ?><option value="<?=$ar_Value['ID']?>"><?=$s?></option><? } ?> </select>
Мне всегда было удобно работать с массивами, и я хотел иметь список разделов с подразделами в виде массива, возможно в большинстве случаев это и не нужно, и в случае с хранением данных в массиве при выводе нужна рекурсия, я все же попробовал собрать дерево в массив.
$rs_Section = CIBlockSection::GetList( array('DEPTH_LEVEL' => 'desc'), $ar_Filter, false, array('ID', 'NAME', 'IBLOCK_SECTION_ID', 'DEPTH_LEVEL', 'SORT') ); $ar_SectionList = array(); $ar_DepthLavel = array(); while($ar_Section = $rs_Section->GetNext(true, false)) { $ar_SectionList[$ar_Section['ID']] = $ar_Section; $ar_DepthLavel[] = $ar_Section['DEPTH_LEVEL']; } $ar_DepthLavelResult = array_unique($ar_DepthLavel); rsort($ar_DepthLavelResult); $i_MaxDepthLevel = $ar_DepthLavelResult[0]; for( $i = $i_MaxDepthLevel; $i > 1; $i-- ) { foreach ( $ar_SectionList as $i_SectionID => $ar_Value ) { if( $ar_Value['DEPTH_LEVEL'] == $i ) { $ar_SectionList[$ar_Value['IBLOCK_SECTION_ID']]['SUB_SECTION'][] = $ar_Value; unset( $ar_SectionList[$i_SectionID] ); } } } function __sectionSort($a, $b) { if ($a['SORT'] == $b['SORT']) { return 0; } return ($a['SORT'] < $b['SORT']) ? -1 : 1; } usort($ar_SectionList, "__sectionSort");
Использовать будем поступорядоченный метод обхода, по этому сначала найдем максимальный DEPTH_LEVEL, получаем его в переменную $i_MaxDepthLevel и начинаем в цикле собирать массив с самих глубоких по иерархии разделов. Раздел после добавления в массив родитель удаляем unset( $ar_SectionList[$i_SectionID] );
Важный момент, сортировка списка должно идти именно в таком направлении array(‘DEPTH_LEVEL’ => ‘desc’) при этом подготовленный массив разделов перед сборкой будет иметь вид
[5097] => Array ( [ID] => 5097 [NAME] => LENOVO [IBLOCK_SECTION_ID] => 5087 [DEPTH_LEVEL] => 4 [SORT] => 400 ) [5100] => Array ( [ID] => 5100 [NAME] => SONY [IBLOCK_SECTION_ID] => 5087 [DEPTH_LEVEL] => 4 [SORT] => 430 ) [5104] => Array ( [ID] => 5104 [NAME] => ACER [IBLOCK_SECTION_ID] => 5103 [DEPTH_LEVEL] => 4 [SORT] => 640 ) [5105] => Array ( [ID] => 5105 [NAME] => ASUS [IBLOCK_SECTION_ID] => 5103 [DEPTH_LEVEL] => 4 [SORT] => 650 )
т.е мы начинаем перебор с разделов с наибольшим DEPTH_LEVEL в результате получаем дерево в виде заветного массива, кусок дампа
[6] => Array ( [ID] => 5086 [NAME] => Кабели [IBLOCK_SECTION_ID] => 5425 [DEPTH_LEVEL] => 2 [SORT] => 290 [SUB_SECTION] => Array ( [0] => Array ( [ID] => 5087 [NAME] => Шлейфы для матриц [IBLOCK_SECTION_ID] => 5086 [DEPTH_LEVEL] => 3 [SORT] => 300 [SUB_SECTION] => Array ( [0] => Array ( [ID] => 5097 [NAME] => LENOVO [IBLOCK_SECTION_ID] => 5087 [DEPTH_LEVEL] => 4 [SORT] => 400 ) ...
Как видим удалось построить массив, но как теперь его выводить и вообще работать с ним? Во первых мы можем обращаться к определенным разделам по ключам, выдергивать части массива и тд, все что можно сделать с массивом данных.
Для вывода всего массива необходимо использовать рекурсию, думаю тема актуально для построения меню для сайта
function __recursivRenderMenu($ar_Items) { foreach ($ar_Items as $ar_Value) { if( count($ar_Value['SUB_SECTION']) > 0 ) { echo '<ul>'; echo '<li>'; echo $ar_Value['NAME']; echo '<ul>'; # рекурсивный вызов функции echo __recursivRenderMenu($ar_Value['SUB_SECTION']); echo '</ul>'; echo '</li>'; echo '</ul>'; } else { echo '<li>'.$ar_Value['NAME'].'</li>'; } } } echo __recursivRenderMenu($ar_SectionList);
На выходе получим
Если есть способы сделать тоже самое легче, быстрее, правильнее и тд, прошу написать в комментарии :)
Комментарий от Олега Щукина
Ошибки в статье:
— в CIBlockSection::GetList() по умолчанию обход в ширину, а не симметричный. Это можно увидеть, если убрать ‘left_margin’ => ‘asc’ (пустой массив — значит без сортировки).
— лишняя пара тегов
вокруг echo __recursivRenderMenu().
— usort() портит ключи, но вас спасает то, что usort() еще и не проходит массив рекурсивно ;)
Здесь вам нужно рекурсивно вызывать uasort():
[code]function uasort_recursive (&$array, $cmp_function)
{
foreach ($array as $k => &$v)
if (is_array($v))
if (!uasort_recursive($v, $cmp_function))
return false;
return uasort($array, $cmp_function);
}
uasort_recursive($ar_SectionList, «__sectionSort»);[/code]
А еще, можно добавить ссылку на варианты обхода дерева в наглядных картинках, которые есть на английской странице Википедии.
Тема нормальная, но не понятно мне , если можно простым языком, как просто разделы в select собрать. У меня получается только св-во элементов вытянуть в select и все.
$ob_Section = new CIBlockSection();
$rs_Section = $ob_Section->GetList(array(‘left_margin’ => ‘asc’), array(‘IBLOCK_ID’ => $ob_Bookmarks->getIblockID()), false);
while($ar_Section = $rs_Section->GetNext(true, false))
{
$arResult[‘SECTION_LIST’] = $ar_Section;
}
— Поместить в папку —
<?
foreach( $arResult['SECTION_LIST'] as $ar_Value )
{
$s = '';
for($i = 1; $i
<option value="»>
http://pastebin.com/NUHpcmED
А вот немного покороче код…
$arStructured = array();
$sectionLink = array();
$arStructured[‘ROOT’] = array();
$sectionLink[0] = &$arStructured[‘ROOT’];
foreach ($arResult[‘SECTIONS’] as $arSection) {
$sectionLink[intval($arSection[‘IBLOCK_SECTION_ID’])][‘CHILD’][$arSection[‘ID’]] = $arSection;
$sectionLink[$arSection[‘ID’]] = &$sectionLink[intval($arSection[‘IBLOCK_SECTION_ID’])][‘CHILD’][$arSection[‘ID’]];
}
unset($sectionLink);
$arResult[‘_STRUCTURED_SECTIONS’] = $arStructured;
Это пример для result_modifier.php компонента «Структура разделов»
А есть у кого-нибудь готовые решения для компонента bitrix:menu с реализованным последним сценарием для работы с массивом?
Спасибо вам большое, вы гений :)