Skip to content

Instantly share code, notes, and snippets.

@mikedoubintchik
Created August 4, 2017 19:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikedoubintchik/deca86f0d1f7df80c252d3754e0bbed4 to your computer and use it in GitHub Desktop.
Save mikedoubintchik/deca86f0d1f7df80c252d3754e0bbed4 to your computer and use it in GitHub Desktop.
Navwalker class for Sage 9
<?php
namespace App;
/**
* Class NavWalker
*
* Bootstrap 4 walker with cleaner markup for wp_nav_menu()
* For use with Sage >= 8.5
*
* Based on Soil NavWalker
* @url https://github.com/roots/soil
*
*
* Walker_Nav_Menu (WordPress default) example output:
* <li id="menu-item-8" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-8"><a href="/">Home</a></li>
*
* NavWalker example output:
* <li class="nav-item menu-item menu-home"><a class="nav-link" href="/">Home</a></li>
*
* @package Roots\Sage\Nav
*/
class NavWalker extends \Walker_Nav_Menu
{
/**
* @var bool
*/
private $cpt; // Boolean, is current post a custom post type
/**
* @var false|string
*/
private $archive; // Stores the archive page for current URL
/**
* NavWalker constructor.
*/
public function __construct()
{
add_filter('nav_menu_css_class', array($this, 'cssClasses'), 10, 2);
add_filter('nav_menu_item_id', '__return_null');
$cpt = get_post_type();
$this->cpt = in_array($cpt, get_post_types(array('_builtin' => false)));
$this->archive = get_post_type_archive_link($cpt);
}
/**
* Check item classes for current or active
*
* @param $classes
*
* @return int
*/
public function checkCurrent($classes)
{
return preg_match('/(current[-_])|active/', $classes);
}
// @codingStandardsIgnoreStart
/**
* Add dropdown menu class to dropdown UL
*
* @param string $output
* @param int $depth
* @param array $args
*/
function start_lvl(&$output, $depth = 0, $args = [])
{
$output .= "\n<ul class=\"dropdown-menu\" aria-labelledby=\"navbarDropdownMenuLink\">\n";
}
/**
* Add required Bootstrap 4 classes to anchor links.
*
* @param string $output
* @param \WP_Post $item
* @param int $depth
* @param array $args
* @param int $id
*/
function start_el(&$output, $item, $depth = 0, $args = [], $id = 0)
{
$item_html = '';
parent::start_el($item_html, $item, $depth, $args);
if ($item->is_subitem) {
$item_html = str_replace('<a', '<a class="nav-link dropdown-toggle" data-hover="dropdown" aria-haspopup="true" aria-expanded="false"', $item_html);
$item_html = str_replace('</a>', ' <b class="caret"></b></a>', $item_html);
} else {
$item_html = str_replace('<a', '<a class="nav-link"', $item_html);
}
$item_html = apply_filters('wp_nav_menu_item', $item_html);
$output .= $item_html;
}
/**
* Add active classes to active items & sub items
*
* @param object $element
* @param array $children_elements
* @param int $max_depth
* @param int $depth
* @param array $args
* @param string $output
*/
public function display_element($element, &$children_elements, $max_depth, $depth = 0, $args, &$output)
{
$element->is_subitem = ((!empty($children_elements[$element->ID]) && (($depth + 1) < $max_depth || ($max_depth === 0))));
if ($element->is_subitem) {
foreach ($children_elements[$element->ID] as $child) {
if ($child->current_item_parent || $this->url_compare($this->archive, $child->url)) {
$element->classes[] = 'active';
}
}
}
$element->is_active = (!empty($element->url) && strpos($this->archive, $element->url));
if ($element->is_active) {
$element->classes[] = 'active';
}
parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output);
}
// @codingStandardsIgnoreEnd
/**
* Clean up css classes
*
* @param $classes
* @param $item
*
* @return array
*/
public function cssClasses($classes, $item)
{
$slug = sanitize_title($item->title);
// Fix core `active` behavior for custom post types
if ($this->cpt) {
$classes = str_replace('current_page_parent', '', $classes);
if ($this->url_compare($this->archive, $item->url)) {
$classes[] = 'active';
}
}
// Remove most core classes
$classes = preg_replace('/(current(-menu-|[-_]page[-_])(item|parent|ancestor))/', 'active', $classes);
$classes = preg_replace('/^((menu|page)[-_\w+]+)+/', '', $classes);
// Add `menu-item` class & re-add core `menu-item` class
$classes[] = 'nav-item menu-item';
// Add `dropdown` class & re-add core `menu-item-has-children` class on parent elements
if ($item->is_subitem) {
$classes[] = 'dropdown menu-item-has-children';
}
// Add `menu-<slug>` class
$classes[] = 'menu-' . $slug;
$classes = array_unique($classes);
$classes = array_map('trim', $classes);
return array_filter($classes);
}
/**
* Make a URL relative
*
* Utility function, from soil
* @url https://github.com/roots/soil
*
* @param $input
*
* @return string
*/
public function root_relative_url($input)
{
if (is_feed()) {
return $input;
}
$url = parse_url($input);
if (!isset($url['host']) || !isset($url['path'])) {
return $input;
}
$site_url = parse_url(network_home_url()); // falls back to home_url
if (!isset($url['scheme'])) {
$url['scheme'] = $site_url['scheme'];
}
$hosts_match = $site_url['host'] === $url['host'];
$schemes_match = $site_url['scheme'] === $url['scheme'];
$ports_exist = isset($site_url['port']) && isset($url['port']);
$ports_match = ($ports_exist) ? $site_url['port'] === $url['port'] : true;
if ($hosts_match && $schemes_match && $ports_match) {
return wp_make_link_relative($input);
}
return $input;
}
/**
* Compare URL against relative URL
*
* Utility function, from Soil
* @url https://github.com/roots/soil
*
* @param $url
* @param $rel
*
* @return bool
*/
public function url_compare($url, $rel)
{
$url = trailingslashit($url);
$rel = trailingslashit($rel);
return ((strcasecmp($url, $rel) === 0) || $this->root_relative_url($url) == $rel);
}
}
/**
* Clean up wp_nav_menu_args
*
* Remove the container
* Remove the id="" on nav menu items
*
* @param string $args
*
* @return array
*/
function nav_menu_args($args = '')
{
$nav_menu_args = [];
$nav_menu_args['container'] = false;
if (is_array($args) && !$args['items_wrap']) {
$nav_menu_args['items_wrap'] = '<ul class="%2$s">%3$s</ul>';
}
if (!$args['walker']) {
$nav_menu_args['walker'] = new NavWalker();
}
return array_merge($args, $nav_menu_args);
}
add_filter('wp_nav_menu_args', __NAMESPACE__ . '\\nav_menu_args');
add_filter('nav_menu_item_id', '__return_null');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment