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

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

Как создать собственное кастомное меню 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)

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

[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]’);

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

Понравилась статья? Поделиться с друзьями:
Комментариев: 3
  1. User

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

    1. koshsh (автор)

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

  2. Александр

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

Добавить комментарий

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