List All Posts In WordPress Navigation Menu

You may remember that last week, I’ve been dabbling in the world or WordPress navigation manus hacking around and looking into how to get dynamic Custom Post Type Archives in WordPress Menus.

I got directed towards a related and highly interesting question by @topher1kenobe. Couldn’t stop thinking about an easy and viable solution, so decided to go ahead and code it up.

List All Posts In Nav Menus WordPress

I know WordPress can automatically add pages to a custom menu, but can we have one where it’ll automatically add posts from a custom content type?

The code is completely based on this one here, with a couple of minor changes in the interpretation part of the item filter.

/* take care of the urls */
add_filter( 'wp_get_nav_menu_items', 'cpt_archive_menu_filter', 10, 3 );
function cpt_archive_menu_filter( $items, $menu, $args ) {
  /* alter the URL for cpt-archive objects */

  $menu_order = count($items); /* Offset menu order */

  $child_items = array();
  foreach ( $items as &$item ) {
    if ( $item->object != 'cpt-archive' ) continue;
    $item->url = get_post_type_archive_link( $item->type );
  
    /* retrieve all children */
    foreach ( get_posts( 'post_type='.$item->type.'&numberposts=-1' ) as $post ) {
      /* hydrate with menu-specific information */
      $post->menu_item_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 );
      /* add as a child */
      $child_items []= $post;
    }
  }
  return array_merge( $items, $child_items );
}

Remember, this routing looks for a menu item with the object set to “cpt-archive” (no time to change to a newer more appropriate name as this is quick concept code). That’s it. Again, for the rest of the code refer and a thorough explanation of how it works take a look at Custom Post Type Archives in WordPress Menus.

Since posts are fetched dynamically every time they’re always up to date with the current posts set, meaning when a post is removed or added the menu will change accordingly, which is a huge advantage. Performance may decrease a bit, caching with save_post invalidation should save you time if it becomes an issue.

Even simpler

A more simple solution, where this function can actually be used as a standalone function, without the metaboxes code for the backend can be implemented as well. By creating a simple Custom Link with the Label of “##post##”, for example and then changing the filter to act like so:

/* take care of the urls */
add_filter( 'wp_get_nav_menu_items', 'cpt_archive_menu_filter', 10, 3 );
function cpt_archive_menu_filter( $items, $menu, $args ) {
  /* alter the URL for cpt-archive objects */

  $menu_order = count($items); /* Offset menu order */

  $child_items = array();
  foreach ( $items as &$item ) {
    if ( $item->title != '##post##' ) continue;
    $item->url = '#';
    $item->title = 'All Posts';
  
    foreach ( get_posts( 'post_type=post&numberposts=-1' ) as $post ) {
      $post->menu_item_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 );
      /* add children */
      $child_items []= $post;
    }
  } 
  return array_merge( $items, $child_items );
}

Generics can be implemented using regular expression matching for the title. The URL can be set using the get_post_type_archive_link function for post types with archives. This last version of the snippet can be used as a standalone function, just make sure you label your Custom Links appropriately.

Solutions, improvements, ideas, thoughts always welcome. I’m sure there’s a faster way to add all posts as menu items, isn’t there?