WordPress Archive Pages Based on Custom Taxonomy

Published 3 years 7 months ago on September 6, 2010 — 8 min read

Please Note: This article has since been extended. Please read more up-to-date information in Revisiting Custom Post Types, Custom Taxonomies, and Permalinks

Note: This article is an extension of Custom Post Types, Custom Taxonomies, and Permalinks in WordPress 3.0 which discusses the Custom Post Type and Custom Taxonomy implementation we’ve setup thus far. While a summary is offered here, please know that some details discussed herein reflect the implementation discussed previously.

In order to set up front end visible archive pages for Custom Taxonomies, everything begins with the rewrite slug attached to your Custom Taxonomy. This can be edited using the Custom Rewrite Slug field provided by Custom Post Type UI. Some planning is required on your part, but the plan must take into consideration the Page structure currently implemented.

So far, we’ve based our Cameras Custom Post Type on the slug of /cameras/. This works out well as our Custom Post Type houses information based on DSLR Cameras. We’ve set up an index-style page that sits at /cameras/, and our single pages are stored under /cameras/%postname%/. We’ve just now included a Custom Taxonomy, and we’d like to include that in the mix as well.

Out of the box taxonomy permalinks

By default, a Custom Taxonomy rewrite slug is based on the Taxonomy Name. Considering our Brands Custom Taxonomy, we might expect the Taxonomy listing would be made available at /cameras/brands/%taxonomyname%/; unfortunately that’s not the case. Given the Taxonomy being a default implementation, we’d actually find the listing at /brands/%taxonomyname%/. You’ll also note that any Cameras we’ve marked under a particular brand will show up in the listing. For example, if we navigated to http://wordpress/cpt/brands/canon/, we’d be faced with a page resembling:

Default post listing based on taxonomy

This listing behaves just like a regular Posts archive page would, and that’s because it’s literally based on the archive.php template file of your theme. While it works, it’s not ideal. Thinking ahead, if you have multiple Custom Post Types along with WordPress posts, there isn’t much segmentation when it comes to actually viewing archive pages on the front end.

Take into consideration the possibility of us expanding our content by also including a Custom Post Type for Flashes. If we were to use a Custom Taxonomy for Flash Brands (as opposed to Camera Brands) with that Custom Post Type as well, we’d have overlapping entries and it would quickly become unorganized as results for both flashes and cameras would turn up when viewing http://wordpress/cpt/brands/canon/ which isn’t always desirable.

At this point it’s important to note the specificity of a Custom Post Type/Custom Taxonomy implementation. There is a strong possibility that you very well may want to merge Canon flashes and cameras in your archive pages, but you may not. For the purpose of example, we’re going to deem it necessary to segment the two. To accomplish that, you’ll need to beef up the rewrite slug for your Custom Taxonomies.

Customizing your Custom Taxonomy template

When dealing with taxonomies, WordPress looks for specific theme files. As with other theme files, templates for Custom Taxonomies can be based on the taxonomy name, in this case brands. Create a file called taxonomy-brands.php in your theme directory, WordPress checks for this file before hitting archive.php which can allow you to make the front end more custom. As an example, we’ll use the following for our taxonomy-brands.php:

<?php get_header(); ?>

  <?php $term = get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) ); ?>
  <div id="container">
    <div id="content" role="main">

      <h1 class="page-title"><?php echo $term->name; ?> Archives</h1>

      <?php if (have_posts()) : ?>
        <?php while (have_posts()) : the_post(); ?>

          <div class="post type-post hentry">
            <h2 class="entry-title">
              <a href="<?php echo get_permalink(); ?>" title="<?php the_title(); ?>" rel="bookmark">
                <?php the_title(); ?>
              </a>
            </h2>

            <div class="entry-meta">
              <span class="meta-prep meta-prep-author">Posted on</span> 
              <a href="<?php echo get_permalink(); ?>" title="<?php the_time( 'g:i a' ); ?>" rel="bookmark">
              <span class="entry-date"><?php the_time( 'F j, Y' ); ?></span></a>
            </div><!-- .entry-meta -->

            <div class="entry-summary">
              <?php the_excerpt(); ?>
            </div><!-- .entry-summary -->
          </div>

        <?php endwhile; ?>
      <?php endif; ?>

    </div><!-- #content -->
  </div><!-- #container -->

  <?php get_sidebar(); ?>
<?php get_footer(); ?>

This file is based on category.php with a few modifications. First is the call to get_term_by() which uses get_query_var() to determine what Custom Taxonomy term we’re working with. From there, we carry on through The Loop as normal, outputting the information we’d like to use and where.

The only lasting problem here, is the fact that we’re still sitting at http://wordpress/cpt/brands/canon/. I’d much rather have this page available at http://wordpress/cpt/cameras/brands/canon/ as to differentiate it from other Custom Post Types and their Custom Taxonomies.

Correctly identifying your rewrite slug

Custom Post Type UI makes it really easy to modify our Custom Rewrite Slug, which controls this change entirely. As mentioned earlier, a rewrite slug, unless otherwise defined, is based on the Custom Taxonomy name itself, hence our current /brands/%taxonomyname%/ result. We simply need to change our Custom Rewrite Slug to include the URL structure we’re looking for, in this case cameras/brands:

Refining the slug used for a taxonomy archive page

Saving the change to our Custom Taxonomy rewrite slug will push the permalink change and we’ll finally be able to hit our more appropriate http://wordpress/cpt/cameras/brands/canon/ URL, right? Not quite. It turns out that if we hit http://wordpress/cpt/cameras/brands/canon/ WordPress thinks we’re trying to hit a single post from our Cameras Custom Post Type and redirects us to the closest match.

In order to get our final archives-style pages to load properly, we’re going to have to modify our Custom Post Type Custom Rewrite Slug to differentiate a request for a single post entry versus our archive-style pages. To do that, we’ll need to change the slug from cameras to something more appropriate like cameras/body which could signify that we’re viewing a camera body.

With that change in place, we’re in business! We’re now able to hit http://wordpress/cpt/cameras/brands/canon/ and view the Posts that have been marked as Canon Posts, using our custom permalink as well as our custom template file. You can repeat this process with additional taxonomies by creating appropriate rewrite slugs in conjunction with new taxonomy-%taxonomyname%.php template files.

Adding links to Taxonomy archives in the sidebar

Many of the WordPress-native functions can be adapted for use with Custom Post Types and Custom Taxonomies. To add links to your newly created Custom Taxonomy archive pages, you could add something like this snippet to your sidebar.php:

<li id="camera-brands" class="widget-container">
  <h3 class="widget-title">Camera Brands</h3>
  <?php $cam_brands = get_terms('brands', 'hide_empty=1'); ?>
  <ul>
    <?php foreach( $cam_brands as $brand ) : ?>
      <li>
        <a href="<?php echo get_term_link( $brand->slug, 'brands' ); ?>">
          <?php echo $brand->name; ?>
        </a>
        <ul>
          <?php 
            $wpq = array( 'post_type' => 'cameras', 'taxonomy' => 'brands', 'term' => $brand->slug );
            $brand_posts = new WP_Query ($wpq);
          ?>
          <?php foreach( $brand_posts->posts as $post ) : ?>
            <li>
              <a href="<?php echo get_permalink( $post->ID ); ?>">
                <?php echo $post->post_title; ?>
              </a>
            </li>
          <?php endforeach ?>
        </ul>
      </li>
    <?php endforeach ?>
  </ul>
</li>

While a bit of the markup is specific to Twenty Ten’s sidebar.php we can dissect what’s going on here:

  1. Pull our brands
  2. Create a list item for our brand
  3. Pull all posts that have been marked as being that brand (via the taxonomy and term arguments)
  4. Loop through each post within that defined Custom Taxonomy entry and dump out what we’d like to use
  5. Repeat until finished with all Custom Taxonomy entries

When incorporated into the sidebar, we’ll get something like this:

Customized sidebar based on Custom Post Types and Custom Taxonomies

As you can imagine, that sidebar listing would quickly grow to an unmanageable size, but the example illustrates just what’s possible using a few WordPress native functions and your Custom Post Types/Custom Taxonomies.

Custom Post Types and Custom Taxonomies are powerful

These changes in WordPress 3.0 are more than welcome, as they’re going to set the stage for much more traditional CMS-like behavior. The implementation discussed in this short series of articles is quite specific but hopefully outlines one of the many ways to work with Custom Post Types and Custom Taxonomies, all the while keeping a prettier permalink structure throughout your sites.

There's a conversation brewing

  1. Hey, nice work explaining everything, Im just getting my head around the custom post types templating now.

    Im using your sidebar code and trying to modify it to only display the posts(custom post type) from the same term(inside a custom taxonomy) of the current page. so all pages of that certain post-type’s term will be listed. This assumes there will only be one term selected for the taxonomy.

    No matter what I do I keep getting all the posts of the current taxonomy. It should be easy, but i think Im mixing up the arguments do you have any suggestions?

  2. Thanks for the tut! Very helpful!

  3. Thank you! This was so incredibly helpful!

  4. I have been banging my head all over the internet just trying to find a way to display posts from a taxonomy. Looking at your code I narrowed it down to what sounded like it would do that

    $term = get_term_by( ‘slug’, get_query_var( ‘term’ ), get_query_var( ‘taxonomy’ ) );

    if (have_posts()) :

    while (have_posts()) : the_post();

    But no luck. Any suggestions?

  5. Jonathan, thanks a lot for this post!
    Wordpress became a truly powerful CMS.

  6. Hello Jonathan, thank you for this article. For Adding links to Taxonomy archives in the sidebar i use Custom Menus function that exists in WordPress 3.1. I’t the easiest way to do it.

  7. What if we wanted the following:

    http://wordpress/cpt/cameras/canon/7d
    – Single post about the Canon 7D
    http://wordpress/cpt/cameras/canon/
    – Archive page of all Canon cameras
    http://wordpress/cpt/cameras/
    – Archive page of all cameras of all brands

    Logically it’s a sensible hierarchy, but is something like that possible within the current framework of custom posts and taxonomies?

    • You’re absolutely right; it definitely is a sensible hierarchy. The trouble comes up when there’s a collision between a Page slug and the rewrite slug of a Custom Post Type. On a high level we need to keep in mind that the rewrite slug of a Custom Post Type can in fact collide with the slug of an existing Page.

  8. Now, if I could figure out, how to sort the posts displayed by title in alphabetic order.
    You see, I am building a business directory, based on a cutom post type and with custom fields and with custom taxonomies. The displaying of the query results is not the problem, but they appear in chronological order, not alphabetical. Can you help out?

  9. OK, I got it working:

    Here is my code for taxonomy.php:
    [code]

    Gefundene Einträge für:name; ?>

    <a href="" title="" rel="bookmark">

    post->ID;
    echo get_post_meta($postid, 'postleitzahl', true);echo ' - ';
    echo get_post_meta($postid, 'ort', true);
    wp_reset_query();
    ?>

    Nicht gefunden
    Keine Einträge unter Ihrem Suchbegriff.

    [/code]

    Thanks for the wonderful article and your input.

    Thomas

    • Sorry, that was only a part of the code. Here goes:

      [code]

      Gefundene Einträge für:name; ?>

      <a href="" title="" rel="bookmark">

      post->ID;
      echo get_post_meta($postid, 'postleitzahl', true);echo ' - ';
      echo get_post_meta($postid, 'ort', true);
      wp_reset_query();
      ?>

      Nicht gefunden
      Keine Einträge unter Ihrem Suchbegriff.

      [/code]

    • Hey Thomas it would actually be most beneficial if you could paste your code into something like http://pastie.org – would you mind doing that and posting back?

  10. The latest snippet saved me alot of time. Thanks for sharing!

  11. Hey Jonathan, very great tutorial, thank you. I got one issue, hope you could help me. While i was working on localhost everything was fine and taxomomy-name.php worked great. But when i’ve installed the theme on a reall hosting, wordpress ignores this template and goes strait to the archive.php. Do you familiar with this problem? Thank you.

  12. It’s working, i changed all uppercase letters to lowercase in taxonomy name. I don’t know why it was causing a problem. Jonathan, thanks for your help.

  13. Great tutorial Jonathan, the best i’ve found on the web for custom posts and taxonomies!

    I followed your instruction and got everything working that you said, however, i have run into a problem that i need some help with. In your example, we can create a working page for:
    mysite/cameras
    mysite/cameras/brands/canon
    mysite/cameras/postname

    How though do i go about creating the page for mysite/cameras/brands?? When i try to access it i get a page not found error. Any help would be greatly appreciated. Thanks for your time, Patrick.

    • Hey Patrick, I completely understand the issue you’re hitting and it comes down to the way WordPress handles slugs. Unfortunately Page slugs and CPT slugs need to be carefully constructed to avoid such collisions. From what I’ve overheard I believe that issue will be addressed in upcoming releases.

  14. Hello,
    I’m running into problems with a 404 on “page 2″ of the index for my custom post type. I have a “news-articles” custom type with a rewrite slug of “news”. I created a page with a slug of “news” for displaying an index of my articles. In the “news” page’s template I am doing the following:

    query_posts(“post_type=news-articles”); // Get the right custom post type
    if (have_posts()) :
    while (have_posts()) : the_post();
    get_template_part(‘partial-news-article’); // Display the article using a template file
    endwhile;
    endif;

    I included navigation links on the “news” page using the next_posts_link and previous_posts_link functions. The links generate the correct URLs (e.g., http://www.example.com/news/page/2), but I’m getting a blank page (that uses the index.php template file).

    Is there a way to get the pagination working? I’ve tried the “Category pagination fix” and “Custom Post Type Category Pagination Fix” plugins but to no avail.

    Thanks.

    • @Robert,

      I’m having the exact same issue. Everything is broken the very same way. Right down to the 404 error that grabs the index.php file instead. I read something where someone got it to work on page/2/?custom_type=posts but I can’t seem to replicate it. And it doesn’t help with the slugs. Any help/fix would be greatly appreciated!

    • Currently WordPress won’t be supporting Custom Post Type display in core until version 3.1 — luckily that’s coming soon. If you have an impending deadline to hit I would suggest either setting up your own pagination and manually setting the paged variable in your query_posts() call or seeing what the Simple Custom Post Type Archives can do for you. I hope that helps!

    • @Jonathan, thanks for the info.

      @Ramsey, I had ended up adding the ‘paged’ parameter like Jonathan suggested:

      $paged should be set by WordPress automatically when “/page/X” is used in the URL, and you would substitute ‘news-article’ with the name of the custom post type you are looking to display.

  15. Hi Jonathan,

    Thanks so much for this info, it ended a day-long quest to find information which, in my humble opinion, should not be this hard to find. The code to customize the taxonomy template should be in the Codex somewhere…

    Stef

  16. Good insight. There is very little information around planning custom post types with taxonomies other than the movies example that is repeated everywhere.

    I’m having a hard time deciding the architecture for my art portfolio site.

    I could choose to create an overall post type of Art with a taxonomy of type that would include photography, drawing, ceramics, etc.

    Or I could create custom post types for photography, drawing, ceramics, etc.

    Not sure the best way to slice this up. Any insights or recommendations?

    • That’s definitely up to personal preference and would in part depend on how far you want to go with customizing the Custom Post Type(s). If you plan on having different data associated with each (Custom Fields or otherwise) I’d suggest separate Custom Post Types. If they’re all using the same fields, a single Custom Post Type with a Custom Taxonomy would be a great way to go about it.

  17. Just wondering, is there a way to pull traditional post meta data into a POD record? I’m wanting to have students create pages that hold links to uploaded files and some entered text. I want those pages to have the capability of allowing comments, as well as knowing which student posted the page and when. I thought I would use PODs for this, but it seems like your implementation described here would be more appropriate.

    Would you agree?

  18. Hi Jonathan,

    we are working with custom post types (for business listings) – with 2 orthogonal custom taxonomies (one for the city they are in, the other for the type of business).

    Is it possible to do any ‘url rewrite’ magic to combine the two taxonomies into a ‘nested url’ for the cpt e.g.:

    mysite/london/cafe/

    Would we then implement the ‘archive’ in taxonomy-business-type.php (modding the query to include the city0 or is a more specific taxonomy-city-business-type.php possible?

    Thanks,
    Roger

    Roger

  19. I found a very simple solution to the paging problem.

    http://wordpress.org/support/topic/pagination-with-custom-post-type-listing?replies=23#post-1637753

    Synopsis…
    The page that you create to act as the custom post archive cannot have the same name as the custom post type itself.

    For instance…
    If you have a custom post type called “products” and you create a page with a title of ‘products’ to use as the top level page for that post-type, paging will break. Rename the page to something else, i.e. ‘product’, manually select the template you want to use from the right hand column on the page edit screen and wallah, paging works again.

  20. To clarify, it seems that it is not the custom post type name that matters but the slug you assign to it. You cannot create a custom post type with a slug that matches a page slug or paging will break.

    If you have a page at: http://www.yoursite.com/page/
    and a custom post type with a rewrite slug of ‘page’
    the paging will not work.

    Change the slug of the post type to ‘pages’ and paging works again. However that means that each single post will have a different base directory than the cpt archive page.

    Single cpt post: http://www.yoursite.com/pages/postname/
    cpt archive: http://www.yoursite.com/page/

    If a user was to strip off ‘postname/’ from the single url, expecting a listing of all the posts, they would get a 404.

    That’s an acceptable trade-off in my opinion. I wish there was a way around though. Any ideas?

  21. Good stuff! I’ve learned a ton here!

    What if you want to have a custom number of posts per page on your taxonomy-brands.php? For blog posts the Reading > Settings is 5, but for my custom post type 5 won’t do because of the unique way I’ve designed those templates. For me my custom post type is “portfolio” and on all those pages I want a grid of thumbnails. I need more like 20 per page with pagination.

    I’ve been working on this problem all week and I’ve found no solution yet. Any help would be appreciated. Thanks!

  22. Admittedly, I am only reading and by doing right now, and this has been a great article, surely the solution I’ve been looking for for days!

    I’m getting stumped, however, when you rewrite the slug to camera/body. I see that it is to overcome the other problem but does that mean every single post would now have body in the permalink? It seemed to come from nowhere!

    Before I get into trouble for misunderstanding any further I shall try it out. But please, if my concern makes sense could you enlighten me?

    Thanks for a great article.

  23. It would seem that this is not an issue for me in any case. You saved me Jonathan, thanks!

  24. Jonathan,

    Great information. I’ve got a spin on it that I’m having trouble figuring out. I’ve got multiple pods that are inter-related and I would like to access these pods via custom taxonomy (I think). Basically, I have a job site that lists license requirements based on the state and the job. Preferably, I’d like to only have to create the data in the pods for each license related to each job and state without having to create tons of pages. I’d like to have a general page for each state, as well as each job, with specific pages for each license, like this:

    site.com/state – Info taken from the state’s pod (e.g. info about Oregon with cross-reference data about what jobs require licenses)
    site.com/job – Info taken from the job’s pod (e.g. info about Carpenters with some cross-referenced data about which states require licenses)
    site.com/state/job – Info taken from the specific license pod related to that state’s requirements for that job

    The trouble I’m having is trying to figure out the best way to set that up and it seems to be that custom taxonomies would be the way to go. It sounds like I might be sol on the state specific page if I use it as the parent for the job taxonomy per your previous comment responses, right? Any general code or idea to get me started would be greatly appreciated, my php is moderate but I’m jumping into a bit of the deep-end with this one.

    Oh, and I’m running Thesis theme, too ;-)

    Any thoughts/ideas?

    Thanks!
    Joshua

    • I would actually suggest working with Pods Pages and wildcard URI segments, e.g. Pods Pages for state, job, and state/* and from there you can implement your logic based on which Pods Page you’re viewing.

  25. Jonathan,

    This was a huge help in figuring out a nagging issue I was having with a client’s site. Thanks for an excellent post. Very well written… and explained.

  26. I love you. I’ve been trying to figure this out for about 2 hours, and within 10 seconds of reading your post I have it working perfectly. Thank you.

  27. Hi Jonathan,
    I’ve been trying to figure something out, and haven’t been able. Thought maybe you’d have some insights. I posted on the WP forums: http://wordpress.org/support/topic/custom-taxonomies-hierarchical-is-what-im-trying-possible-best-approach

    Basically, just trying to get a multi-level archive working. (I think that’s the best way to describe it.) Any help would be greatly appreciated!!

  28. Hey Jonathan,
    that is a great explanation of how it all can fit together! The WP standard way is quite confusing. Thanks for sharing this with us!

  29. Hi,

    i want to know what the current taxonomy is in my taxonomy-tpl.php. The reason is I want to echo a class on the current page url I am on. How can I do that?

    Thanks for a great tutorial!!

By all means, contribute

Leave a comment

Powered by Fusion

This article is so meta

Published September 6th, 2010

Random article

css.php