2

The question

I would like to check what type of menu item each item in my menu is i.e. is it a Page, Category, or Custom link.

What I want to achieve

I want to create a menu made up of categories and pages links. Of the categories in the menu I only want to show those which contain posts. Page links will always show.

This menu will be used on an ecommerce site with a catalogue that changes constantly. Making a dynamic menu which hides empty categories removes the need to constantly update the menu in the admin area.

What I have now

A custom walker menu based on this SE post which successfully hides links to empty categories by adding logic to the start_el function. Unfortunately my logic hides pages links too

Full walker menu function:

function cs_modify_nav_menu_args( $args ){
if( 'primary-menu' == $args['theme_location'] ){
    $args['walker'] = new cs_walker_nav_menu();
}
return $args;
}

class cs_walker_nav_menu extends Walker_Nav_Menu {
// filter empty categories and add main/sub classes to li's and links
function start_el(  &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
    global $wp_query;


    if(!is_page($item->ID)){

        $non_empty_categories = get_categories(array('taxonomy' => 'product_cat'));
        $empty_categories = array();
        $is_empty = true;
        // check menu items are for an empty category
        foreach ( $non_empty_categories as $cat )
            if ($item->object_id === $cat->term_id)
                $is_empty = false;
            // if it is empty add it to array
            if ($is_empty)
                $empty_categories[] = $item->ID;
        // Don't build nav item for items in the is_empty array
        foreach( $empty_categories as $category_to_skip )
            if( $item->ID == $category_to_skip )
                return $output;
    }

    $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 ? 'menu-item-odd' : 'menu-item-even' ),
        '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
    $output .= $indent . '<li id="nav-menu-item-'. $item->ID . '" class="' . $depth_class_names . ' ' . $class_names . '">';

    // 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' ) . '"';

    $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
    );

    // build html
    $output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}

Attempted solutions

The nav_menu_item object does not supply the menu item type.

Passing $item->ID to is_page does not return. I believe $item->ID is the menu item ID not a post ID.

1
  • did u tried get_posts() function to check the post_type ?
    – Zammuuz
    Sep 25, 2014 at 6:07

1 Answer 1

1

Fortunately, there is an easy fix for this.

The $item in the Walker delivers Classes, and if the menu item is an archive, you get one of those:

  • menu-item-type-taxonomy (general)
  • menu-item-object-product_cat (product_cat == taxonomyname)

as well as

  • $item->type == 'taxonomy'
  • $item->object == 'your_taxonomy'

If you want this logic to work for various Archives (not just the product_cat) you need to change the taxonomy as well - again, $item conveniently delivers your $taxonomy as $item->object.

Now you only need to change your if and the taxonomy in your Walker

if( $item->type == 'taxonomy' ) {

    $non_empty_categories = get_categories( array( 'taxonomy' => $item->object ) );

    // ... The rest of your logic works fine

}

Should work just fine :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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