Предположим у вас стоит задача как у верстальщика – сверстать шаблон на движке WordPress и по техническому заданию нужно иметь полный контроль к построению и отображению меню. Как это сделать правильно?
WordPress позволяет использовать так называемые классы Walker для отображения различных древовидных структур. В этом посте мы узнаем о том, как создать и кастомизировать дочерний класс walker для построения собственного меню.
Надо сказать, что WordPress использует классы Walker не только для меню, но и для целого ряда других случаев, например для вывода иерархий таксономии, иерархий комментариев, wp_list_pages() и wp_list_categories(). Все они расширяют общий класс Walker. Итак как было заявлено в теме сейчас мы расширим класс Walker_Nav_Menu, который как раз используется для построения меню в WordPress.
Для лучшего понимания темы стоит хотя бы примерно понимать что такое классы в php и как можно их расширять.
Поскольку мы именно расширяем какой-то класс, то нам нужно добавить только те функции, которые мы хотим переопределить. Остальные функции будут браться из родительского класса (класса, который мы расширяем).
[the_ad id=’954′]
Подготовка
Вы можете добавить свой кастомный класс walker куда угодно – в файлы плагинов, в function.php темы или в любой PHP-файл, зарегистрированный в functions.php. Итак, мы создаем свой класс, (убедитесь, что имя класса уникально и оно начинается с заглавной буквы), и указываем, собственно говорим, что расширяем Walker_Nav_Menu, выглядит это так:
class Web86_Menu_Walker extends Walker_Nav_Menu {
}
Code language: JavaScript (javascript)После этого, как нам сказать WordPress чтобы он начал использовать наш класс walker взамен стандартного? Сделать это можно прямо там где мы непосредственно выводим меню через wp_nav_menu(). Эта функция отвечает за вывод меню.
В массиве аргументов wp_nav_menu() мы добавляем новый элемент с ключом ‘walker’ и создаем новый экземпляр нашего класса walker следующим образом:
wp_nav_menu([
'theme_location' => 'primary',
'menu_class' => 'main-menu',
'container' => 'nav',
'container_class' => 'header__main-nav',
'walker' => new Web86_Menu_Walker() // Вот тут!
]);
Code language: PHP (php)И теперь если вы обновите свой сайт, то конечно не увидите никаких изменений. Потому, что наш класс не переопределяет ни одну из родительских функций, в результате WordPress просто запускает стандартные функции walker когда формирует меню.
Какие функции мы можем переопределить в Walker_Nav_Menu?
Ниже приведены основные функции, которые вы можете добавить в свой кастомный класс, чтобы переопределить родительский класс Walker_Nav_Menu:
- start_lvl()
- end_lvl()
- start_el()
- end_el()
Внимание! В них мы не используем никакое ECHO, а просто добавляем к первому аргументу $output (строковый тип данных) свои данные паровозиком, подробнее ниже.
start_lvl
Функция start_lvl отвечает за вывод HTML-кода с которого начинается новый уровень меню (т.е. подменю). По умолчанию он выводит начальный <ul>, и его можно переопределить например на <div class=”pod-menu”>
function start_lvl(&$output, $depth=0, $args=null) {
$output .= '<div class="pod-menu">';
}
Code language: PHP (php)Первый параметр $output – это строка, к которой вы добавите свой вывод. $depth – это целое число, которое указывает, на каком уровне вы находитесь; 0 для верхнего уровня, 1 для потомка верхнего уровня и так далее. $args – это объект всех аргументов, представленных в wp_nav_menu().
end_lvl
Функция end_lvl отвечает за вывод HTML-кода для конца подуровня меню. По умолчанию это просто закрытие </ul>, но мы можем его переопределить на </div>.
function end_lvl(&$output, $depth=0, $args=null) {
$output .= '</div>';
}
Code language: PHP (php)Остальные параметры идентичны функции start_lvl().
start_el
Эта функция отвечает за вывод HTML-кода каждого элемента по умолчанию в не зависимости от уровня. По умолчанию она выводит начальный тег <li> и тег <a> с заголовком и ссылкой внутри, и это также мы можем переопределить.
function start_el(&$output, $item, $depth=0, $args=null, $id=0) {
}
Code language: PHP (php)Первый аргумент – $output, является строкой, к которой вы добавляете какие-то данные. Второй аргумент $item – объект пункта меню – и именно в нем вы получаете большую часть данных для вывода элемента меню (можете посмотреть что в нем находится просто через print_r($item)). Если типом пункта меню является “пост”, то вы получите объект “поста”. В зависимости от типа меню, вы получите какие-то дополнительные полезные элементы, такие как классы, url, title и description, которые можно вывести.
Третий аргумент, $depth, – это целое число, указывающее, на каком уровне мы находимся. Уровень 0 – это верхний уровень, 1 – прямой потомок верхнего уровня и так далее. Четвертый аргумент, $args, является объектом всех аргументов, предоставляемых wp_nav_menu(). Пятый параметр $ID, это идентификатор текущего элемента меню.
end_el
Функция end_el отвечает за вывод закрытия элемента. Обычно он просто выводит тег </li>.
function end_el(&$output, $item, $depth=0, $args=null) {
}
Code language: PHP (php)Аргументы для end_el такие же, как и для start_el описанные выше, за исключением того, что эта функция не имеет пятого параметра $id.
Изменим вывод элементов меню
Итак мы узнали, что функция start_el() отвечает за вывод HTML-кода для одного элемента меню. Давайте начнем с переопределения этой функции в нашем классе walker на простом примере.
Пример 1: Как убрать ссылки для пунктов меню с ‘#’
Если у пункта меню есть ссылка ‘#‘ то мы вместо того чтобы вывести содержимое в тег <a> выведем его в тег <span>, выглядеть это будет так:
class Web86_Menu_Walker extends Walker_Nav_Menu {
function start_el(&$output, $item, $depth=0, $args=[], $id=0) {
$output .= "<li class='" . implode(" ", $item->classes) . "'>";
if ($item->url && $item->url != '#') {
$output .= '<a href="' . $item->url . '">';
} else {
$output .= '<span>';
}
$output .= $item->title;
if ($item->url && $item->url != '#') {
$output .= '</a>';
} else {
$output .= '</span>';
}
}
}
Code language: PHP (php)Мы начали с того что добавили тег <li> к $output. Мы хотим убедиться, что классы WordPress по умолчанию (например, “menu-item”, “menu-item-has-children” и т.д.), а также классы, введенные вручную в Редакторе меню, будут добавлены в наш элемент списка. Мы склеиваем классы, представленные в виде массива в $item->classes, используя PHP-функцию implode(), разделяющую каждый элемент пробелом.
В строках 5-9 мы прописали условие вывода элемента как ссылку или как просто текст. Если $item->url не равен #, то выводим элемент в теге <span>. Далее в 11 строке мы добавляем к $output название пункта меню, которое содержится в $item->title.
Это все, что нам нужно сделать для того, чтобы все элементы меню, имеющие ” # ” в качестве URL-адреса были не кликабельны!
Пример 2: Как показать поле “Описание” для пунктов меню?
Возможно вам понадобится вывести описание пунктов меню, которое задается в Редакторе меню. По умолчанию оно не активировано, чтобы его активировать в Редакторе меню WordPress вам нужно нажать кнопку “Настройки экрана” в правом верхнем углу и поставить галочку на “описание”:
Таким образом мы разрешили добавлять описание для каждого пункта меню. Давайте его выведем в нашем классе Walker. Допустим, вы хотите показать описание только для меню верхнего уровня. Мы можем просто проверить есть ли у $item описание и если $depth равно 0, таким образом:
if ($depth == 0 && !empty($item->description)) {
$output .= '<span class="description">'.$item->description.'</span>';
}
Code language: PHP (php)Пример 3: добавление каретки для пукнтов имеющих подпункты
Как добавить значок каретки для родительского пункта меню, который бы указывал на выпадающее меню?
Саму иконку каретки можно например взять из Fontawesome, это библиотека с бесплатными иконками, вывод иконки например можно сделать так:
if ($args->walker->has_children) {
$output .= '<i class="caret fa fa-angle-down"></i>';
}
Code language: PHP (php)Ну а теперь добавим немного логики, в каком случае мы будем показывать каретку. Нам нужно условие, которое будет проверять есть ли у текущего элемента дочерние элементы, в этом нам поможет $args->walker->has_children, если условие верное, то иконка добавится:
class Web86_Menu_Walker extends Walker_Nav_Menu {
function start_el(&$output, $item, $depth=0, $args=[], $id=0) {
$output .= "<li class='" . implode(" ", $item->classes) . "'>";
$output .= '<a href="' . $item->url . '">';
$output .= $item->title;
$output .= '</a>';
if ($args->walker->has_children) {
$output .= '<i class="caret fa fa-angle-down"></i>';
}
}
}
Code language: PHP (php)Если вы конечно хотите, чтобы при выпадении вложенного меню значок каретки как-то переворачвался, то придется написать для этого пару строк на JS.
Кроме того, если вам нужно как-то стилизовать только верхний уровень меню, добавив к нему какие-то блоки и классы, то это можно сделать просто используя условие if ( $depth === 0 ) и далее делать вывод в output если условие отвечает true.
Или другая ситуация, вам нужно изменить выходные данные в зависимости от наличия определенного класса (например, класса, введенного вручную в Редакторе меню), а затем искать его в $item->classes, так например можно добавлять класс active активному пункту меню, вот рабочий пример:
$is_current_item = '';
if(array_search('current-menu-item', $item->classes) != 0)
{
$is_current_item = ' active';
}
Code language: PHP (php)Как видно из приведенных выше примеров, вы можете манипулировать выводом так, как вам хочется, используя любые условия.
[the_ad id=’977′]
Передача аргументов вашему walker через функцию wp_nav_menu
Хочу рассказать вам про еще одну весьма полезную вещь. Объект $args содержит в себе все пользовательские аргументы переданные через функцию wp_nav_menu(). И конечно вы можете использовать их как условия для кастомизации меню.
Например вы можете использовать параметр theme_location, чтобы показать меню в какой-то области темы. Или если вы хотите вывести одно и то же меню для настольного компьютера и для мобильного телефона, но с изменениями, то использовать параметр is_mobile_menu. Или если вы хотите, чтобы ваш walker манипулировал элементами только тогда, когда они выводятся через wp_nav_menu() в вашей теме, а не когда меню добавляется через виджет. Все эти данные можно передавать в wp_nav_menu() и затем брать из $args.
В качестве простого примера я добавлю логическое значение ‘show_carets‘ к аргументам wp_nav_menu() , чтобы каретки для обозначения родителя добавлялись не во все меню, а только в тех случаях, когда я этого захочу:
wp_nav_menu([
'theme_location' => 'primary',
'menu_class' => 'main-menu',
'container' => 'nav',
'container_class' => 'header__main-nav',
'walker' => new Web86_Menu_Walker(),
'show_carets' => true // Вот тут!
]);
Code language: PHP (php)Затем я могу немного подредактировать код, который мы уже написали в примерах выше, который добавляет каретку. Я добавлю еще одно условие – если в $args есть “show_carets”, то каретки будут показаны:
if ($args->show_carets && $args->walker->has_children) {
$output .= '<i class="caret fa fa-angle-down"></i>';
}
Code language: PHP (php)Таким образом вы можете добавлять любые аргументы, которые вам нужны.
Вывод меню через шорткод
Если вы делаете плагин который через Walker кастомизирует меню и вам нужно вывести его через шорткод с помощью функции wp_nav_menu(), то стоит обратить внимание на один из ее аргументов, который стоит по умолчанию как [‘echo’ => true] – это означает, что меню будет выводиться автоматически БЕЗ return и БЕЗ echo. Но если вам нужно иметь полный контроль над тем что выводит шорткод, например вам нужно вывести какую-то HTML структуру до ввода самого меню, то следует в функцию wp_nav_menu(), передать [‘echo’ => false] и возвращать меню уже как обычную перемененную в любом контексте.
Перейдем к практике:
function my_function(){
$variable = '<div id="main"><div class="wrap1"><div class="wrap2"><div class="wrap3">';
$variable .= wp_nav_menu([
'theme_location' => 'primary',
'menu_class' => 'main-menu',
'container' => 'nav',
'container_class' => 'header__main-nav',
'walker' => new Web86_Menu_Walker(),
'show_carets' => true,
'echo' => false // Вот тут!
]);
$variable .= '</div></div><div class="arrows"><i>некоторые теги управления</i></div></div></div>';
return $variable;
}
add_shortcode('menu-test','my_function');
Code language: PHP (php)Вот и все, теперь мы можем через шорткод в любом месте вывести наше меню. Если вы добавляете его в статью то в редакторе пишем [menu-test], а если в самом шаблоне, то do_shortcode(‘[menu-test]’);
И на этом я буду заканчивать этот обзор, а если у вас будут вопросы, то не стесняйтесь писать в комментариях.


Спасибо за разжевывание! Очень помог!)
Рад, что материал оказался полезным!
Спасибо! Отличный материал! Сейчас пытаюсь вывести подменю колонками. Попытаюсь решить самостоятельно, если не получится – спрошу у вас)))
Капча не дала отправить комментарий из Google Chrome.