Программный вывод меню в Drupal 7

Периодически возникает необходимость вывода кастомного меню. Многие способы вывода меню, которые я встречал в Интернете основаны на использовании функции theme.

Признаюсь честно, я на данный момент не смог разобраться как с помощью данной функции выводить меню, у которых класс тэга UL меняется в зависимости от уровня вложенности меню, то есть у разных вложенных меню разные стили, а также разные классы у LI в зависимости от требований.

Покажу как это делаю я. Скорее всего это не самый лучший метод, не самый правильный, и не самый удобный. Но из всего, что я видел мне он представляется самым гибким.

    $parameters = array('conditions' => array('hidden' => 0));
    $main_menu = menu_build_tree('main-menu', $parameters);
    $main_menu = main_menu($main_menu, $menu_array, 0);
    $desktop_menu = get_main_desktop_menu($main_menu, $menu_render);
    print $desktop_menu;

Теперь по порядку разберём что тут и как:

    $parameters = array('conditions' => array('hidden' => 0));
    $main_menu = menu_build_tree('main-menu', $parameters);

menu_build_tree
Данный код возвращает возвращает дерево меню. В данном случае дерево главного меню, системное имя которого main-menu. Но дерево это огромное. Работать с ним скажем так не очень удобно, поэтому мы собираем только реально нужные данные. Сделает это кастомная функция main_menu. В template.php реализуем код данной функции:

	    /**
		 * @param $menu массив, который получаем с помощью функции menu_build_tree
		 * @param $menu_array переменная, в которую собирается результирующий массив
		 * @param $level переменная, которая определяет уровень вложенности меню
		 * @return массив, содержащий все необходимые данные по меню
		 */
		function main_menu($menu, &$menu_array, $level)
		{
		    $i = 0;

		    foreach ($menu as $item) {
		        $menu_array[$i]['link_title'] = $item['link']['link_title'];
		        $menu_array[$i]['level'] = $level;
		        $menu_array[$i]['has_children'] = $item['link']['has_children'];
		        $menu_array[$i]['mlid'] = $item['link']['mlid'];
		        $menu_array[$i]['plid'] = $item['link']['plid'];
		        $menu_array[$i]['depth'] = $item['link']['depth'];

		        if (substr_count($item['link']['link_path'], "node/") > 0) {
		            $menu_array[$i]['href'] = drupal_lookup_path("alias", $item['link']['link_path']);
		        } else {
		            $menu_array[$i]['href'] = $item['link']['link_path'];
		        }
		        $menu_array[$i]['href'] = ($menu_array[$i]['href'] != '') ? $menu_array[$i]['href'] : '';

		        if (!empty($item['below'])) {
		            main_menu($item['below'], $menu_array[$i]['below'], $level + 1);
		        }
		        $i++;
		    }

		    return $menu_array;
		}
	

На выходе получаем массив вида:

		Array
		(
		    [0] => Array
		        (
		            [link_title] => Главная
		            [level] => 0
		            [has_children] => 1
		            [mlid] => 219
		            [plid] => 0
		            [depth] => 1
		            [href] => 
		            [below] => Array
		                (
		                    [0] => Array
		                        (
		                            [link_title] => Почему нас выбирают
		                            [level] => 1
		                            [has_children] => 0
		                            [mlid] => 443
		                            [plid] => 219
		                            [depth] => 2
		                            [href] => why_us_choose
		                        )

		                )

		        )

		    [1] => Array
		        (
		            [link_title] => Наставники
		            [level] => 0
		            [has_children] => 1
		            [mlid] => 439
		            [plid] => 0
		            [depth] => 1
		            [href] => teachers
		            [below] => Array
		                (
		                    [0] => Array
		                        (
		                            [link_title] => Контакты
		                            [level] => 1
		                            [has_children] => 0
		                            [mlid] => 444
		                            [plid] => 439
		                            [depth] => 2
		                            [href] => contacts
		                        )

		                )

		        )
		)
	

С этим массивом уже работать нормально. Дальше уже в зависимости от того, какой вид должно принимать меню реализуется функция get_main_desktop_menu. В моём случае было двухуровневое меню. У списка UL верхнего уровня согласно вёрстки были классы class="nav navbar-nav", у вложенных UL класс class="drop-down d-7". У меня получилась вот такая функция:

		/**
		 * @param $menu массив, полученный с помощью функции main_menu
		 * @param $menu_render переменная, в которую рекурсивно выводятся нужные данные
		 * @return string, html, содержащий десктопное главное меню
		 */
		function get_main_desktop_menu($menu, &$menu_render)
		{
		    foreach ($menu as $key => $item) {
		        $class_li = ($_GET['q'] == $item['href']) ? "class='active'" : "";
		        if (($key == 0) && ($item['level'] == 0)){ // если это первый элемент на верхнем уровне
		            
		            }
		            $menu_render .= "...";
		        } elseif(($key + 1) == count($menu) && ($item['level'] == 0)){ // если это последний элемент на верхнем уровне
		            
		           $menu_render .= "...";
		        } elseif($key != 0){ // если это не первый и не последний элемент
		            
		            $menu_render .= "...";
		        }
		    }
		    return $menu_render;
		}
	

Пример конечной функции, которая рекурсивно выводит меню.

		/**
		 * @param $menu массив, полученный с помощью функции main_menu
		 * @param $menu_render переменная, в которую рекурсивно выводятся нужные данные
		 * @return string, html, содержащий десктопное главное меню
		 */
		function get_main_map_menu($menu, &$menu_render)
		{

		    foreach ($menu as $key => $item) {
		        $parent = ($item['has_children']) ? 'parent' : '';
		        $sub_menu_render = NULL;

		        $link = "/".$item['href'];

		        if (($key == 0) && (($key + 1) != count($menu))) { // если это первый элемент в списке на текущем уровне и не последний
		            $menu_render .= ul+li+a
		            if (!empty($item['below'])) {
		                $sub_menu = get_main_map_menu($item['below'], $sub_menu_render);
		                $menu_render .= $sub_menu;
		            }
		            $menu_render .= /li;
		            
		        } elseif (($key != 0) && (($key + 1) == count($menu))) { // если это последний элемент в списке на текущем уровне и не единственный
		            $menu_render .= li+a
		            if (!empty($item['below'])) {
		                $sub_menu = get_main_map_menu($item['below'], $sub_menu_render);
		                $menu_render .= $sub_menu;
		            }
		            $menu_render .= /li;
		            $menu_render .= /ul;

		        } elseif (($key == 0) && (($key + 1) == count($menu))) { // если это первый и единственный элемент на текущем уровне
		            $menu_render .= ul+li+a
		            if (!empty($item['below'])) {
		                $sub_menu = get_main_map_menu($item['below'], $sub_menu_render);
		                $menu_render .= $sub_menu;
		            }
		            $menu_render .= /li;
		            $menu_render .= /ul;
		        } else { //если это не первый и не единственный элемент на текущем уровне
		            $menu_render .= li+a
		            if (!empty($item['below'])) {
		                $sub_menu = get_main_map_menu($item['below'], $sub_menu_render);
		                $menu_render .= $sub_menu;
		            }
		            $menu_render .= /li;
		        }

		    }

		    return $menu_render;

		}
	

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

Тэги:

Тэг в списке: