0

What I want

If a 'category' is added to the menu using the 'menus' tool in WordPress, I want my theme to automatically add a submenu containing links to all of the posts in that category.

<ul>
    <li><a href="link">Page 1</a></li>
    <li><a href="link">Page 2</a></li>
    <li><a href="link">Category X</a>
        //Automatically insert list of links to all posts in Category X
        <ul>
            <li><a href="link">Post 1</a></li>
            <li><a href="link">Post 2</a></li>
        </ul>
    </li>
    <li><a href="link">Category Y</a>
        //Automatically insert list of links to all posts in Category Y
        <ul>
            <li><a href="link">Post 1</a></li>
            <li><a href="link">Post 2</a></li>
            <li><a href="link">Post 3</a></li>
            <li><a href="link">Post 4</a></li>
        </ul>
    </li>
</ul>

What I've tried

In my functions.php file I have created a function and hooked it to walker_nav_menu_start_el action. By passing the $item variable into the function I am able to identify if the current menu item is a category and what category it is. But I'm having trouble creating the list of links with post titles for all posts in that category.

At first I thought I could use the get_posts function and a foreach loop, but because I'm not outputting, I'm just building an output variable to be passed back to the walker_nav_menu_start_el action, it's merely outputting the details of the current post instead of the details for each post it loops through. This is the code I tried:

function hw_submenu( $output, $item ) {
   if( $item->object == 'category' ) {

      $hw_output = '<ul>';
      $subposts = get_posts( array(
         'posts_per_page' => -1,
         'category' => $item->object_id
      ) );
      if ( $subposts ) {
         foreach ( $subposts as $post ) :
            setup_postdata( $post );
            $hw_output .= "<li><a href='" . the_permalink() . "''>" . the_title('', '', false) . "</a></li>";
         endforeach; 
         wp_reset_postdata();
      }

      $output .= $hw_output . "</ul>";
   }    

   return $output;
}
add_action( 'walker_nav_menu_start_el', 'hw_submenu', 10, 2);

And this is an example menu output:

<ul>
    <li><a href="link">Page 1</a></li>
    <li><a href="link">Page 2</a></li>
    <li><a href="link">Category X</a>
        //Automatically inserts list of links to the post I'm currently viewing
        <ul>
            <li><a href="link">Post I'm currently viewing</a></li>
            <li><a href="link">Post I'm currently viewing</a></li>
        </ul>
    </li>
    <li><a href="link">Category Y</a>
        //Automatically inserts list of links to the post I'm currently viewing
        <ul>
            <li><a href="link">Post I'm currently viewing</a></li>
            <li><a href="link">Post I'm currently viewing</a></li>
            <li><a href="link">Post I'm currently viewing</a></li>
            <li><a href="link">Post I'm currently viewing</a></li>
        </ul>
    </li>
</ul>

How can I get each category to list its posts in the menu? Can I do it with this action hook? Or should I be looking at a custom walker?

| improve this question | | | | |
  • I'm not completely sure what is happening, but it looks like your foreach loop is going through the right posts, but the_title() and the_permalink() are taking their cue from the global $post variable. I'd try changing $post in your loop to $subpost and set setup_postdata( $subpost ) to avoid namespace problems. – cjbj Oct 22 '16 at 17:18
  • @cjbj, I tried that, it didn't work. If I echo the values right there and then, it echos them correctly, but if I just concatenate the values into a variable it does the above. – Josh Oct 22 '16 at 17:48
2

I was able to solve my own problem by using get_the_title with the post ID rather than using the_title. Then I decided to try using $subpost->post_title and that worked too. I figure this is better than get_the_title since it doesn't have to "get" the data again, it just uses the data we've already gotten. I have no idea why $subpost->post_title would work and the_title wouldn't. Would love to hear any insights people have about this.

Working code:

function hw_submenu( $output, $item ) {
   if( $item->object == 'category' ) {

      $hw_output = '<ul>';
      $subposts = get_posts( array(
         'posts_per_page' => -1,
         'category' => $item->object_id
      ) );
      if ( $subposts ) {
         foreach ( $subposts as $subpost ) :
            setup_postdata( $subpost );
            $hw_output .= "<li><a href='" . get_the_permalink($subpost->ID) . "''>" . $subpost->post_title . "</a></li>";
         endforeach; 
         wp_reset_postdata();
      }

      $output .= $hw_output . "</ul>";
   }    

   return $output;
}
add_filter( 'walker_nav_menu_start_el', 'hw_submenu', 10, 2);
| improve this answer | | | | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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