A Guide to WordPress Custom Post Types: Taxonomies, Admin Columns, Filters and Archives

A Guide to WordPress Custom Post Types: Taxonomies, Admin Columns, Filters and Archives

Tutorial Details
  • Program: WordPress
    Version: 3.3 and above
    Difficulty: Intermediate
    Estimated Completion Time: 30 minutes to 1 hour
This entry is part 2 of 2 in the series A Guide to WordPress Custom Post Type

Before diving into this tutorial please go through my first tutorial on Custom Post Types, where I have explained some of the important aspects of CPTs (Custom Post Types). In this tutorial we shall explore more about this wonderful feature in WordPress.

This tutorial covers how to create custom taxonomies, admin columns, taxonomy filters and an archive page for your CPT. So let’s begin.


Custom Taxonomy for Custom Post Types

Taxonomies are a great way to group things together and help us to search posts belonging to a specific group. In WordPress we generally use Categories and Tags as taxonomies. The steps given below explain how to create custom taxonomies for your CPT.

Step 1 : Register Custom Function

Open your plugin file, in our case Movie-Reviews.php and add the following code to register the custom function.

add_action( 'init', 'create_my_taxonomies', 0 );

Step 2 : Implementation of Custom Function and Registering Custom Taxonomy

function create_my_taxonomies() {
	register_taxonomy(
		'movie_reviews_movie_genre',
		'movie_reviews',
		array(
			'labels' => array(
				'name' => 'Movie Genre',
				'add_new_item' => 'Add New Movie Genre',
				'new_item_name' => "New Movie Type Genre"
			),
			'show_ui' => true,
			'show_tagcloud' => false,
			'hierarchical' => true
		)
	);
}

Here the register_taxonomy function does all the hard work of creating a custom taxonomy (in our case a category) with the name ‘movie_reviews_movie_genre‘ for the custom post type ‘movie_reviews‘. The labels define the different strings used in the admin section of the taxonomy.

  • 'show_ui' => true is used to make the taxonomy editor visible in the dashboard.
  • 'show_tagcloud' => false decode whether the tag cloud should be visible. In our case it’s disabled.
  • 'hierarchical' => true decodes the format of the custom taxonomy.

Note: 'hierarchical' => false converts the categories into tags.

Step 3 : Displaying Custom Taxonomies

After saving the Movie-Reviews.php file, open your custom template file, in our case single-movie_reviews.php and add the following highlighted code to make the categories visible in our posts.


<?php
 /*Template Name: New Template
 */
get_header(); ?>
<div id="primary">
    <div id="content" role="main">
     <?php $mypost = array( 'post_type' => 'movie_reviews', );
      $loop = new WP_Query( $mypost ); ?>
	  <!-- Cycle through all posts -->
      <?php while ( $loop->have_posts() ) : $loop->the_post();?>
          <article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
	          <header class="entry-header">
                <!-- Display featured image in top-aligned floating div -->
                 <div style="float: top; margin: 10px">
                    <?php the_post_thumbnail( array( 100, 100 ) ); ?>
                 </div>
				 <!-- Display Title and Author Name -->
				 <strong>Title: </strong><?php the_title(); ?><br />
                 <strong>Director: </strong>
                 <?php echo esc_html( get_post_meta( get_the_ID(), 'movie_director', true ) ); ?>
                 <br />
				<strong>Genre: </strong>
                <?php  
				the_terms( $post->ID, 'movie_reviews_movie_genre' ,  ' ' );
                    ?>
<br />
                 <!-- Display yellow stars based on rating -->
                <strong>Rating: </strong>
                <?php
                $nb_stars = intval( get_post_meta( get_the_ID(), 'movie_rating', true ) );
                for ( $star_counter = 1; $star_counter <= 5; $star_counter++ ) {
                    if ( $star_counter <= $nb_stars ) {
                        echo '<img src="' . plugins_url( 'Movie-Reviews/images/icon.png' ) . '" />';
                    } else {
                        echo '<img src="' . plugins_url( 'Movie-Reviews/images/grey.png' ). '" />';
                    }
                }
                ?>
	          </header>
			  <!-- Display movie review contents -->
	          <div class="entry-content">
	               <?php the_content(); ?>
              </div>
	          <hr/>
	     </article>
   <?php endwhile;  ?>
   </div>
</div>
<?php wp_reset_query(); ?>
<?php get_footer(); ?>
             

Step 4 : The Result

Here we have added a custom taxonomy ‘Movie Genre’ in our movie reviews CPT. Now we will be able to add new categories from the admin panel and assign each of them to our CPT.


Displaying Additional Columns

In the WordPress admin CPT listing page, by default two columns exist – Date and Comments – through which we can sort those CPT items. In order to add extra columns and sorting please follow the steps given below.

Step 1 : Register Function

Open the plugin file Movie-Reviews.php and add the following line of code to register a function to be called when the Movie Reviews listing page is being prepared.

add_filter( 'manage_edit-movie_reviews_columns', 'my_columns' );

Here we have used the variable filter manage_edit-(Custom_Post_Type)_columns, which passes the column list of the CPT as an argument to the function.

Step 2 : Implementation of the Function

function my_columns( $columns ) {
	$columns['movie_reviews_director'] = 'Director';
	$columns['movie_reviews_rating'] = 'Rating';
	unset( $columns['comments'] );
	return $columns;
}

Here we have added two columns: Director and Rating in the admin panel of the CPT and also deleted the Comments column from the listing.

Step 3 : Populating the Columns

Register a function to populate the columns.

add_action( 'manage_posts_custom_column', 'populate_columns' );

Step 4 : Implementation

function populate_columns( $column ) {
	if ( 'movie_reviews_director' == $column ) {
		$movie_director = esc_html( get_post_meta( get_the_ID(), 'movie_director', true ) );
		echo $movie_director;
	}
	elseif ( 'movie_reviews_rating' == $column ) {
		$movie_rating = get_post_meta( get_the_ID(), 'movie_rating', true );
		echo $movie_rating . ' stars';
	}
}

Here since the function gets executed when any of the CPT columns is rendered, it checks for the currently requested columns before echoing them. We have used the get_the_ID() function to retrieve the index of the current row and then in turn have used the get_post_meta to retrieve the data in the column.

Step 5 : Register Columns as Sortable

Now let us register a function to be called when WordPress identifies sortable columns in CPT.

add_filter( 'manage_edit-movie_reviews_sortable_columns', 'sort_me' );

Step 6 : Implementation

function sort_me( $columns ) {
	$columns['movie_reviews_director'] = 'movie_reviews_director';
	$columns['movie_reviews_rating'] = 'movie_reviews_rating';

	return $columns;
}

This function identifies two columns to make them sortable and then returns the array. But our work hasn’t finished yet.

Step 7 : Order by Custom Field

add_filter( 'request', 'column_ordering' );

add_filter( 'request', 'column_orderby' );

function column_orderby ( $vars ) {
	if ( !is_admin() )
		return $vars;
	if ( isset( $vars['orderby'] ) && 'movie_reviews_director' == $vars['orderby'] ) {
		$vars = array_merge( $vars, array( 'meta_key' => 'movie_director', 'orderby' => 'meta_value' ) );
	}
	elseif ( isset( $vars['orderby'] ) && 'movie_reviews_rating' == $vars['orderby'] ) {
		$vars = array_merge( $vars, array( 'meta_key' => 'movie_rating', 'orderby' => 'meta_value_num' ) );
	}
	return $vars;
}

The above function is associated with the request filter and adds elements to the query array, based on the variables in the query URL. Actually WordPress doesn’t know how to order by the fields ‘Movie Director’ or ‘Movie Rating’, so we need to teach WordPress how to do that through this function.

We have successfully added two sortable columns in the admin section.


Creating Filters With Custom Taxonomy

Here we shall see how custom taxonomies (in this case, categories) can be used as an additional filter in the CPT listing page in the WordPress admin, so that administrators can display CPT elements belonging to a specific category.

Step 1 : Register the Function

Open your plugin file and add the following code to register a function to be called when WordPress is preparing to display the filter drop down list.

add_action( 'restrict_manage_posts', 'my_filter_list' );

Step 2 : Implementation of the Function

function my_filter_list() {
	$screen = get_current_screen();
	global $wp_query;
	if ( $screen->post_type == 'movie_reviews' ) {
		wp_dropdown_categories( array(
			'show_option_all' => 'Show All Movie Genres',
			'taxonomy' => 'movie_reviews_movie_genre',
			'name' => 'movie_reviews_movie_genre',
			'orderby' => 'name',
			'selected' => ( isset( $wp_query->query['movie_reviews_movie_genre'] ) ? $wp_query->query['movie_reviews_movie_genre'] : '' ),
			'hierarchical' => false,
			'depth' => 3,
			'show_count' => false,
			'hide_empty' => true,
		) );
	}
}

Here we have used a global variable to know the type of post that is being displayed and also used a post query variable to check if there is already an existing filter and accordingly set the filter. The wp_dropdown_categories function is used to display all the taxonomies registered with Movie Genres. The ‘orderby‘, ‘show_count‘, ‘hide_empty‘, ‘depth‘ etc. are different arguments specifying sorting, show items count on each category, hide non associated categories, determine the maximum depth to be shown for the hierarchical categories respectively.

Step 3 : Display Filtered Results

Now after the filter drop down list has been prepared, we shall write some code to display the filtered results. Register a function to be called when the post display query is prepared.

add_filter( 'parse_query','perform_filtering' );

Step 4 : Implementation of the Display Function

function perform_filtering( $query ) {
	$qv = &$query->query_vars;
	if ( ( $qv['movie_reviews_movie_genre'] ) && is_numeric( $qv['movie_reviews_movie_genre'] ) ) {
		$term = get_term_by( 'id', $qv['movie_reviews_movie_genre'], 'movie_reviews_movie_genre' );
		$qv['movie_reviews_movie_genre'] = $term->slug;
	}
}

The perform_filtering function receives the current WordPress post query object and then commences by getting a pointer to the query variables stored inside the query object. Then it verifies if a Movie Genre is a part of the query variables and then executes the query.

Now you will be able to use the filter to display the Movies by their Genres.


Last but Not the Least: Create an Archive Page

As we have created a custom template for our CPT, we can also create a custom archive page overriding the default archive template.

Step 1 : Adding a Fallback to the Archive Template

Open the plugin file Movie-Reviews.php and the add the highlighted code in the include_template_function function.

function include_template_function( $template_path ) {
	if ( get_post_type() == 'movie_reviews' ) {
		if ( is_single() ) {
			// checks if the file exists in the theme first,
			// otherwise serve the file from the plugin
			if ( $theme_file = locate_template( array ( 'single-movie_reviews.php' ) ) ) {
				$template_path = $theme_file;
			} else {
				$template_path = plugin_dir_path( __FILE__ ) . '/single-movie_reviews.php';
			}
		}
		elseif ( is_archive() ) {
			if ( $theme_file = locate_template( array ( 'archive-movie_reviews.php' ) ) ) {
				$template_path = $theme_file;
			} else { $template_path = plugin_dir_path( __FILE__ ) . '/archive-movie_reviews.php';

			}
		}
	}
	return $template_path;
}

WordPress searches through the theme directory for an archive template file before using the default one. This function checks if the user has provided an archive template in the theme directory else it looks for the file in the plugin’s folder.

Step 2 : Create the Archive Template

Save and Close the plugin file and then create a new file called archive-movie_reviews.php and add the following code into it.

<?php get_header(); ?>
<section id="primary">
	<div id="content" role="main" style="width: 70%">
	<?php if ( have_posts() ) : ?>
		<header class="page-header">
			<h1 class="page-title">Movie Reviews</h1>
		</header>
		<table>
			<!-- Display table headers -->
			<tr>
				<th style="width: 200px"><strong>Title</strong></th>
				<th><strong>Director</strong></th>
			</tr>
			<!-- Start the Loop -->
			<?php while ( have_posts() ) : the_post(); ?>
				<!-- Display review title and author -->
				<tr>
					<td><a href="<?php the_permalink(); ?>">
					<?php the_title(); ?></a></td>
					<td><?php echo esc_html( get_post_meta( get_the_ID(), 'movie_director', true ) ); ?></td>
				</tr>
			<?php endwhile; ?>

			<!-- Display page navigation -->

		</table>
		<?php global $wp_query;
		if ( isset( $wp_query->max_num_pages ) && $wp_query->max_num_pages > 1 ) { ?>
			<nav id="<?php echo $nav_id; ?>">
				<div class="nav-previous"><?php next_posts_link( '<span class="meta-nav">&larr;</span> Older reviews'); ?></div>
				<div class="nav-next"><?php previous_posts_link( 'Newer reviews <span class= "meta-nav">&rarr;</span>' ); ?></div>
			</nav>
		<?php };
	endif; ?>
	</div>
</section>
<br /><br />
<?php get_footer(); ?>

Here we have used the loop to cycle through the post entries and then display them using a table layout. We have also defined a navigation menu if there are more items than the maximum number configured under WordPress Settings. Navigation menus are displayed using the next_post_links and previous_post_links functions.

We have used the global wp_query object containing the data about the currently executed query, in order to render the page contents. The get_post_meta function has been used to retrieve custom field data.

Step 3 : The Result

Save the file and check the archive page for the Movie Reviews archive list.


Here we come to the end of this tutorial. I hope you have been able to grasp the significance of Custom Post Types. There is more to explore, just play around with it.

Thanks for reading and feel free to give your feedback.


Other parts in this series:A Guide to WordPress Custom Post Types: Creation, Display and Meta Boxes
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://wpsites.net/ Brad Dalton

    Just finished writing a post about exactly the same topic. Maybe i’ll add more to it now before i publish it.

    I’m using a really cool plugin to do this which also generaties the code which can be exported to a plugin or other site.

    I really like the way the plugin can edit and customize existing themes which use custom post types, taxonomies and meta box fields in admin.

    Like the star rating as reviews seem to be attracting the traffic at the moment.

  • Pingback: Tutorials. Daily Digest (01.11.12) | Design Daily

  • http://nathanrjones.com/ Nathan Jones

    Great overview.

    There are many plugins out there now that can get you 90% of the way there with custom post types, but it’s definitely worth understanding the underlying code. Usually, there’s always something missing you need to code manually.

    • Soumitra
      Author

      @nathanrjones:disqus Exactly!

  • Randy Federighi

    I have never added a filtered results option before. I like that!

    One little thing: on Step 3 : Displaying Custom Taxonomies line 14 you have a “float:top;”. Hmm?

    • Soumitra
      Author

      @randyfederighi:disqus Yes :)

  • Pingback: nCircle WebApp360™ Now Scans WordPress and Joomla Content … | Open Knowledge

  • http://www.jsxtech.com Jaspal Singh aka jsxtech

    Great article, Thanks for sharing.

    • Soumitra
      Author

      @jsxtech:disqus You are welcome :)

  • Marco

    Got a question. What if you had a tourist site with regions, things to do and t ype of destination. How do you make taxonomies where I can

    1) View Hotels on X region

    2) View Hotels suitable for X type of destination (romance).

    3) View hotels suitable for Y type of activity (hiking).

    Note that I am asking for TWO types of taxonomies here.

    • Soumitra
      Author

      @disqus_Kam1coGW6t:disqus Please clarify:- 1. Do you want to have region as the parent and type of destination as the child?

      2. View Hotels – In the admin section or in the front end?

      • Marco

        Hello Soumitra,

        I need this on the frond end. And I haven’t thought of the relationships between custom post types and taxonomies yet. But it’s a tourist site and people want to see types of destinations (Romance, Adventure, Relax, etc)…. regions of the country, and activities, and all of them must be linked together.

        So for example if I go to Romance (Destination) I should see which places are suitable for romance.

        Are you from India? Let’s say I go a site destined to Tourism in India and I want to see Romantic destinations in India, so I see Taj Mahal, Beach in Goa as romantic places to visit, etc. Those are attractions or places if you will. But those places belong to a region (Uttar Pradhesh and Marahastra), respectively. And furthermore, in Agra or Goa, I should be able to do activities or tours (Walk in Agra, or Snorkling in Goa, etc).

        So I am thinking about how inserting all of this. Regions/Destinations/Activities with Places/Tours/Hotels. Using custom posts and taxonomies. But I am stuck because I don’t know how to make a custom design based on the criteria (hotels in Uttar Pradhesh, hotels that are Romantic, tours that are romantic, tours in Uttar Pradhesh, places in Uttar Pradesh).

        Thanks for your help!!

        • Soumitra
          Author

          Marco

          You can do one thing:- Let’s say for Hotels CPT

          1. Create three custom categories- Region, Type of Destination and Type of Activity and then create the required child categories under them, for e.g. Region -> New York, California etc etc. Finally associated each of the hotels with all the appropriate categories from the three.

          2. Use this plugin to create a search by custom taxonomy widget in your sidebar ( the sidebar should already be available in your template). This widget helps you to filter the list of hotels based on the different category selections using check boxes. You can also include/exclude a category from the widget.

          I hope this helps.

          • Marco

            Beautiful. I am going to work on it and let you know.

            Thank you very much.

            Marco

  • Pingback: Aptakz Crib | A Guide to WordPress Custom Post Types: Creation, Display and Meta Boxes | Wptuts+

  • http://franceimage.com/ Fr. Image

    Very nice complement to your previous tut on custom posts

  • Gizburdt

    Nice Article. I wrote a nice helper to simplify this. Maybe you can have a look: https://github.com/Gizburdt/Wordpress-Cuztom-Helper

  • Pingback: A Guide to WordPress Custom Post Types: Taxonomies, Admin Columns, Filters and Archives | billybacklinks.com

  • John

    How i can use it to filter custom post type ? when i create an relationship ?

    • Soumitra
      Author

      @disqus_0qNUM3FBuj:disqus Can you please give an example?

  • Pingback: Пользовательские таксономии, фильтры и архивы в WordPress | Wordpresso

  • Pingback: [veille]18 articles à lire » 4h18

  • http://www.facebook.com/andrew.was Andrew Jackson

    Thank you for this Tutorial, is there anyway to limit authors to only see the custom posts they create in the CPT listing page?

    • Soumitra
      Author

      You can use something like-

      if ( is_user_logged_in() ) {

      global $current_user;

      get_currentuserinfo();

      $mypost = array('post_type' => 'movie_reviews','author' => $current_user->ID);

      $loop = new WP_Query($mypost);

      while($loop->have_posts()) : $loop->the_post();

      <!--rest as usual -->

  • Jason Wiser

    I have followed this tut and it is amazing! Thanks for the great detail.
    I need help with one issue. It will not sort by Custom Taxonomy. My custom post type is ‘stores’ and my taxonomy is ‘store-states’. I am creating a list of stores that stock my products. I need to click on ‘Florida’ and get all stores in ‘Florida’. The problem is that it defaults back to the archive-stores.php and I cannot figure out how to test for taxonomy-stores.php

    • http://webinationstation.com/ Jason Wiser

      I found the answer and it was too simple, but took forever.

      The same way that it asks if (is_single) or if (is_archive) simply copy the code and replace if (is_tax).

      So now I have it going to my taxonomy-cpt.php but I cannot get the loop correct. I want it to list all posts in the taxonomy that was requested.

  • Daniel Payne

    You have an extra line in Step 7: Order By Custom Field

    add_filter( 'request', 'column_ordering' );

    • http://www.justusbarkers.com Matt B.

      Thanks for that!

  • Shashank Agarwal

    Hi @disqus_WvVhVwckSg:disqus, Thanks for the tuts and for the plugin.
    Would like to bring to your notice, I wasn’t really able to run the plugin successfully. Everything seems fine, but on visiting the post brings me the 404 error.
    I’m a newbie to wordpress and PHP. Request you to kindly help.

  • omgclassifieds

    Great Tutorial, but how can I make the posts display in a grid on the archive page??

  • ZBV

    I was also unable to the the single and archives pages working, in the plugin or in the theme. I get 404 also

  • http://twitter.com/clazon Andreas Claesson

    Great post @disqus_WvVhVwckSg:disqus, I added the create_my_taxonomies function to the plugin php file and it’s now visible in wp-admin GUI, but I can’t create new ‘Add new Movie Genre”. I get “An error has occured, please reload the page and try again”. What have I missed? (I have only done the ”

    Custom Taxonomy for Custom Post Types” part of this tutorial)

  • webapptester

    Great Article!!It is exactly what I am looking for! :)

  • Karen

    Very helpful, and informative. However, I downloaded the complete plugin, and added it to a site on my localhost to see how it works before jumping into my own implementation, and with debugging turned on for WP:

    Notice: Undefined index: movie_reviews_movie_genre in C:xampphtdocsDesertSiteswp-contentpluginsMovie-ReviewsMovie-Reviews.php on line 267

    Wonder what might be missing?

  • http://www.justusbarkers.com Matt B.

    Any help with creating the template to display the linked custom taxonomies (shown at the end of Step 4 in “Custom Taxonomy for Custom Post Types”)?

  • Shawn Mulligan

    I have a have a questions: In addition to the select menu for rating, could you create another select field whereby the options can be populated by the titles from another post type? For example, what if you had another custom post type called “Actors” where you provide actor bios and you want to be able to select those actors and connect them to your Movie Reviews and vice-versa.

  • http://www.facebook.com/pieter.baecke Pieter Baecke

    How can I view the archive page I created?

  • Aijaz Tanoli

    superb code easy to understand