Что такое список разделов в информационном блоке, это данные структурированные в виде дерева.

Есть несколько вариантов обхода дерева, вот что говорит 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 с реализованным последним сценарием для работы с массивом?
Спасибо вам большое, вы гений :)