Adding Custom Post Types to the navigation menu in WordPress (3.1)
30th
March
2011
Posted by James Beattie - under News 7 Comments
Custom Post Types in WordPress are awesome! However, making them appear in your main navigation menu is difficult (and until now, some might have said impossible without hacking the WordPress core files). So after days of stumbling, reverse engineering and swearing, I present a solution which works nicely and involves just adding a filter to your theme functions file.
The following function will automatically grab all your custom post types (which are set to hierarchical= true) and add them to the main nav. You should ensure your custom post type has has_archive set to true also, as the archive page will be the main nav item, with all items in the custom post type appearing as children of this main item.
The way this function is written, it writes a Home link, it then loops through the custom post types adding the archive page as the main item and the items as children pages of the archive page. Then it loops through and writes the normal page menu, so your menu will end up looking something like this:
HOME | POST TYPE 1 | POST TYPE 2 | PAGE 1 | PAGE 2 | PAGE 3
This is by no means a complete solution, but hopefully it’s a starting point and will save someone the days of work it took me to come up with it.
Add the following code to your function.php file in your theme folder:
function custom_page_menu($menu, $args) {
// get supplied args
$list_args = $args;
// Overide some menu settings
$list_args['echo'] = false;
$list_args['title_li'] = '';
$list_args['show_home'] = false;
$list_args['exclude'] = 4; // excluding the homepage as I am manually adding it to the start below
// get the current page object as we will need to refer to it when setting current items below
global $wp_query;
$current_page = $wp_query->get_queried_object();
// Show Home item at the start of the menu
$menu .= '<li ' . $class . '><a href="' . home_url( '/' ) . '" title="' . esc_attr(__('Home')) . '">' . $args['link_before'] . __('Home') . $args['link_after'] . '</a></li>';
// Loop through the custom post types and add them to the menu
foreach(get_post_types(array(
'public' => true,
'_builtin' => false,
'hierarchical' => true
)) as $pt) {
$obj_pt = get_post_type_object($pt);
$list_args['post_type'] = $pt;
$menu .= '<li><a href="/' . $obj_pt->rewrite['slug'] . '/">' . $obj_pt->labels->name . '</a><ul>';
// little bit of hacking here to force wp_list_pages to add current classes to
// active pages in the generated navs. It's messy, but it means not hacing to
// hack the WordPress core files
$original_is_posts_page = $wp_query->is_posts_page;
$wp_query->is_posts_page = true;
$menu .= wp_list_pages($list_args);
$wp_query->is_posts_page = $original_is_posts_page;
$menu .= '</ul></li>';
}
// Now add the normal page collection which belongs in the nav
$list_args['post_type'] = 'page';
$menu .= wp_list_pages($list_args) ;
// glue the menu together and send back
if ( $menu )
$menu = '<ul>' . $menu . '</ul>';
$menu = '<div>' . $menu . "</div>\n";
return $menu;
}
add_filter( 'wp_page_menu', 'custom_page_menu' ,10,2 );
7 Comments
theshae says:
April 7th, 2011
Awesome! I have been looking everywhere for this!
Thanks!
The Custom Post Type shows in the navigation with the posts as child items. The child items link to single post template, but the custom post type is not linking to the archive page. Hierarchical and Has Archive is set to true. Any idea why?
theshae says:
April 7th, 2011
Any idea how to remove the that gets added in?
I’ve tried
false )); ?>
with no luck.
Andrew says:
April 7th, 2011
Looks great but didn’t work for me. Could be due to the fact I’m running the Genesis Framework though.
I have been struggling with adding the custom post type’s archive page to the main wordpress navigation for days. Can’t work out why this isn’t included in WordPress by default.
I noticed there’s a ticket open for it so hopefully it’s included soon!
http://core.trac.wordpress.org/ticket/16075
theshae says:
April 12th, 2011
Ok, I am getting closer. I have this working properly but it needs to only show the custom post type if it has posts.
Any ideas?
trox says:
September 20th, 2011
nice try, but buggy as can be
- conceptional buggy, since it does not deal with sort order of menu items *AT ALL*
- coding buggy, probably because of the home_url( ‘/’ ) thingy … never used this before and I guess it normally just says “localhost” which is of no use *AT ALL*
sigh … such a good concept, such a sloppy implementation
trox says:
September 20th, 2011
I’d like to retract my remark about home_url, but point out that $menu .= ‘rewrite['slug'] . ‘/”>’ creates a localhost/… link under certain circumstances
James Beattie says:
September 20th, 2011
@Trox – I think you’re being a little harsh. You’re correct that is not a complete solution, nor did I pass it off as one. However it is a good starting point that should suit most uses with a little more refinement. I would certainly be interested to see your more complete, bug-free, conceptually improved solution in action!
Leave a Comment