Как создать собственное кастомное меню WordPress с помощью класса Walker_Nav_Menu?

Предположим у вас стоит задача как у верстальщика – сверстать шаблон на движке WordPress и по техническому заданию нужно иметь полный контроль к построению и отображению меню. Как это сделать правильно?

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

Надо сказать, что WordPress использует классы Walker не только для меню, но и для целого ряда других случаев, например для вывода иерархий таксономии, иерархий комментариев, wp_list_pages() и wp_list_categories(). Все они расширяют общий класс Walker. Итак как было заявлено в теме сейчас мы расширим класс Walker_Nav_Menu, который как раз используется для построения меню в WordPress.

Для лучшего понимания темы стоит хотя бы примерно понимать что такое классы в php и как можно их расширять.

Поскольку мы именно расширяем какой-то класс, то нам нужно добавить только те функции, которые мы хотим переопределить. Остальные функции будут браться из родительского класса (класса, который мы расширяем).

Подготовка

Вы можете добавить свой кастомный класс 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 вам нужно нажать кнопку “Настройки экрана” в правом верхнем углу и поставить галочку на “описание”:

Как создать собственное кастомное меню WordPress с помощью класса Walker_Nav_Menu?

Таким образом мы разрешили добавлять описание для каждого пункта меню. Давайте его выведем в нашем классе Walker. Допустим, вы хотите показать описание только для меню верхнего уровня. Мы можем просто проверить есть ли у $item описание и если $depth равно 0, таким образом:

if ($depth == 0 && !empty($item->description)) {
	$output .= '<span class="description">'.$item->description.'</span>';
}
Code language: PHP (php)

Пример 3: добавление каретки для пукнтов имеющих подпункты

Как добавить значок каретки для родительского пункта меню, который бы указывал на выпадающее меню?

WP добавление каретки для пукнтов имеющих подпункты
Пример каретки в действии

Саму иконку каретки можно например взять из 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)

Как видно из приведенных выше примеров, вы можете манипулировать выводом так, как вам хочется, используя любые условия.

Передача аргументов вашему 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]’);

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

Like this post? Please share to your friends:
Comments: 3
  1. User

    Спасибо за разжевывание! Очень помог!)

    1. koshsh (автор)

      Рад, что материал оказался полезным!

  2. Александр

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

Leave a Reply

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: