Программный вывод меню в 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; }
Буду признателен, если в комментариях кто-нибудь покажет более элегантный и не такой громоздкий способ вывода кастомного меню с неограниченным уровнем вложенности.