Рекурсивный алгоритм обхода дерева

Рекурсивный алгоритм обхода дерева

687
12.10.2014

Мой ремейк очень полезного алгоритма прохода по дереву каталога от Юлии Бедросовой

Небольшой ремейк для себя  очень полезного алгоритма

CModule::IncludeModule('iblock');
$map=array();
$arParams["IBLOCK_MAP_ID"] = XX;  // - каким-то образом задается параметр с ID инфоблока
$sort_by = 'name';  // поле, по которому нужно произвести сортировку разделов
$sort_order = 'asc';  // направление сортировки

if(isset($arParams["IBLOCK_MAP_ID"])){
   $arFilter = Array(
        'IBLOCK_ID'=>$arParams["IBLOCK_MAP_ID"],
        'GLOBAL_ACTIVE'=>'Y'
   );
   $db_list = CIBlockSection::GetList(Array($sort_by=>$sort_order), $arFilter, true);
   while($ar_result = $db_list->GetNext()) {
        $map[''.$ar_result['ID']]=$ar_result;
   }
}

Таким образом, в переменной $map получаем полный список разделов, отсортированных по нужному полю и в нужном направлении. 

Дальше - строим дерево из разделов: 

$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]['SECTION_PAGE_URL'] = $map[$parent_id]['SECTION_PAGE_URL'];
            $map_sec[$parent_id]['UF_SOMECODE'] = $map[$parent_id]['UF_SOMECODE'];

            $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'],
            "SECTION_PAGE_URL" => $val['SECTION_PAGE_URL'],
            "UF_SOMECODE" => $val['UF_SOMECODE'],
            "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'],
               "SECTION_PAGE_URL" => $val['SECTION_PAGE_URL'],
               "UF_SOMECODE" => $val['UF_SOMECODE'],
               "IBLOCK_SECTION_ID" => $val['IBLOCK_SECTION_ID'],
            );
         }
      }
   }

Таким образом, в переменной $map_sec хранится дерево разделов. В отличие от гениально простого алгоритма, предложенного Юлией по ссылке в начале статьи - данный алгоритм также учитывает и те разделы, который не имеют подразделов, а являются листьями на первом же уровне вложенности. 

Ну и напоследок, алгоритм вывода дерева, начиная с конкретного узла, или всего дерева целиком: 

function ShowElementsTree($category_arr, $parent_id)
    {
        if (intval($parent_id) > 0) {
            if (isset($category_arr[$parent_id])) {

                foreach ($category_arr[$parent_id]['CHILDS'] as $value) { // Обходим
                    echo "<div style=\"margin-left:" . ($value['DEPTH_LEVEL'] * 25) . "px;\">" . $value['ID'] . '. ' . $value['NAME'] . ' (' . $value['ELEMENT_CNT'] . ')' . "</div>";
                    echo "<div style=\"margin-left:" . ($value['DEPTH_LEVEL'] * 35) . "px;\">";
                        // тут выводим какие-то дополнительные данные о разделе
                    echo "</div>";

                    if (count($category_arr[$value["ID"]]['CHILDS']) > 0) {
                        //Рекурсивно вызываем эту же функцию, но с новым $parent_id
                        self::ShowElementsTree($category_arr, $value["ID"]);
                    } else {
                        //Выводим листик
                    }
                }
            }
        } else {
            foreach ($category_arr as $parent_id => $arItems) {
                if (isset($category_arr[$parent_id])) {
                    $value = $category_arr[$parent_id];
                    echo "<div>" . $value['ID'] . '. ' . $value['NAME'] . ' (' . $value['ELEMENT_CNT'] . ')' . "</div>";

                    foreach ($category_arr[$parent_id]['CHILDS'] as $value) { //Обходим
                        echo "<div style=\"margin-left:" . ($value['DEPTH_LEVEL'] * 25) . "px;\">" . $value['ID'] . '. ' . $value['NAME'] . ' (' . $value['ELEMENT_CNT'] . ')' . "</div>";
                        echo "<div style=\"margin-left:" . ($value['DEPTH_LEVEL'] * 35) . "px;\">";
                            // тут выводим какие-то дополнительные данные о разделе
                        echo "</div>";

                        if (count($category_arr[$value["ID"]]['CHILDS']) > 0) {
                            // Рекурсивно вызываем эту же функцию, но с новым $parent_id
                            self::ShowElementsTree($category_arr, $value["ID"]);
                        } else {
                            //Выводим листик
                        }
                    }
                }
            }
        }
    }

Таким образом, если нужно вывести все дерево, вызываем функцию: 

ShowElementsTree($map_sec,''); 

а для того, чтобы вывести ветку только для одного раздела, вторым параметром передаем этот раздел. 

Надеюсь, кому-то кроме меня этот алгоритм будет полезен ... 

Всем легкого кода!

UPDATE 2015-09-29: Понадобилось тут мне дерево разделов в формате массива, а не в формате echo-публикаций. Вот родилась новая функция:

function GetSectionsTreeArray(&$category_arr, $parent_id){
        $arResult = array();
        if (intval($parent_id) > 0) {
            if (isset($category_arr[$parent_id])) {
                foreach ($category_arr[$parent_id]['CHILDS'] as $value) {
                    if (count($category_arr[$value["ID"]]['CHILDS']) > 0) {
                        //Рекурсивно вызываем эту же функцию, но с новым $parent_id
                        $value['CHILDS'] = self::GetSectionsMenu($category_arr, $value["ID"]);
                    }
                    $arResult[$value["ID"]] = $value;
                }
            }
        } else {
            foreach ($category_arr as $parent_id => $arItems) {
                if (isset($category_arr[$parent_id])) {
                    $value = $category_arr[$parent_id];
                    if(intval($value['DEPTH_LEVEL']) > 1) continue;
                    foreach ($category_arr[$parent_id]['CHILDS'] as $value2) {
                        if (count($category_arr[$value2["ID"]]['CHILDS']) > 0) {
                            $value2['CHILDS'] = self::GetSectionsMenu($category_arr, $value2["ID"]);
                        }
                        $value['CHILDS'][$value2["ID"]] = $value2;
                    }
                    $arResult[$value["ID"]] = $value;
                }
            }
        }
        return $arResult;
    }

Источник: http://dev.1c-bitrix.ru/community/webdev/user/60622/blog/11819/


Благодарю за внимание! Делитесь вашими замечаниями в комментариях ниже.


P.S. Обращайтесь ко мне за приобретением лицензий и продлений на 1C-Битрикс "Управление сайтом", лицензий на облачную и коробочную версии Битрикс 24 а также за приобретением и внедрением готовых решений на базе 1С-Битрикс от партнеров. За более подробной информацией свяжитесь со мной любым удобным для вас способом


Комментарии

Еще никто не комментировал данную публикацию. Будьте первыми!

Добавить комментарий

captcha

Возврат к списку