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