3

I'm looking around and see some hints at what I'm wanting to do may be possible, but it's just not clicking for me.

I have a custom post type rt_doctors. I'm trying to make it so there will be a dynamic list of the doctors in my main menu.

I'd like to have in the Nav Menu a menu item doctors that links to the index/archive page of my doctors Custom Post Type, and hanging from this Menu Item, a list of Submenu Items each linking to a single post of the doctors themselves.

It looks like i may need to use a custom walker, but i don't seem to understand it.

here is the code i added just as a test: (I created a page called Products2 because my cpt is making products as the archive page. I've tried with just Products as well (I have that page created from before the cpt was made also)

class Walker_rt_Submenu extends Walker_Nav_Menu {
    function end_el(&$output, $item, $depth=0, $args=array()) {
        if( 'Products2' == $item->title ){
            $output .= '<ul><li>Dynamic Subnav</li></ul>';
        }
        $output .= "</li>\n";  
    }
}
wp_nav_menu(
    array(
        'theme_location' => 'primary',
        'walker' => new Walker_rt_Submenu
    )
);

but i get this error:

Call to a member function get_page_permastruct() on null in /home/randomnoises/public_html/wp-includes/link-template.php on line 355 

I'm just using twenty sixteen as the theme for testing.

2 Answers 2

4

Walker_Nav_Menu isn't what you would use for this.

Instead, use the wp_get_nav_menu_items filter. Before we start, you need some way of allowing the code to identify which is the menu item/page that you wish to be the parent of the list of posts. You can do this with a CSS class - give the parent menu item a CSS class in admin panel, let's call it 'doctors-parent-item'.

add_filter( 'wp_get_nav_menu_items', 'my_theme_doctors_menu_filter', 10, 3 );

function my_theme_doctors_menu_filter( $items, $menu, $args ) {
  $child_items = array(); // here, we will add all items for the single posts
  $menu_order = count($items); // this is required, to make sure it doesn't push out other menu items
  $parent_item_id = 0; // we will use this variable to identify the parent menu item

  //First, we loop through all menu items to find the one we want to be the parent of the sub-menu with all the posts.
  foreach ( $items as $item ) {
    if ( in_array('doctors-parent-item', $item->classes) ){
        $parent_item_id = $item->ID;
    }
  }

  if($parent_item_id > 0){

      foreach ( get_posts( 'post_type=rt_doctors&numberposts=-1' ) as $post ) {
        $post->menu_item_parent = $parent_item_id;
        $post->post_type = 'nav_menu_item';
        $post->object = 'custom';
        $post->type = 'custom';
        $post->menu_order = ++$menu_order;
        $post->title = $post->post_title;
        $post->url = get_permalink( $post->ID );
        array_push($child_items, $post);
      }

  }

  return array_merge( $items, $child_items );
}

Your menu will now dispaly all rt_doctors's in a sub menu underneath the menu item that you gave the CSS class 'doctors-parent-item`

3
  • Christmas came early! Thanks Dan. Worked perfectly.
    – rudtek
    Nov 28, 2016 at 17:06
  • Hey Dan, Quick question. when i use this code and save my menu in appearances menu, i get this error: "The given object ID is not that of a menu item." do you know any way to address that?
    – rudtek
    Dec 9, 2016 at 22:13
  • Exactly was I was looking for, thanks. Aug 13, 2019 at 19:19
0

To add a few things to Dan's excellent answer that may not be obvious to some:

  1. You need to turn on the ability to add CSS classes to Wordpress menu item in the "Screen Options" menu in the top right after navigating to Appearance->Menus. Then you will be able to add your chosen class name to the menu item(s) in question.
  2. You can add separate post types to respective menu items by simply creating unique ID vars for each and using conditionals:
add_filter( 'wp_get_nav_menu_items', 'wpse246963_add_cpt_to_menu', 10, 3 );

function wpse246963_add_cpt_to_menu( $items, $menu, $args ) {
  $child_items = array(); // here, we will add all items for the single posts
  $menu_order = count($items); // this is required, to make sure it doesn't push out other menu items
  $doctors_parent_item_id = 0; // we will use this variable to identify the parent menu item
  $locations_parent_item_id = 0; // we will use this variable to identify the parent menu item

  //First, we loop through all menu items to find the one we want to be the parent of the sub-menu with all the posts.
  foreach ( $items as $item ) {
    if ( in_array('doctors-parent-item', $item->classes) ){
        $doctors_parent_item_id = $item->ID;
    } 

    if ( in_array('locations-parent-item', $item->classes) ){
        $locations_parent_item_id = $item->ID;
    }
  }

  if ($doctors_parent_item_id > 0) {

      foreach ( get_posts( 'post_type=rt-doctors&numberposts=-1' ) as $post ) {
        $post->menu_item_parent = $doctors_parent_item_id;
        $post->post_type = 'nav_menu_item';
        $post->object = 'custom';
        $post->type = 'custom';
        $post->menu_order = ++$menu_order;
        $post->title = $post->post_title;
        $post->url = get_permalink( $post->ID );
        array_push($child_items, $post);
      }

  } 

  if ($locations_parent_item_id > 0) {

      foreach ( get_posts( 'post_type=rt-locations&numberposts=-1' ) as $post ) {
        $post->menu_item_parent = $locations_parent_item_id;
        $post->post_type = 'nav_menu_item';
        $post->object = 'custom';
        $post->type = 'custom';
        $post->menu_order = ++$menu_order;
        $post->title = $post->post_title;
        $post->url = get_permalink( $post->ID );
        array_push($child_items, $post);
      }

  }

  return array_merge( $items, $child_items );
}

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.