WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have a custom walker menu.( See the code below)

I would like to have my nav menu like this

----------------------------------
Home | link1 | link2 | link3 (5)
----------------------------------

Where 5 is the children count of link3.

Can someone help me to implement this?

Here is my current custom nav walker code.

<?php
/**
 * Cleaner walker for wp_nav_menu()
 *
 * 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>
 *   <li id="menu-item-9" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-9"><a href="/sample-page/">Sample Page</a></l
 *
 * Roots_Nav_Walker example output:
 *   <li class="menu-home"><a href="/">Home</a></li>
 *   <li class="menu-sample-page"><a href="/sample-page/">Sample Page</a></li>
 */
class Roots_Nav_Walker extends Walker_Nav_Menu {
  function check_current($classes) {
    return preg_match('/(current[-_])|active|dropdown/', $classes);
  }

  function start_lvl(&$output, $depth = 0, $args = array()) {
    $output .= "\n<ul class=\"dropdown-menu\">\n";
  }

  function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
    $item_html = '';
    parent::start_el($item_html, $item, $depth, $args);

    if ($item->is_dropdown && ($depth === 0)) {
      $item_html = str_replace('<a', '<a class="dropdown-toggle" data-toggle="dropdown" data-target="#"', $item_html);
      $item_html = str_replace('</a>', ' <b class="caret"></b></a>', $item_html);
    }
    elseif (stristr($item_html, 'li class="divider')) {
      $item_html = preg_replace('/<a[^>]*>.*?<\/a>/iU', '', $item_html);
    }
    elseif (stristr($item_html, 'li class="nav-header')) {
      $item_html = preg_replace('/<a[^>]*>(.*)<\/a>/iU', '$1', $item_html);
    }

    $output .= $item_html;
  }

  function display_element($element, &$children_elements, $max_depth, $depth = 0, $args, &$output) {
    $element->is_dropdown = !empty($children_elements[$element->ID]);

    if ($element->is_dropdown) {
      if ($depth === 0) {
        $element->classes[] = 'dropdown';
      } elseif ($depth === 1) {
        $element->classes[] = 'dropdown-submenu';
      }
    }

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

/**
 * Remove the id="" on nav menu items
 * Return 'menu-slug' for nav menu classes
 */
function roots_nav_menu_css_class($classes, $item) {
  $slug = sanitize_title($item->title);
  $classes = preg_replace('/(current(-menu-|[-_]page[-_])(item|parent|ancestor))/', 'active', $classes);
  $classes = preg_replace('/^((menu|page)[-_\w+]+)+/', '', $classes);

  $classes[] = 'menu-' . $slug;

  $classes = array_unique($classes);

  return array_filter($classes, 'is_element_empty');
}
add_filter('nav_menu_css_class', 'roots_nav_menu_css_class', 10, 2);
add_filter('nav_menu_item_id', '__return_null');

/**
 * Clean up wp_nav_menu_args
 *
 * Remove the container
 * Use Roots_Nav_Walker() by default
 */
function roots_nav_menu_args($args = '') {
  $roots_nav_menu_args['container'] = false;

  if (!$args['items_wrap']) {
    $roots_nav_menu_args['items_wrap'] = '<ul class="%2$s">%3$s</ul>';
  }

  if (current_theme_supports('bootstrap-top-navbar')) {
    $roots_nav_menu_args['depth'] = 3;
  }

  if (!$args['walker']) {
    $roots_nav_menu_args['walker'] = new Roots_Nav_Walker();
  }

  return array_merge($args, $roots_nav_menu_args);
}
add_filter('wp_nav_menu_args', 'roots_nav_menu_args');
share|improve this question
    
Not sure if duplicate, but here's a very similar Q with solution: wordpress.stackexchange.com/q/88419/12615 – brasofilo Feb 26 '13 at 8:55
    
@brasofilo Thankyou. I solved it using that answer. – Giri Feb 26 '13 at 9:20
    
I find it ridiculous that you can find out if the menu item has children from the $item via $item->has_children but you can't find out how many children it has? Would be nice to know this without having to load up nav menu functions that will cause extra burden within the start_el function, since everything should be known about the menu at that point already! – Solomon Closson Jul 21 at 17:25
    
typo, meant $args->has_children. In any case, if you can find out that it has children, why can't we know how many children it has? – Solomon Closson Jul 21 at 17:36
    
@SolomonClosson You do realise this Q&A is more than 3 years old? WordPress released lot of versions in the last 3 years and improved their codebase. If you would like to improve the code of this Q&A feel free to edit it and submit for review. Thanks – Giri Jul 21 at 18:30
up vote 1 down vote accepted
function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) {
    $item_html = '';
    parent::start_el($item_html, $item, $depth, $args);
    $locations = get_nav_menu_locations();
    $menu = wp_get_nav_menu_object( $locations['primary_navigation'] );
    $menu_items = wp_get_nav_menu_items($menu->term_id);
    $count = 0;
    foreach( $menu_items as $menu_item ){
        if( $menu_item->menu_item_parent == $item->ID ){
            $count++;           
        }       
    }
    if ($item->is_dropdown) {
      $item_html = str_replace('</a>', ' <span class="badge">'.$count.'</span></a>', $item_html);
    }
    if ($item->is_dropdown && ($depth === 0)) {
      $item_html = str_replace('<a', '<a class="dropdown-toggle" data-toggle="dropdown" data-target="#"', $item_html);
      $item_html = str_replace('</a>', ' <b class="caret"></b></a>', $item_html);
    }
    elseif (stristr($item_html, 'li class="divider')) {
      $item_html = preg_replace('/<a[^>]*>.*?<\/a>/iU', '', $item_html);
    }
    elseif (stristr($item_html, 'li class="nav-header')) {
      $item_html = preg_replace('/<a[^>]*>(.*)<\/a>/iU', '$1', $item_html);
    }

    $output .= $item_html;
  }
share|improve this answer
    
Loading up menu items from start_el is going to kill your servers performance. The count of children it has should already be known and be a property of $item. They need to add that property in there, this is RIDICULOUS! – Solomon Closson Jul 21 at 17:27

Another option would be to take a look at what the $item variable includes within thestart_el() function.

For example, the menu-order property might be of use to you.

function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    echo $item->menu_order;
}

This might output something like: 1234 if you have four menu items.

share|improve this answer

I sort of added onto @Giri's answer by using array_map and array_count_values. if this helps anyone in the future. I didn't wish to use a counter and a foreach loop for something so simple.

function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    if ($item->hasChildren) {
        $locations = get_nav_menu_locations(); // Getting the locations of the nav menus array.
        $menu = wp_get_nav_menu_object( $locations['primary_navigation'] ); // Getting the menu calling the walker from the array.
        $menu_items = wp_get_nav_menu_items($menu->term_id); // Getting the menu item objects array from the menu.
        $menu_item_parents = array_map(function($o) { return $o->menu_item_parent; }, $menu_items); // Getting the parent ids by looping through the menu item objects array. This will give an array of parent ids and the number of their children.
        $children_count = array_count_values($menu_item_parents)[$item->ID]; // Get number of children menu item has.
    }
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.