Custom Walker - split child menu items into columns - php

I have this pretty basic custom walker for my nav. Is there any way to split the child menu items into columns, for example, if we have 6 child nav items in the first nav item, how can I put 3 in one column and 3 in another column?
I know I can do that with CSS, but I would extend it further to group items based on their count and that won't work with CSS.
class SC_Menu_Walker extends Walker_Nav_Menu {
function start_el(&$output, $item, $depth=0, $args=[], $id=0) {
global $wp_query;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$this->curItem = $item;
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args, $depth ) );
$class_names = ' class="'. esc_attr( $class_names ) . '"';
$output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';
if ($item->url && $item->url != '#') {
$output .= '<a href="' . $item->url . '">';
} else {
$output .= '<span>';
}
$output .= $item->title;
}
function start_lvl(&$output, $depth = 0, $args = array()) {
$sc_page_item_label = get_post_meta( $this->curItem->object_id, '_sc_page_item_label', true );
$sc_page_item_description = get_post_meta( $this->curItem->object_id, '_sc_page_item_description', true );
$sc_page_item_url = get_post_meta( $this->curItem->object_id, '_sc_page_item_url', true );
$sc_page_item_page_url = get_page_link($sc_page_item_url);
$secondMenuClass="sub-menu__list o-sm-col-12 o-lg-col-7 u-font-size-medium";
$wrapperScrollStart="";
if ($depth == 1) {
$secondMenuClass="sub-menu__list sub-menu__list--scrollbar";
$wrapperScrollStart="<div class='sub-menu__inner'>";
$wrapperScrollEnd ='</div>';
}
$output .= '<div class="sub-menu sub">
<div class="o-custom-container">
<div class="sub-menu__wrapper_info o-sm-col-12 o-lg-col-5"><div class="o-lg-col-7"><h5>'.$sc_page_item_label.'</h5> <div class="u-font-size-medium">'.$sc_page_item_description.'</div></div>
<div>
<div class="c-learn-more-holder">
<div class="c-learn-more">
<div class="c-learn-more-title">Learn more</div>
</div>
</div>
</div>
</div>'.$wrapperScrollStart.'<ul class="'.$secondMenuClass.'">';
}
//end of the sub menu wrap
function end_lvl(&$output, $depth = 0, $args = array()) {
$wrapperScrollEnd = '';
if ($depth == 1) {
$wrapperScrollEnd ='</div>';
}
$wrapperScrollEnd ='</div>';
$output .= '</ul>'.$wrapperScrollEnd.'</div>';
}
}

Related

Walker_Nav_Menu() for top level links only

I have a menu in the WordPress backend that looks like this:
What we do
Solutions
Solution 1
Solution 2
Services
Service 1
Contact
Items What we do and Contact are top level links and I only want these level 1 links to show in my main header.
As such, I created a custom walker to achieve this:
<?php
class simple_header extends Walker_Nav_Menu{
/*
* start_lvl : Responsible for start of a new level, such as <ul>
* end_lvl : Responsible for end of a level, such as </ul>
* start_el : Responsible for start of inner element, such as <li>
* end_el : Responsible for end of inner element, such as <\li>
*/
public function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
}
public function end_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("\t", $depth);
}
public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = $item->ID;
$class_names = 'header__nav-menu-li';
if (in_array('current-menu-item', $classes)) {
$class_names .= ' header__nav-menu-li--active';
}
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
if ($depth === 0) {
$output .= $indent . '<li data-test' . $class_names .'>';
}
$atts = array();
$atts['title'] = ! empty( $item->attr_title ) ? $item->attr_title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
// New
if ($depth === 0) {
$atts['class'] = 'header__nav-menu-anchor link-header';
}
if (in_array('current-menu-item', $item->classes)) {
$atts['class'] .= ' link-header--active';
}
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args, $depth );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( ! empty( $value ) ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
/** This filter is documented in wp-includes/post-template.php */
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
public function end_el( &$output, $item, $depth = 0, $args = array() ) {
if ($depth === 0) {
$output .= "</li>\n";
}
}
}
?>
However, with the above walker, it still prints out the child markup. The HTML looks like this:
<li class="header__nav-menu-li header__nav-menu-li--active">
What we do
Solutions
Services
</li>
I have defined $depth === 0 in my walker, so unsure why it's printing child links?
Normally, at the start_lvl and end_lvl you would output <ul> and </ul> tags respectively to start and end a new list.
Every item in your menu consists of an anchor tag wrapped inside a list item:
<li>Target</li>
In your code you only have an 'if' statement around the code that adds the list item tags to the output.
On the last line of your start_el function you still add the anchor.
Instead of wrapping an if statement around the parts where you want to output markup, my advice would be to add the following line of code as the first line of your start_el function:
if($depth !== 0) { return; }
That way you skip execution of code for items that are not in the first level.

Add top level menu item title to start_lvl in Wordpress menu walker

I've created a custom WordPress menu using the WordPress walker function. I've managed to do all the necessary HTML I need except adding the top-level menu title to just after one of the divs in the "start_lvl" section in the function.
Output should look like this:
<ul>
<li>Title
<div class="mega-menu">
<div class="mega-menu-inner">
<h2 class="fz-40">DISPLAY TOP MENU TITLE HERE</h2>
<ul class="mega-menu-accordion-wrap">
<li class="mega-menu-accordion">
<div class="mega-menu-accordion-inner">
<ul>
<li>
<div class="sub-mega-menu-wrap">
<ul>
<li></li>
</ul>
</div>
</li>
</ul>
</div>
</li>
</ul>
</div>
</div>
</li>
</ul>
This is what I have so far for the WP nav walker:
wp_nav_menu( array(
'theme_location' => "header-menu",
'walker' => new WPDocs_Walker_Nav_Menu() ) );
/**
* Custom walker class.
*/
class WPDocs_Walker_Nav_Menu extends Walker_Nav_Menu {
/**
* Starts the list before the elements are added.
*
* Adds classes to the unordered list sub-menus.
*
* #param string $output Passed by reference. Used to append additional content.
* #param int $depth Depth of menu item. Used for padding.
* #param array $args An array of arguments. #see wp_nav_menu()
*/
function start_lvl( &$output, $depth = 0, $args = array() ) {
// Depth-dependent classes.
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
$display_depth = ( $depth + 1); // because it counts the first submenu as 0
$classes = array(
'sub-menu',
( $display_depth % 2 == 0 ? 'menu-odd' : 'mega-menu-accordion-wrap' ),
( $display_depth >=2 ? 'sub-sub-menu' : '' ),
'menu-depth-' . $display_depth
);
$class_names = implode( ' ', $classes );
// Build HTML for output.
if( $display_depth === 1):
global $title;
$output .= '<div class="mega-menu">';
$output .= '<div class="mega-menu-container-wrap">';
$output .= '<div class="mega-menu-help-bar"><figure> <img src="'.get_template_directory_uri().'/svg/search-white.svg" alt=""></figure>Need some help?</div>';
$output .= '<div class="mega-menu-inner">';
$output .= '<h2 class="fz-40">Need the Title Here''</h2>';
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
elseif( $display_depth % 3 === 0):
$output .= "\n" . $indent . '<div class="sub-mega-menu-wrap">' . "\n";
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
elseif( $display_depth % 2 === 0):
$output .= "\n" . $indent . '<div class="mega-menu-accordion-inner">' . "\n";
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
else:
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";
endif;
}
/**
* Start the element output.
*
* Adds main/sub-classes to the list items and links.
*
* #param string $output Passed by reference. Used to append additional content.
* #param object $item Menu item data object.
* #param int $depth Depth of menu item. Used for padding.
* #param array $args An array of arguments. #see wp_nav_menu()
* #param int $id Current item ID.
*/
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
global $wp_query;
$indent = ( $depth > 0 ? str_repeat( "\t", $depth ) : '' ); // code indent
// Depth-dependent classes.
$depth_classes = array(
( $depth == 0 ? 'main-menu-item' : 'sub-menu-item' ),
( $depth >=2 ? 'sub-sub-menu-item' : '' ),
( $depth % 2 == 0 ? 'menu-item-odd' : 'mega-menu-accordion' ),
'menu-item-depth-' . $depth
);
$depth_class_names = esc_attr( implode( ' ', $depth_classes ) );
// Passed classes.
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$class_names = esc_attr( implode( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) ) );
// Build HTML.
if( $depth % 2 === 0):
$output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . ' ">';
else:
$output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';
endif;
// Link attributes.
$attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) .'"' : '';
$attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) .'"' : '';
$attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : '';
$attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) .'"' : '';
$attributes .= ' class="menu-link ' . ( $depth > 0 ? 'sub-menu-link' : 'main-menu-link' ) . '"';
// Build HTML output and pass through the proper filter.
$item_output = sprintf( '%1$s<a%2$s>%3$s%4$s%5$s</a>%6$s',
$args->before,
$attributes,
$args->link_before,
apply_filters( 'the_title', $item->title, $item->ID ),
$args->link_after,
$args->after
);
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
Solved this shortly after my post,
for anyone who requires this, I did the following:
Add this to the functions.php file:
function add_menu_title( $item_output, $item, $depth, $args ) {
global $menuTitle;
$menuTitle = $item->title;
return $item_output;
}
add_filter( 'walker_nav_menu_start_el', 'add_menu_title', 10, 4);
Then in your header template just add this to the section you would like it display. In my example I updated the depth===1 to the following:
if( $display_depth === 1):
global $menuTitle;
$output .= '<div class="mega-menu">';
$output .= '<div class="mega-menu-container-wrap">';
$output .= '<div class="mega-menu-help-bar"><figure> <img src="'.get_template_directory_uri().'/svg/search-white.svg" alt=""></figure>Need some help?</div>';
$output .= '<div class="mega-menu-inner">';
$output .= '<h2 class="fz-40">'.$menuTitle.'</h2>';
$output .= "\n" . $indent . '<ul class="' . $class_names . '">' . "\n";

Unable to create sub menu more than two levels in wordpress

I did go through this: https://www.elegantthemes.com/blog/tips-tricks/how-to-create-custom-menu-structures-in-wordpress
I did as per the article above to create sub menu of 3rd level. However, In front end the third level of product category for Woocommerce (green apple) not showing up in the dropdown list.
I also read one of stackoverflow answer which says its not possible to create third-level sub menu. I do believe there's a workaround for this. Please advise me.
Thank you.
Tried as per #Rafin's answer,
If third level placed after all second level, then it shows up but this is not where the sub menus for second level menu (apple) supposed to be.
in the backend, placed after all second level menus
Shows up this way in front end,
But I need it to be arranged this way:
You can use following Walker Class that extends Wordpress Nav Menu
<?php
class ods_bootstrap_navwalker extends Walker_Nav_Menu {
/**
* #see Walker::start_lvl()
* #since 3.0.0
*
* #param string $output Passed by reference. Used to append additional content.
* #param int $depth Depth of page. Used for padding.
*/
public function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat( "\t", $depth );
$output .= "\n$indent<div class=\"drop\"><ul role=\"menu\" class=\" dropdown-menu\">\n";
}
/**
* #see Walker::start_el()
* #since 3.0.0
*
* #param string $output Passed by reference. Used to append additional content.
* #param object $item Menu item data object.
* #param int $depth Depth of menu item. Used for padding.
* #param int $current_page Menu item ID.
* #param object $args
*/
public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
/**
* Dividers, Headers or Disabled
* =============================
* Determine whether the item is a Divider, Header, Disabled or regular
* menu item. To prevent errors we use the strcasecmp() function to so a
* comparison that is not case sensitive. The strcasecmp() function returns
* a 0 if the strings are equal.
*/
if ( strcasecmp( $item->attr_title, 'divider' ) == 0 && $depth === 1 ) {
$output .= $indent . '<li role="presentation" class="divider">';
} else if ( strcasecmp( $item->title, 'divider') == 0 && $depth === 1 ) {
$output .= $indent . '<li role="presentation" class="divider">';
} else if ( strcasecmp( $item->attr_title, 'dropdown-header') == 0 && $depth === 1 ) {
$output .= $indent . '<li role="presentation" class="dropdown-header">' . esc_attr( $item->title );
} else if ( strcasecmp($item->attr_title, 'disabled' ) == 0 ) {
$output .= $indent . '<li role="presentation" class="disabled">' . esc_attr( $item->title ) . '';
} else {
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'scroll menu-item-' . $item->ID;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
if ( $args->has_children )
$class_names .= ' dropdown';
if ( in_array( 'current-menu-item', $classes ) )
$class_names .= ' active';
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $value . $class_names .'>';
$atts = array();
$atts['title'] = ! empty( $item->title ) ? $item->title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
$atts['class'] = "back-top";
// If item has_children add atts to a.
if ( $args->has_children && $depth === 0 ) {
$atts['href'] = '#';
$atts['data-toggle'] = 'dropdown';
$atts['class'] = 'dropdown-toggle';
$atts['aria-haspopup'] = 'true';
} else {
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
}
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( ! empty( $value ) ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
$item_output = $args->before;
/*
* Glyphicons
* ===========
* Since the the menu item is NOT a Divider or Header we check the see
* if there is a value in the attr_title property. If the attr_title
* property is NOT null we apply it as the class name for the glyphicon.
*/
if ( ! empty( $item->attr_title ) )
$item_output .= '<a'. $attributes .'><span class="glyphicon ' . esc_attr( $item->attr_title ) . '"></span> ';
else
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= ( $args->has_children && 0 === $depth ) ? ' <span class="icon-play"></span></a>' : '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
/**
* Traverse elements to create list from elements.
*
* Display one element if the element doesn't have any children otherwise,
* display the element and its children. Will only traverse up to the max
* depth and no ignore elements under that depth.
*
* This method shouldn't be called directly, use the walk() method instead.
*
* #see Walker::start_el()
* #since 2.5.0
*
* #param object $element Data object
* #param array $children_elements List of elements to continue traversing.
* #param int $max_depth Max depth to traverse.
* #param int $depth Depth of current element.
* #param array $args
* #param string $output Passed by reference. Used to append additional content.
* #return null Null on failure with no changes to parameters.
*/
public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
if ( ! $element )
return;
$id_field = $this->db_fields['id'];
// Display this element.
if ( is_object( $args[0] ) )
$args[0]->has_children = ! empty( $children_elements[ $element->$id_field ] );
parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
/**
* Menu Fallback
* =============
* If this function is assigned to the ods_nav_menu's fallback_cb variable
* and a manu has not been assigned to the theme location in the WordPress
* menu manager the function with display nothing to a non-logged in user,
* and will add a link to the WordPress menu manager if logged in as an admin.
*
* #param array $args passed from the ods_nav_menu function.
*
*/
public static function fallback( $args ) {
if ( current_user_can( 'manage_options' ) ) {
extract( $args );
$fb_output = null;
if ( $container ) {
$fb_output = '<' . $container;
if ( $container_id )
$fb_output .= ' id="' . $container_id . '"';
if ( $container_class )
$fb_output .= ' class="' . $container_class . '"';
$fb_output .= '>';
}
$fb_output .= '<ul';
if ( $menu_id )
$fb_output .= ' id="' . $menu_id . '"';
if ( $menu_class )
$fb_output .= ' class="' . $menu_class . '"';
$fb_output .= '>';
$fb_output .= '<li>Add a menu</li>';
$fb_output .= '</ul>';
if ( $container )
$fb_output .= '</' . $container . '>';
echo $fb_output;
}
}
}
And you can display menu with
<?php
wp_nav_menu( array(
'menu' => your_menu_name,
'theme_location' => your_menu_location,
'depth' => 3,
'container' => '',
'container_class' => '',
'container_id' => 'bs-example-navbar-collapse-1',
'menu_class' => 'navigation',
'fallback_cb' => 'ods_bootstrap_navwalker::fallback',
'walker' => new ods_bootstrap_navwalker())
);
?>
Take a reference to https://developer.wordpress.org/reference/classes/walker_nav_menu/
The easy code is here you can customize your HTML whatever you want
$parent = array();
$menu_name = 'menu-1'; //change menu name
if (($locations = get_nav_menu_locations()) && isset($locations[$menu_name]))
{
$menu = wp_get_nav_menu_object($locations[$menu_name]);
$menu_items = wp_get_nav_menu_items($menu->term_id);
$parent_id = 0;
foreach((array)$menu_items as $key => $menu_item)
{
if($menu_item->menu_item_parent == 0)
{
$parent_id = $menu_item->db_id;
$title = $menu_item->title;
$url = $menu_item->url;
array_push($parent, array("title" => $title, "url" => $url, "child" => array()));
}
else if($menu_item->menu_item_parent == $parent_id)
{
$childId = $menu_item->ID;
$title = $menu_item->title;
$url = $menu_item->url;
array_push($parent[count($parent) - 1]["child"], array("ID"=>$childId,"title" => $title, "url" => $url));
}
}
}
foreach($parent as $key=>$childval){
$parent[$key]['child']['subchild'];
if (!empty($childval['child'])) {
for($i=0;$i<count($childval['child']);$i++){
foreach($menu_items as $childitems){
if($childitems->menu_item_parent==$childval['child'][$i]['ID']){
$values = array("ID"=>$childitems->ID,"title" => $childitems->title, "url" => $childitems->url);
$parent[$key]['child'][$i]['childvalue'][] = $values;
}
}
}
}
}
echo "<pre>";
print_r($parent);
?
this will print your array then modify according to you.
thank me later..
the demo HTML is here
<ul class="nav navbar-nav">
<?php
foreach ($parent as $key => $value)
{
if(empty($value["child"]))
{
echo "<li><a href='" . $value["url"] . "'>" . $value["title"] . "</a></li>";
}
else
{
echo '<li class="dropdown">' . $value["title"] . ' <b class="caret"></b><ul class="dropdown-menu">';
foreach ($value["child"] as $key => $value)
{
echo '<li>' . $value["title"] . '</li>';
echo "<ul>";
foreach ($value['childvalue'] as $childval ){
echo '<li>' . $childval["title"] . '</li>';
}
echo "</ul>";
}
echo '</ul></li>';
}
}
?>
</ul>
You can use this code to display 3 levels hierarchical menu
Add this code in the function.php file.
function create_bootstrap_menu( $theme_location ) {
if ( ($theme_location) && ($locations = get_nav_menu_locations()) && isset($locations[$theme_location]) ) {
$menu = get_term( $locations[$theme_location], 'nav_menu' );
$menu_items = wp_get_nav_menu_items($menu->term_id);
//echo '<pre>'; print_r($menu_items); die;
$menu_list = '<ul>';
$menucount = 1;
$bool = true;
foreach( $menu_items as $menu_item ) {
if( $menu_item->menu_item_parent == 0 ) {
$parent = $menu_item->ID;
$menu_array = array();
foreach( $menu_items as $submenu ) {
if( $submenu->menu_item_parent == $parent ) {
$bool = true;
$menu_array[] = '<li class="dropdown"><a href="' . $submenu->url . '" >' . $submenu->title . '</a><ul>';
$parents = $submenu->ID;
foreach($menu_items as $submenus){
if( $submenus->menu_item_parent == $parents ) {
$menu_array[] .= '<li><a href="' . $submenus->url . '" >' . $submenus->title . '</a></li>';
}
}
$menu_array[] .= '</ul></li>';
}
}
if( $bool == true && count( $menu_array ) > 0 ) {
$menu_list .= '<li class="dropdown">';
$menu_list .= '<span>'.$menu_item->title.'</span> <i class="bi bi-chevron-down"></i>';
$menu_list .= '<ul>' ."\n";
$menu_list .= implode( $menu_array );
$menu_list .= '</ul>';
} else {
// echo "<pre>"; print_r($menu_item);
$menu_list .= '<li>';
$menu_list .= '<a class="nav-link scrollto active" href="'.$menu_item->url.'">' . $menu_item->title . '</a>';
}
}
// end <li>
$menu_list .= '</li>';
$menucount++;
}
} else {
$menu_list = '<!-- no menu defined in location "'.$theme_location.'" -->';
}
return $menu_list;
}
Add this code to your menu page(header.php)
<?php echo create_bootstrap_menu('primary'); ?>

Wordpress Walker — how to add number of post to category menu items?

I wanted to add number of posts for menu items that are categories.
For example:
Category 1 (5)
Category 2 (8)
Category 3 (2)
etc...
I edit walker:
get number:
$item_count = $depth == 0 ? get_posts( array(
'post_type' => 'post',
'numberposts' => -1,
'orderby' => 'category',
'order' => 'ASC'
) ) : false;
output:
$item_output .= $item_count ? ' <span>(' . count( $item_count ) . ')</span>' : '';
The result is a postscript to each menu item the number of all posts.
How to modify?
use wp_list_categories
<?php wp_list_categories(array('show_count' => true)); ?>
UPDATE for your instance:
with the full code from the link you provided in the comments we add an if statement right before the closing <a>
if($item->type == 'taxonomy'){
$cat = get_category( $item->object_id);
$item_output .= ' ('.$cat->count.')</a>';
}
FULL CODE:
/* Bootstrap_Walker for Wordpress
* Author: George Huger, Illuminati Karate, Inc
* More Info: http://illuminatikarate.com/blog/bootstrap-walker-for-wordpress
*
* Formats a Wordpress menu to be used as a Bootstrap dropdown menu (http://getbootstrap.com).
*
* Specifically, it makes these changes to the normal Wordpress menu output to support Bootstrap:
*
* - adds a 'dropdown' class to level-0 <li>'s which contain a dropdown
* - adds a 'dropdown-submenu' class to level-1 <li>'s which contain a dropdown
* - adds the 'dropdown-menu' class to level-1 and level-2 <ul>'s
*
* Supports menus up to 3 levels deep.
*
*/
class Bootstrap_Walker extends Walker_Nav_Menu
{
/* Start of the <ul>
*
* Note on $depth: Counterintuitively, $depth here means the "depth right before we start this menu".
* So basically add one to what you'd expect it to be
*/
function start_lvl(&$output, $depth)
{
$tabs = str_repeat("\t", $depth);
// If we are about to start the first submenu, we need to give it a dropdown-menu class
if ($depth == 0 || $depth == 1) { //really, level-1 or level-2, because $depth is misleading here (see note above)
$output .= "\n{$tabs}<ul class=\"dropdown-menu\">\n";
} else {
$output .= "\n{$tabs}<ul>\n";
}
return;
}
/* End of the <ul>
*
* Note on $depth: Counterintuitively, $depth here means the "depth right before we start this menu".
* So basically add one to what you'd expect it to be
*/
function end_lvl(&$output, $depth)
{
if ($depth == 0) { // This is actually the end of the level-1 submenu ($depth is misleading here too!)
// we don't have anything special for Bootstrap, so we'll just leave an HTML comment for now
$output .= '<!--.dropdown-->';
}
$tabs = str_repeat("\t", $depth);
$output .= "\n{$tabs}</ul>\n";
return;
}
/* Output the <li> and the containing <a>
* Note: $depth is "correct" at this level
*/
function start_el(&$output, $item, $depth, $args)
{
global $wp_query;
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
/* If this item has a dropdown menu, add the 'dropdown' class for Bootstrap */
if ($item->hasChildren) {
$classes[] = 'dropdown';
// level-1 menus also need the 'dropdown-submenu' class
if($depth == 1) {
$classes[] = 'dropdown-submenu';
}
}
/* This is the stock Wordpress code that builds the <li> with all of its attributes */
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
$output .= $indent . '<li id="menu-item-'. $item->ID . '"' . $value . $class_names .'>';
$attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) .'"' : '';
$attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) .'"' : '';
$attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : '';
$attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) .'"' : '';
$item_output = $args->before;
/* If this item has a dropdown menu, make clicking on this link toggle it */
if ($item->hasChildren && $depth == 0) {
$item_output .= '<a'. $attributes .' class="dropdown-toggle" data-toggle="dropdown">';
} else {
$item_output .= '<a'. $attributes .'>';
}
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
/* Output the actual caret for the user to click on to toggle the menu */
if ($item->hasChildren && $depth == 0) {
$item_output .= '<b class="caret"></b></a>';
}elseif($item->type == 'taxonomy'){
$cat = get_category( $item->object_id);
$item_output .= ' ('.$cat->count.')</a>';
} else {
$item_output .= '</a>';
}
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
return;
}
/* Close the <li>
* Note: the <a> is already closed
* Note 2: $depth is "correct" at this level
*/
function end_el (&$output, $item, $depth, $args)
{
$output .= '</li>';
return;
}
/* Add a 'hasChildren' property to the item
* Code from: http://wordpress.org/support/topic/how-do-i-know-if-a-menu-item-has-children-or-is-a-leaf#post-3139633
*/
function display_element ($element, &$children_elements, $max_depth, $depth = 0, $args, &$output)
{
// check whether this item has children, and set $item->hasChildren accordingly
$element->hasChildren = isset($children_elements[$element->ID]) && !empty($children_elements[$element->ID]);
// continue with normal behavior
return parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output);
}
}
If you are using some custom post taxonomie you can change the code for this one :
if ($item->type == 'taxonomy'){
$term = get_term($item->object_id);
$item_output .= '<div class="meni-item-badge"> '.$term->count .'</div>';
}
Hope this help someone !!
Regards Mario

wordpress menu - assign specefic class of list to anchor

I want to assign specefic class in menu list to its child anchor.
for example:
<li class="icon-home list-item">
Home
</li>
I want to convert above output to this (Remove class icon-home from list and assign it to its child anchor):
<li class="list-item">
<a class="icon-home" href="index.php">Home</a>
</li>
What I want to do ??
Search for matching class icon- in wordpress menu array, then set it to its child anchor and then unset it from list.
I am using a code which search for a class divider in menu array and then convert it to a new list with class="divider". Here is that code:
// managing divider: add divider class to an element to get a divider before it.
$divider_class_position = array_search('divider', $classes);
if($divider_class_position !== false){
$output .= "<li class=\"divider\"></li>\n";
unset($classes[$divider_class_position]);
}
Here is my all code for wordpress menu override, Please have a look before answering:
<?php
/**
* Extended Walker class for use with the
* Twitter Bootstrap toolkit Dropdown menus in Wordpress.
* Edited to support n-levels submenu.
* #author johnmegahan https://gist.github.com/1597994, Emanuele 'Tex' Tessore https://gist.github.com/3765640
*/
class NavMenuWalker extends Walker_Nav_Menu {
function start_lvl( &$output, $depth ) {
$indent = str_repeat( "\t", $depth );
$submenu = ($depth > 0) ? ' sub-menu' : '';
$output .= "\n$indent<ul class=\"dropdown-menu$submenu depth_$depth\">\n";
}
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
$li_attributes = '';
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
// managing divider: add divider class to an element to get a divider before it.
$divider_class_position = array_search('divider', $classes);
if($divider_class_position !== false){
$output .= "<li class=\"divider\"></li>\n";
unset($classes[$divider_class_position]);
}
$classes[] = ($args->has_children) ? 'dropdown' : '';
$classes[] = ($item->current || $item->current_item_ancestor) ? 'active' : '';
$classes[] = 'menu-item-' . $item->ID;
if($depth && $args->has_children){
$classes[] = 'dropdown-submenu';
}
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
$class_names = ' class="' . esc_attr( $class_names ) . '"';
$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
$id = strlen( $id ) ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $value . $class_names . $li_attributes . '>';
$attributes = ! empty( $item->attr_title ) ? ' title="' . esc_attr( $item->attr_title ) .'"' : '';
$attributes .= ! empty( $item->target ) ? ' target="' . esc_attr( $item->target ) .'"' : '';
$attributes .= ! empty( $item->xfn ) ? ' rel="' . esc_attr( $item->xfn ) .'"' : '';
$attributes .= ! empty( $item->url ) ? ' href="' . esc_attr( $item->url ) .'"' : '';
$attributes .= ($args->has_children) ? ' class="dropdown-toggle" data-toggle="dropdown"' : '';
$item_output = $args->before;
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= ($depth == 0 && $args->has_children) ? ' <b class="caret"></b></a>' : '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
function display_element( $element, &$children_elements, $max_depth, $depth=0, $args, &$output ) {
//v($element);
if ( !$element )
return;
$id_field = $this->db_fields['id'];
//display this element
if ( is_array( $args[0] ) )
$args[0]['has_children'] = ! empty( $children_elements[$element->$id_field] );
else if ( is_object( $args[0] ) )
$args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
$cb_args = array_merge( array(&$output, $element, $depth), $args);
call_user_func_array(array(&$this, 'start_el'), $cb_args);
$id = $element->$id_field;
// descend only when the depth is right and there are childrens for this element
if ( ($max_depth == 0 || $max_depth > $depth+1 ) && isset( $children_elements[$id]) ) {
foreach( $children_elements[ $id ] as $child ){
if ( !isset($newlevel) ) {
$newlevel = true;
//start the child delimiter
$cb_args = array_merge( array(&$output, $depth), $args);
call_user_func_array(array(&$this, 'start_lvl'), $cb_args);
}
$this->display_element( $child, $children_elements, $max_depth, $depth + 1, $args, $output );
}
unset( $children_elements[ $id ] );
}
if ( isset($newlevel) && $newlevel ){
//end the child delimiter
$cb_args = array_merge( array(&$output, $depth), $args);
call_user_func_array(array(&$this, 'end_lvl'), $cb_args);
}
//end this element
$cb_args = array_merge( array(&$output, $element, $depth), $args);
call_user_func_array(array(&$this, 'end_el'), $cb_args);
}
}
You have to look for:
$item_output .= '<a'. $attributes .'>';
and replace it with:
$item_output .= '<a'. $attributes .' class="'.$classes[0].'">';
Now it changes <li class="icon-home list-item"> to <a href="index.php" class="icon-home">
On $attributes just display Link active and hover effect no one specific class define in link .

Categories