Blog Post

URL Rewriting: Custom Post Types Date Archive

Date archives are very useful if you want to list your posts only for the specified year, month or day. But, they only work with posts, and not with the custom post types.

Here is how the date based archives look like:

Year archive: http://www.example.com/2011/
Month archive: http://www.example.com/2010/10/
Day archive: http://www.example.com/2012/02/22/
Month archive, feed: http://www.example.com/2010/10/feed/
Year archive, page 2: http://www.example.com/2012/page/2/

And, by default, they will show only ‘posts’, and if you want to show other post types, you need to modify the main query to include additional post types. But, to get similar URL’s limited to custom post types, that will not work.

If we have post type names ‘movie’, and for the post type archive slug we have set ‘movies’, you expect to get these date based archives for it:

Year archive: http://www.example.com/movies/2011/
Month archive: http://www.example.com/movies/2010/10/
Day archive, feed: http://www.example.com/movies/2012/02/22/feed/
Year archive, page 5: http://www.example.com/movies/2011/page/5/

Such URL’s will filter posts by post type and date, returning date based archives for a post type. But, it is not that hard to do this. What’s more, there are two ways you can do it: using a plugin and doing it your self with custom coding.

Generate rewrite rules

For the year, month and date archives you need one extra rewrite rule. And, if you want to have feeds and pages for them, you need 3 more rules for each one. So, in total there are 12 new rules for each post type for the date based archives. For this to work, we need to hook into action ‘generate_rewrite_rules‘. Here is the basic action and function:

add_action('generate_rewrite_rules', 'my_date_archives_rewrite_rules');
function my_date_archives_rewrite_rules($wp_rewrite) {
  $rules = my_generate_date_archives('movie', $wp_rewrite);
  $wp_rewrite->rules = $rules + $wp_rewrite->rules;
  return $wp_rewrite;
}

This code attached  ‘my_date_archives_rewrite_rules‘ function to ‘generate_rewrite_rules‘ action. On line 3, we call another function that will generate rules for a post type ‘movie’.

Function to build the rules

Now we need the function that generates rules for the specified post type.

function my_generate_date_archives($cpt, $wp_rewrite) {
  $rules = array();

  $post_type = get_post_type_object($cpt);
  $slug_archive = $post_type->has_archive;
  if ($slug_archive === false) return $rules;
  if ($slug_archive === true) {
    $slug_archive = isset($post_type->rewrite['slug']) ? $post_type->rewrite['slug'] : $post_type->name;
  }

  $dates = array(
    array(
      'rule' => "([0-9]{4})/([0-9]{1,2})/([0-9]{1,2})",
      'vars' => array('year', 'monthnum', 'day')),
    array(
      'rule' => "([0-9]{4})/([0-9]{1,2})",
      'vars' => array('year', 'monthnum')),
    array(
      'rule' => "([0-9]{4})",
      'vars' => array('year'))
  );

  foreach ($dates as $data) {
    $query = 'index.php?post_type='.$cpt;
    $rule = $slug_archive.'/'.$data['rule'];

    $i = 1;
    foreach ($data['vars'] as $var) {
      $query.= '&'.$var.'='.$wp_rewrite->preg_index($i);
      $i++;
    }

    $rules[$rule."/?

quot;] = $query;
$rules[$rule."/feed/(feed|rdf|rss|rss2|atom)/?


quot;] = $query."&feed=".$wp_rewrite->preg_index($i);
$rules[$rule."/(feed|rdf|rss|rss2|atom)/?


quot;] = $query."&feed=".$wp_rewrite->preg_index($i);
$rules[$rule."/page/([0-9]{1,})/?


quot;] = $query."&paged=".$wp_rewrite->preg_index($i);
}

return $rules;
}
Let’s see what is going on in this function. Function is called with two arguments: name of the post type ($cpt) and the WP rewrite object ($wp_rewrite). First, we need to find out what is the slug for the archives URL for this post type ($slug_archive). If you have disabled archives for the post type, this function will exit on line 6, and you can’t add date-based archives. If the archives are set to use a custom slug, the function will use that, or it will use post type name (lines 7-9).

Lines 11 to 21 are used to set up day, month and year based regular expressions rules and variables to be used in the query. First one is for the day archives and it needs a year, month and day. Month based archive needs year and month, and the year based archive needs only a year. After that, we go through these 3 archives types and we generate rules for them.

Line 24 sets up the base for the query using post type name and on 25 we set up the base for the rewrite rule using post type archives slug and the rule for the archive. Now, we need to add to the query all the variables needed for the rule (27 to 31). This builds the basic rewrite rule and the rewrite query rule point to. Lines 33 to 36 are setting up rules for basic archive rule, feeds and pages. Feeds and pages need extra bits for the regular expression to detect feed or page, and extra query variable for feed or page. If you don’t want to have feeds for these date-based archives, you can remove lines 34 and 35. Line 33 adds basic rewrite rule, and line 36 adds pages for archives, and you need to leave these two in place. After all the rules are generated, they are returned back to the rules in ‘my_date_archives_rewrite_rules‘ function, merged with WP rules and ready to use.

Rebuild rewrite rules

Whenever you make changes to rewrite rules, you need to instruct WordPress to rebuild the cached rules. To do this, it is enough to open Permalinks panel on the WordPress admin side under Settings.

Use the plugin

If you don’t want to lose time over this, and you need these extra rules, get yourself GD Content Tools Pro plugin and add new features with just a few clicks. It includes custom rewrite rules, date-based archives and much more.

Conclusion

Adding extra rewrite rules is not that complicated as you can see from this practical example. Depending on what you need, you need to learn (at least basics) PHP regular expressions and to make sure that query for the rule has the sufficient number of variables to correspond to the number of capture groups in the regular expression.



Please wait...
Great managed hosting experience, fast and secure with amazing support.
Siteground Hosting

PHP 7.0 to 8.1, free Let's Encrypt SSL certificates, cache and optimizations for WordPress. Choose between great shared WordPress hosting plans (starting from 3.95 €/month), high performance cloud hosting (starting from 64.00 €/month)...

Disclosure: This post contains affiliate links, which means that I receive compensation if you make a purchase using this link.
GD Content Tools Pro
Enhancing WordPress Content Management

Register and control custom post types and taxonomies. Powerful meta fields and meta boxes management. Extra widgets, custom rewrite rules, enhanced features...

About the author

Milan Petrovic
Milan Petrovic

CEO and Lead developer of Dev4Press Web Development company, working with WordPress since 2008, first as a freelancer, later founding own development company. Author of more than 250 plugins and more than 20 themes.

Subscribe to Dev4Press Newsletter

Get the latest announcements, release digests, promotions and exclusive discounts, and general Dev4Press-related news straight into your mailbox.


This form collects your email (optionally your name) for the purpose of sending you newsletters. Check out our Privacy Policy for more information on how we store and manage your data. We will not send you any spam. Newsletters are sent 2 to 4 times every month.

4 thoughts on “URL Rewriting: Custom Post Types Date Archive”

  1. Awesome !!!

    Please wait...
    Reply
  2. You may want to rewrite line 8 into this:
    $slug_archive = isset($post_type->rewrite[‘slug’]) ? $post_type->rewrite[‘slug’] : $post_type->name;

    If a custom slug has been set, your code will ignore it.

    Please wait...
    Reply
    • Thanks for letting me know. I have fixed the code.

      Please wait...
      Reply
  3. wow it works. thank you so much

    Please wait...
    Reply

Leave a Comment

WP Rocket - Make WordPress Load Fast in a Few Clicks
Grammarly - Number 1 Writing App
25