Archives for Custom Post Types in WordPress

When you create a new post type in WordPress using register_post_type(), WordPress does not automatically create a page for listing the archives of that post type. It’s possible to create one, though, without too much hassle.

For example, you can create an post type called article, and set the permalink for that post type to articles (i.e., an article will have a URL like http://example.com/articles/my-article-title).

Where do you go for a list of all articles, though? You might assume http://example.com/articles/, but you would be assuming erroneously. You could create a page with the slug articles and use a custom template for that page, but that doesn’t work with paging (e.g., http://example.com/articles/page/2/).

Instead, you need to fiddle with WordPress’s rewrite rules. (And thanks to Andrew Wilson for helping me understand this.)

Just after you call register_post_type() to define your custom post type, you’ll call another function, which I’ll call register_post_type_archives. It will take two arguments: the post type (i.e., the first argument to register_post_type), and the base path you’ll be using. So to create an archives page for the articles example above, you would call:

register_post_type_archives('article', 'articles');

What does the register_post_type_archives() function look like? Here you go:

function register_post_type_archives( $post_type, $base_path = '' ) {
  global $wp_rewrite;
  if ( !$base_path ) {
    $base_path = $post_type;
  }
  $rules = $wp_rewrite->generate_rewrite_rules($base_path);
  $rules[$base_path.'/?$'] = 'index.php?paged=1';
  foreach ( $rules as $regex=>$redirect ) {
    if ( strpos($redirect, 'attachment=') == FALSE ) {
      $redirect .= '&post_type='.$post_type;
      if (  0 < preg_match_all('@\$([0-9])@', $redirect, $matches) ) {
        for ( $i=0 ; $i < count($matches[0]) ; $i++ ) {
          $redirect = str_replace($matches[0][$i], '$matches['.$matches[1][$i].']', $redirect);
        }
      }
    }
    add_rewrite_rule($regex, $redirect, 'top');
  }
}

Here, we use $wp_rewrite->generate_rewrite_rules($base_path); to create a default set of rules, and add the case where there are no arguments besides the base path. Then we append the post_type query parameter to each rule. Finally, replace each reference in the redirect (e.g., $1) with the string $matches[1] (or the corresponding number).

Now you have a listing of articles at http://example.com/articles/. You can add other query parameters after that, too. For example, http://example.com/articles/?s=penguin to search articles.

4 thoughts on “Archives for Custom Post Types in WordPress”

  1. I’m having a problem with this code when the name of my custom post type is the same as the url I want for my archives. I have a custom post type ‘soccer-players’ with the permalink set to ‘soccer-players’.

    When I call this function “register_post_type_archives(‘soccer-players’, ‘soccer-players’); nothing seems to happen.
    When I call this function “register_post_type_archives(‘soccer-players’, ‘soccer-playersxx’); it works just as it is supposed to.

    Am I having a conflict with rewrite rules? Why wouldn’t this be working?

Comments are closed.