Advertisement

Fork Your WordPress Taxonomy The Right Way

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

Custom post types and custom taxonomies have been one of the most exciting new tricks for WordPress developers since its release in WordPress version 2.9. However, using them can become a headache for even the most experienced WordPress developers without knowing a few extra tricks. In this article I will be using Twenty Eleven as an example to give you tips and tricks to create your own taxonomy the right way.

Here's the outline of what we're going to cover today. We will use a hypothetical scenario to illustrate some important workarounds; You might not have the exact same situation, but the workaround should apply to lots of taxonomy specific headaches like 404's, pagination problems, and non-searchable taxonomy listings.

  • Step 1: Creating Your Custom Post Type
  • Step 2: Creating Our Product Taxonomies
  • Step 3: Init(iate) The Post Type And Taxonomies
  • Step 4: Creating the Taxonomy File
  • Step 5: Call The Query Using pre_get_posts()

Step 1 Creating Your Custom Post Type

For our example, we will create a custom post type for our hypothetical product listing; Fire up your editor and create a new file called, post-type.php; Then save the file into a new folder called "includes". Here's the code that we'll be adding to that file:

function product_listing() {
	$labels = array(
		'name' => __('Property Listings', 'post type general name'),
		'singular_name' => __('Property Listing', 'post type singular name'),
		'add_new' => _x('Add New', 'Listing'),
		'add_new_item' => __('Add New Listing'),
		'edit_item' => __('Edit Listing'),
		'new_item' => __('New Listing'),
		'all_items' => __('All Listings'),
		'view_item' => __('View Listing'),
		'search_items' => __('Search Listings'),
		'not_found' =>  __('No Listings found'),
		'not_found_in_trash' => __('No Listings found in Trash'), 
		'parent_item_colon' => '',
		'menu_name' => 'Listings'
	);
	
	$args = array(
		'labels' => $labels,
		'public' => true,
		'publicly_queryable' => true,
		'show_ui' => true,
		'show_in_menu' => true,
		'query_var' => true,
		'menu_position' => 4,
		'capability_type' => 'post',
		'hierarchical' => false,
		'has_archive' => true,
		'rewrite' => array( 'slug' => 'listing', 'with_front' => false ), // Important!
		'supports' => array('title', 'editor', 'thumbnail', 'author'),
		'taxonomies' => array( 'product_feature', 'product_category'),
	);
	register_post_type( 'listing' , $args );
	}

What we did there: We created a function for a product that will be added to the init action in WordPress later. $labelsis an array containing all the information we need to create a post type. Next below the label is an array of arguments ($args). The important thing to remember is the 'rewrite' part where we define our slug that will be used as a permalink, for this example we'll use 'listing'. At the bottom we have taxonomy arguments that contain our product_feature and product_category.

One common technique that lots of WordPress developers use is to exclude the custom post type from search results with the argument of 'exclude_from_search' => true. We're not going to do that here because we will need an archive for the post type taxonomy so people can browse for our product. For me, the rule here is don't use 'exclude_from_search' => true when registering your public post type; If you need to display it in an archive page, you want it included in searches. If we wanted to exclude it from blog search, then we have to exclude it with a custom function we'll go over later.


Step 2 Creating Our Product Taxonomies

As you can see from the code in step 1 we will need to register two custom taxonomies for our product listing. First will be product feature and second will be product category. So let's get started.

We need to add a new function after the product_listing function. We will call this function product_taxonomies. The full taxonomies code can be seen below:

/**
 * Add custom taxonomies for product listing
 */

function product_taxonomies() {

	$labels = array(
		'name' => __( 'Product Features', 'taxonomy general name' ),
		'singular_name' => __( 'Product Feature', 'taxonomy singular name' ),
		'search_items' =>  __( 'Search Product Features' ),
		'all_items' => __( 'All Product Features' ),
		'parent_item' => __( 'Parent Product Feature' ),
		'parent_item_colon' => __( 'Parent Product Feature:' ),
		'edit_item' => __( 'Edit Product Feature' ), 
		'update_item' => __( 'Update Product Feature' ),
		'add_new_item' => __( 'Add New Product Feature' ),
		'new_item_name' => __( 'New Product Feature' ),
		'menu_name' => __( 'Product Features' )
	); 	
	
	register_taxonomy( 'product_feature', array('listing'), array (
					'labels' => $labels,
					'hierarchical' =>true,
					'show_ui' => true,
					'rewrite' => array( 'slug' => 'product_feature'),
					'query_var' => true,
					'show_in_nav_menus' => true,
					'public' => true
			));
			
	$labels = array (
		'name' => __( 'Product Categories', 'taxonomy general name' ),
		'singluar_name' => __( 'Product Category', 'taxonomy singular name' ),
		'search_items' => __( 'Search Product Category' ),
		'all_items' => __('All Product Categories'),
		'parent_item' => __('Parent Product Category'),
		'parent_item_colon' => __('Parent Product Category:'),
		'edit_item' => __('Edit Product Category'),
		'update_item' => __('Update Product Category'),
		'add_new_item' => __('Add New Product Category'),
		'new_item_name' => __('New Product Category'),
		'menu_name' => __( 'Product Categories' )
	);
	
	register_taxonomy( 'product_category', array('listing'), array (
					'labels' => $labels,
					'hierarchical' =>true,
					'show_ui' => true,
					'rewrite' => array( 'slug' => 'product_category'),
					'query_var' => true,
					'show_in_nav_menus' => true,
					'public' => true
			));
}

The product_taxonomies function, shown above, registers two custom taxonomies for us. The important thing to note here is that the 'rewrite' argument is the same as the post type rewrite we created a custom slug for in step 1. That will become the url to our taxonomy archives. Next, we also created a public argument so people can browse our taxonomies.


Step 3 Init(iate) The Post Type And Taxonomies

Now let's use the add_actions filter to let WordPress execute our functions.

    add_action('init', 'product_listing', 0);
    add_action('init', 'product_taxonomies', 10);

Pay attention to the very last argument we pass in to the add_action filter. We register the product listing post type with the priority of 0 and the custom taxonomies for the post type itself as 10. This is a crucial thing to note: don't register a custom taxonomy with a priority lower than priority for registering its associated post type. This is the main issue that might cause you a headache; if your taxonomy's priority is lower than your post type priority, things will break since the default of "10" will be used for the custom post type if you don't specify otherwise.

Now let's include it in the Twenty Eleven functions. Open up the functions.php and scroll to the last line and add this code below after the Twenty Eleven body classes functions so the theme can execute our post type code.

	function twentyeleven_body_classes( $classes ) {

	if ( ! is_multi_author() ) {
		$classes[] = 'single-author';
	}

	if ( is_singular() && ! is_home() && ! is_page_template( 'showcase.php' ) && ! is_page_template( 'sidebar-page.php' ) )
		$classes[] = 'singular';

	return $classes;
    }
    add_filter( 'body_class', 'twentyeleven_body_classes' );
  
  // this is our include files  
    require_once('/includes/post-types.php');

Now open up your wp-admin and activate the Twenty Eleven theme, if you haven't already, and you'll see your custom post type and taxonomy has been added below the posts menu.


Step 4 Creating the Taxonomy File

Here is the important step if you want to make your taxonomy browse-able. First, copy the Twenty Eleven archive.php file and rename it to taxonomy.php. What we want to do with our "taxonomies" is very similar to what WordPress does with "archive.php" and "category.php file"; That's why there's no need to run a custom query on a taxonomy/category/post_tag archive page.

Note: What I see most WordPress theme developers doing nowadays is this; They add their custom query directly to the taxonomy file, which often breaks the pagination. How do we work around this? Easy my friends, I will tell you the secret to make your custom query without adding it directly to the taxonomy.php file:

Let's fill the taxonomy file with our code. Go to the line below the get_header() and enter the code below.

$term = get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) );

The get_term_by function will return the value of the slug we specify. Remember the 'rewrite' => array( 'slug' => 'property_feature')? This is the value of the slug that will be returned. We will use it to echo out the slug as the taxonomy title. Now go to the line where the h1 tag with class page-title and write down the code to call the term below.

echo $term->name;

Test The Code

Head back to your admin panel then go to the product features and product categories. We will create some features and categories for our products. I'll call mine feature 1, 2, 3 and category 1, 2, 3.

Next we create a new product listing. Below the published field you'll see your feature and category custom taxonomy that you just created. Don't forget to check them. I created 3 product listings, and so I check the category and feature for each of them.

To see how your taxonomy is working, go to the Product Feature or Product Category and scroll to the category you've created and click the view button as seen in the picture above. If you're pointed to the 404 error page, head into your wp-admin, go to Settings -> Permalinks and hit save to make WordPress flush the rewrite rules. If you got something similar to the picture above then your taxonomy already works.

Notice the title of the taxonomy below the menu is the term name we call with the echo.

Let's create a menu to have easier access to the taxonomy archives. Go to the menu tab and create two menus, we will call the first menu "Browse by Features" and the second menu "Browse by Categories". Since we have set the parameter 'show_in_nav_menus' => true, this will bring our custom taxonomy to the menu options. Choose all features for the "Browse by Features" menu and categories for the "Browse by Categories" menu.

Next head to the widgets area and let's add the menus we just created to the custom menu widget.

Go to your homepage and hit refresh, you'll see your custom taxonomy widget. Now create more placeholder product listings so we can have the pagination appear. I created around 8 listings and assigned them to the feature 1. Then set the "Blog pages show at most" under Settings -> Reading to show only 3 posts and go to the taxonomy archives page to test whether the pagination works or not. Whallah! The pagination works fine in WordPress default navigation.

Let's test it with the famous WordPress pagination plugin, the wp-pagenavi plugin. After you installed the plugin, go to your taxonomy.php and find the line with twentyeleven_content_nav( 'nav-above' ); and twentyeleven_content_nav( 'nav-below' ); replace the code with the wp_pagenavi() function as seen in the code below.

	
// twentyeleven_content_nav( 'nav-above' ); 
wp_pagenavi();

// twentyeleven_content_nav( 'nav-below' ); 
wp_pagenavi();

Now hit refresh and you can see the wp_pagenavi pagination is working if you're following my instructions.

This causes no more headaches on pagination while permalink is not the default.
The next step is creating our custom query to let the taxonomy archives file show what we ask for.


Step 5 Call The Query Using pre_get_posts()

The pre_get_posts() filter is on of the most awesome filters (in my opinion!) It lets us create a post filter before the post is called. Most WordPress theme developers rarely to use this powerful weapon... All right dudes! Let's load our secret weapon with the code. Fire up your editor and open the Twenty Eleven functions.php file.

We will load it with two functions in the end of the functions.php below our required_once function, the first is to call our custom query on the taxonomy and the second is to exclude our listing from search. Here is the code for the first function.

add_action('pre_get_posts', 'add_custom_taxonomy_query');
function add_custom_taxonomy_query(&$query)
{
    if (!is_admin() &&
        is_tax('product_feature') || is_tax('product_category')) {
        $query->set('orderby', 'date');
        $query->set('order', 'ASC');
		$query->set('posts_per_page', '1');
    }
}

The code above will execute the query for both product_feature and product_category taxonomies to order by date and order ascending and also posts per page is set to 1. Go to your taxonomy archive page then hit refresh. Yay! You've just seen our secret weapon being executed. Now test your search form, I type 'product' in the search form and the result is my product still appearing in the result page. Okay! Let's make it invisible in the search result.

	function search_filter($query) {
	if ( !is_admin() ) {
	    if ($query->is_search) {
			$query->set('post_type', array('post', 'page'));
	    }
    }
    return $query;
}
add_filter('pre_get_posts', 'search_filter');

Add the function above in to the functions.php file then go to your site and try to search your product. Yap! The product listing has now become invisible. To include in the search just add 'listing' in to the query set array after 'page'.

	$query->set('post_type', array('post', 'page', 'listing'));

Conclusion

All right, this is the end of the tutorial! I've told you how to handle the pagination 404 error page and let you know three important rules to create your taxonomy.php file which are:

  1. Don't use 'exclude_from_search' => true when registering your public post type, if you need to display it in an archive page.
  2. Don't register custom taxonomy with priority lower than priority for registering its associated post type.
  3. No need to run custom query on taxonomy/category/post_tag archive page, as that will break your pagination.

I also loaded you with my pre_get_posts secret weapon to handle all the queries without ruining the pagination and other things that can cause headaches. The pre_get_posts method itself can still be extended with your own theme options. Enjoy the course!

Advertisement