Creating navigation for WordPress custom post types based on taxonomy

Custom post types in WordPress have really elevated the software from being primarily a blogging platform and into a full-fledged content management system. WordPress has several built-in functions that let you list pages and categories so that users can navigate your site, but for custom post types sometimes you have to dig a bit deeper than wp_list_pages() or wp_list_categories().

I’ve developed a nifty little function that helps you to output a list of the custom taxonomies attached to any post currently being viewed. The full function is posted at the end of this article, but first let’s dig into what we’re doing here.

function manifest_show_post_type_taxonomies() {
	global $post;
	
	$post_type = get_post_type($post->ID);
	$post_type_obj = get_post_type_object( $post_type );

We’ll name our function ‘manifest_show_post_type_taxonomies’. Why the manifest prefix? I generally namespace all my functions to ensure that they don’t conflict with any other plugins being used. It’s my business name so it’s easy for me to remember. Change it if you like.

We then globalize the $post variable so that we can access all of the information attached to this specific post. Next we get the post type slug and assign it to the $post_type variable. Once we have that information we can use the get_post_type_object() function to create an object containing all of the information for the post type we want to work with.

//IS THE POST TYPE HIERARCHICAL?
	$hierarchical = $post_type_obj->hierarchical;
	if ( !$hierarchical ) {
	//We'll do something here...
	}

Now that we have some information about the custom post type, we need to determine if it’s hierarchical or not. Contained within the post type object (remember we assigned this to the $post_type_obj variable) is a boolean (true/false) that tells us whether it’s hierarchical or not.

if ( !$hierarchical ) {
	echo '<li id="pageTree" class="widget treeList">';
		echo '<h3 class="widget-title title-banner">'.$post_type_obj->labels->name.'</h3>';
		echo '<ul class="widget-content">';
			
				$orderby = ( $post_type_object->capability_type == 'page' ? 'menu_order' : 'name');
			
				$menuposts = get_posts('post_type='.$post_type.'&orderby='.$orderby.'&order=ASC&posts_per_page=-1');
				
				foreach ( $menuposts as $item ) {
				setup_postdata($menuposts);
					
					$itemclass = '';
					if ( $item->ID == $wp_query->post->ID ) $itemclass = 'current_page_item';
					
					echo '<li class="'.$itemclass.'">';
						echo '<a href="'.get_permalink($item->ID).'">'.get_the_title($item->ID).'</a>';
					echo '</li>';
				}
		
		echo '</ul>'; // .widget-content
	echo '</li>';// #pageTree
	$widget_count++;
	}// !$hierarchical

We’ll start by writing our code for the non-hierarchical post types. The function assumes that you’re calling it within an <ul> or <ol> list. So we’ll only need to worry about generating each <li> list item.

Our list needs a name, so the first element will be an <h3>. We pull the name from the labels array contained within the post type object.

Each of the links themselves will be a nested unordered list so that it’s easy to add CSS styles down the road.

Next, we’ll be querying the database to get a list of the posts for the custom post type. But we need to tell the function how we want them ordered. We do that here:

$orderby = ( $post_type_object->capability_type == 'page' ? 'menu_order' : 'name');

For those not familiar with PHP’s ternary operator, here’s what we’re saying. If the capability_type is equal to ‘page’ then set the order to ‘menu_order’, otherwise set it to ‘name’.

Now we’re ready to create the query itself and assign it to the $menuposts variable.

$menuposts = get_posts('post_type='.$post_type.'&orderby='.$orderby.'&order=ASC&posts_per_page=-1');
				
				foreach ( $menuposts as $item ) {
				setup_postdata($menuposts);
					
					$itemclass = '';
					if ( $item->ID == $wp_query->post->ID ) $itemclass = 'current_page_item';
					
					echo '<li class="'.$itemclass.'">';
						echo '<a href="'.get_permalink($item->ID).'">'.get_the_title($item->ID).'</a>';
					echo '</li>';
				}

Now we have a list of all of the posts for the custom post type. The posts are ordered by menu ID (if they have page capabilities) otherwise they’re ordered by name. They are sorted in ascending order.

We’ll use a foreach loop to work through each of the posts. The get_posts() function in WordPress doesn’t allow us to access the full information (like the post ID) attached to a post unless we use the setup_postdata() function. We’ll need this information so for each loop iteration, we’ll call that function.

The first if statement here, tells the script that if the current item’s ID has the same ID as the page or post the user is currently viewing, then add a CSS class of ‘current_page_item’. This makes it easy to style later and has the same class name as many similar core WordPress functions.

Finally, we output the link and link text for the post.

Last but not least, we can close our original if ( !$hierarchical ) statement and move on to posts that are hierarchical.

function manifest_show_post_type_taxonomies() {
	global $post;
	
	$post_type = get_post_type($post->ID);
	$post_type_obj = get_post_type_object( $post_type );
	
	//IS THE POST TYPE HIERARCHICAL?
	$hierarchical = $post_type_obj->hierarchical;
	if ( !$hierarchical ) {
	echo '<li id="pageTree" class="widget treeList">';
		echo '<h3 class="widget-title title-banner">'.$post_type_obj->labels->name.'</h3>';
		echo '<ul class="widget-content">';
			
				$orderby = ( $post_type_object->capability_type == 'page' ? 'menu_order' : 'name');
			
				$menuposts = get_posts('post_type='.$post_type.'&orderby='.$orderby.'&order=ASC&posts_per_page=-1');
				
				foreach ( $menuposts as $item ) {
				setup_postdata($menuposts);
					
					$itemclass = '';
					if ( $item->ID == $wp_query->post->ID ) $itemclass = 'current_page_item';
					
					echo '<li class="'.$itemclass.'">';
						echo '<a href="'.get_permalink($item->ID).'">'.get_the_title($item->ID).'</a>';
					echo '</li>';
				}
		
		echo '</ul>'; // .widget-content
	echo '</li>';// #pageTree
	$widget_count++;
	}// !$hierarchical
	else {
		//POST TYPE IS HIERARCHICAL
		$args = array(
			'public' => true,
			'_builtin' => false,
		);
		
		$taxonomies = get_taxonomies($args, 'objects');
		
		foreach ($taxonomies as $taxonomy) {
			
			//CHECK IF THE TAXONOMY IS HIERARCHICAL
			if ( is_taxonomy_hierarchical($taxonomy->name) ) {
				$args = array(
					'orderby' => 'name',
					'order' => 'ASC',
					'show_count' => 0,
					'hide_empty' => 1,
					'hierarchical' => true,
					'title_li' => '',
					'taxonomy' => $taxonomy->name
				);
				
				echo '<li id="catTree" class="widget treeList">';
					echo '<h3 class="widget-title title-banner">'.$taxonomy->label.'</h3>';
					echo '<ul class="widget-content">';
						wp_list_categories($args);
					echo '</ul>';
				echo '</li>';
				$widget_count++;
			} // is_taxonomy_hierarchical()
		}// foreach
	} // else
}

2 thoughts on “Creating navigation for WordPress custom post types based on taxonomy”

  1. Pretty cool, I have just come across this same problem today. It still seems a real work around trying to get navigation working using custom post types and taxonomies. Doesn’t seem quite there yet to me, hopefully this will be an improvement in the next update. Very useful though thanks.

Leave a Reply

Your email address will not be published. Required fields are marked *