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

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

1045
12.10.2014
14.08.2016

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

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

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

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