Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I know this question has been asked in different forms. But as far as I can tell from searching Google and SE/WP it's never been satisfactorily answered.

Let's say I have a taxonomy clubs and a custom post type person.

On each taxonomy-club.php page I display all the people who are members of that club.

But I want to achieve a similar effect in my menus:

wp_nav_menu
|
|--Everyone
|  \
|  |- Mr Verlaine
|  |- Mr Lloyd
|  |- Mr Osterberg 
|  |- Mr Williamson    
|
|--Snooker Club
|  \ 
|  |- Mr Osterberg 
|  |- Mr Williamson
|
|--Golf Club
|  \
|  |- Mr Verlaine
|  |- Mr Lloyd

If possible, I would like my menus to have the current page highlighting functionality of wp_nav_menu().

I've experimented with this:

add_filter('wp_nav_menu_items','add_terms');

function add_terms($items) {
$items .= '<li>' . wp_list_categories( array(
    'taxonomy' => 'clubs', 
    'title_li' => 'Clubs') ) 
     . '</li>';
return $items;
}

but it doesn't populate the submenus with the people's names, and it doesn't have that nav_menu css-class goodness.

Must I create a more complicated <li> by querying out the posts in each term of the taxonomy, paste that in, and that's my lot? Or extend the built-in walker (will it give me the advantages of wp_nav_menu?)

Or something else...

Thanks!

share|improve this question

1 Answer 1

up vote 1 down vote accepted
+50

I would go for custom nav menu walker...

//define the custom post type and custom taxonomy
define("MENU_CPT", "people");
define("MENU_CT", "club");

//custom function for selecting posts based on a term
function get_posts_by_term($term_id, $post_type=MENU_CPT, $taxonomy=MENU_CT) {
    $args = array(
        'posts_per_page' => -1,
        'post_type' => $post_type,
        'tax_query' => array(
            array(
                'taxonomy' => $taxonomy,
                'field' => 'id',
                'terms' => $term_id
            )
        )
    );                  
    return get_posts( $args );
}

//custom nav menu walker class
class Custom_Walker_Nav_Menu extends Walker_Nav_Menu {
    /**
     * Display array of elements hierarchically.
     *
     * It is a generic function which does not assume any existing order of
     * elements. max_depth = -1 means flatly display every element. max_depth =
     * 0 means display all levels. max_depth > 0  specifies the number of
     * display levels.
     *
     * @since 2.1.0
     *
     * @param array $elements
     * @param int $max_depth
     * @return string
     */
    function walk( $elements, $max_depth) {

        $args = array_slice(func_get_args(), 2);
        $output = '';

        if ($max_depth < -1) //invalid parameter
            return $output;

        if (empty($elements)) //nothing to walk
            return $output;

        $id_field = $this->db_fields['id'];
        $parent_field = $this->db_fields['parent'];

        // flat display
        if ( -1 == $max_depth ) {
            $empty_array = array();
            foreach ( $elements as $e )
                $this->display_element( $e, $empty_array, 1, 0, $args, $output );
            return $output;
        }

        /*
         * need to display in hierarchical order
         * separate elements into two buckets: top level and children elements
         * children_elements is two dimensional array, eg.
         * children_elements[10][] contains all sub-elements whose parent is 10.
         */
        $top_level_elements = array();
        $children_elements  = array();
        foreach ( $elements as $e) {
            if ( 0 == $e->$parent_field ) 
            {
                $top_level_elements[] = $e;
                if ( $e->type=='taxonomy' && $e->object == MENU_CT ) {

                    $taxonomy_posts = get_posts_by_term($e->object_id);

                    foreach ( $taxonomy_posts as $tax_post ) {                      
                        $tax_post = wp_setup_nav_menu_item($tax_post);
                        $tax_post->post_type = 'nav_menu_item';
                        $tax_post->menu_item_parent = $e->$id_field;
                        $tax_post->object = 'custom';
                        $tax_post->type = 'custom';
                        $tax_post->ID = $e->$id_field.$tax_post->ID;
                        $children_elements[ $e->$id_field ][] = $tax_post; 
                        $children_elements_classes[] = $tax_post; 
                    }
                }
            }
            else
            {
                $children_elements[ $e->$parent_field ][] = $e;
            }
        }

        /*
         * when none of the elements is top level
         * assume the first one must be root of the sub elements
         */
        if ( empty($top_level_elements) ) {

            $first = array_slice( $elements, 0, 1 );
            $root = $first[0];

            $top_level_elements = array();
            $children_elements  = array();
            foreach ( $elements as $e) {
                if ( $root->$parent_field == $e->$parent_field )
                {
                    $top_level_elements[] = $e;
                    if ( $e->type=='taxonomy' && $e->object == MENU_CT ) {

                        $taxonomy_posts = get_posts_by_term($e->object_id);

                        foreach ( $taxonomy_posts as $tax_post ) {                      
                            $tax_post = wp_setup_nav_menu_item($tax_post);
                            $tax_post->post_type = 'nav_menu_item';
                            $tax_post->menu_item_parent = $e->$id_field;
                            $tax_post->object = 'custom';
                            $tax_post->type = 'custom';
                            $tax_post->ID = $e->$id_field.$tax_post->ID;
                            $children_elements[ $e->$id_field ][] = $tax_post;
                            $children_elements_classes[] = $tax_post; 
                        }
                    }
                }
                else
                {
                    $children_elements[ $e->$parent_field ][] = $e;
                }
            }
        }

        //assing the classes to our dynamically populated posts
        if ( $children_elements_classes )
            _wp_menu_item_classes_by_context($children_elements_classes);

        foreach ( $top_level_elements as $e )
            $this->display_element( $e, $children_elements, $max_depth, 0, $args, $output );

        /*
         * if we are displaying all levels, and remaining children_elements is not empty,
         * then we got orphans, which should be displayed regardless
         */
        if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
            $empty_array = array();
            foreach ( $children_elements as $orphans )
                foreach( $orphans as $op )
                    $this->display_element( $op, $empty_array, 1, 0, $args, $output );
         }

         return $output;
    }
}

...you can now use it in wp_nav_menu function:

wp_nav_menu( array( 'theme_location' => 'primary', 'walker' => new Custom_Walker_Nav_Menu ) );

When you put the term from the defined taxonomy into your menu it will be automatically populated by the posts (people names) from defined custom post type for particular taxonomy term.

share|improve this answer
    
Thank you for being so generous as to demonstrate! This is very clever code. I was struggling to get my head around the wp_nav_menu API and the way in which data passes through a Walker. The above is going to help enormously. Very grateful to you. –  djb Nov 21 '11 at 8:33
    
NB I found this didn't display taxmembers in submenus, but fixed this by allowing the if(e->type == taxonomy) clause to execute even if the element wasn't top level. –  djb Nov 21 '11 at 10:43
    
I am glad it helped you. –  JohnnyPea Nov 21 '11 at 12:05

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.