WordPress + Bootstrap: Как скрестить NavBar с wp_nav_menu.

Стандартная навигация Bootstrap имеет замысловатую разметку, что не позволяет изпользовать ее при создании меню в WordPress с помощью функции wp_nav_menu(), но эту проблему можно решить с помощью нестандартного Walker и нескольких фильтров.

Задача

Главная задача – повторить стандартную разметку NavBar с помощью wp_nav_menu(). Выглядит она вот так:

<nav class="navbar navbar-default" role="navigation">
	<!-- Brand and toggle get grouped for better mobile display -->
	<div class="navbar-header">
		<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
			<span class="sr-only">Toggle navigation</span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
		</button>
		<a class="navbar-brand" href="#">Brand</a>
	</div>

	<!-- Collect the nav links, forms, and other content for toggling -->
	<div class="collapse navbar-collapse navbar-ex1-collapse">
		<ul class="nav navbar-nav">
      <li class="active"><a href="#">Link</a></li>
      <li><a href="#">Link</a></li>
      <li class="dropdown">
        <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>
        <ul class="dropdown-menu">
          <li><a href="#">Action</a></li>
          <li><a href="#">Another action</a></li>
          <li><a href="#">Something else here</a></li>
          <li><a href="#">Separated link</a></li>
          <li><a href="#">One more separated link</a></li>
        </ul>
      </li>
    </ul>
  </div><!-- /.navbar-collapse -->
</nav>

Собственно, только элемент <ul class="nav navbar-nav">...</ul> будет заменен функцией wp_nav_menu(). А значит получим такую конечную разметку:

<nav id="nav" class="navbar navbar-default" role="navigation">
	<!-- Brand and toggle get grouped for better mobile display -->
	<div class="navbar-header visible-xs">
		<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
			<span class="sr-only">Toggle navigation</span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
		</button>
		<a class="navbar-brand" href="#">Kawaii Walls</a>
	</div>

	<!-- Collect the nav links, forms, and other content for toggling -->
	<div class="collapse navbar-collapse navbar-ex1-collapse">
		<?php wp_nav_menu(array(
			'container_class' => 'menu-header',
			'theme_location' => 'primary',
			'items_wrap' => '<ul id="%1$s" class="%2$s nav navbar-nav">%3$s</ul>',
			'walker' => new Bootstrap_Walker_Nav_Menu,
		)); ?>
	</div><!-- /.navbar-collapse -->
</nav>

Стоить заметить, что вам заранее нужно разрешить использование меню в WordPress теме и зарегистрировать новое меню, прикрепив его к позиции primary.

Решение

Создаем свой нестандартный Walker. Это такой класс, который позволяет особым образом вывести стандартное меню в WordPress.

class Bootstrap_Walker_Nav_Menu extends Walker_Nav_Menu {

	/**
	 * Display Element
	 */
	function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
		$id_field = $this->db_fields['id'];

		if ( isset( $args[0] ) && is_object( $args[0] ) )
		{
			$args[0]->has_children = ! empty( $children_elements[$element->$id_field] );

		}

		return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
	}

	/**
	 * Start Element
	 */
	function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
		if ( is_object($args) && !empty($args->has_children) )
		{
			$link_after = $args->link_after;
			$args->link_after = ' <b class="caret"></b>';
		}

		parent::start_el($output, $item, $depth, $args, $id);

		if ( is_object($args) && !empty($args->has_children) )
			$args->link_after = $link_after;
	}

	/**
	 * Start Level
	 */
	function start_lvl( &$output, $depth = 0, $args = array() ) {
		$indent = str_repeat("t", $depth);
		$output .= "\n$indent<ul class=\"dropdown-menu list-unstyled\">\n";
	}
}

Таким образом мы немного кастомизировали вывод меню, чтобы оно подходило для Bootstrap.

Последним шагом остается обеспечить возможность работы вложенных меню. Делается это с помощью следующего фильтра:

add_filter('nav_menu_link_attributes', function($atts, $item, $args) {
	if ( $args->has_children )
	{
		$atts['data-toggle'] = 'dropdown';
		$atts['class'] = 'dropdown-toggle';
	}

	return $atts;
}, 10, 3);

Обратите внимание, что тут используется анонимная функция. Это не будет работать на сервере со старой версией PHP.

Нашли ошибку или возник вопрос – пишите в комментариях.