A good[ish] website
Web development blog, loads of UI and JavaScript topics
This is one of the things that are really easy to do with built in post types, but requires extra work when custom post type is in use.
If you use wp_get_archives()
to get a date archive, it only gets them for the "Posts" post type, and not for custom post types (CPTs). This article deals with three things:
wp_get_archives()
.wp_get_archives()
output right kinda URLs e.g. site.com/cpt/2015/01
.The custom post type needs to have 'has_archive' => true
. More here.
Here's two functions that get the rewrite rules sorted out for you, these go into functions.php
. They're lifted this from here, and I've improved it a bit, see the comments for explanation. Remember to change your custom post type in $rules
in the first function.
/**
* Custom post type specific rewrite rules
* @return wp_rewrite Rewrite rules handled by Wordpress
*/
function cpt_rewrite_rules($wp_rewrite)
{
// Here we're hardcoding the CPT in, article in this case
$rules = cpt_generate_date_archives('article', $wp_rewrite);
$wp_rewrite->rules = $rules + $wp_rewrite->rules;
return $wp_rewrite;
}
add_action('generate_rewrite_rules', 'cpt_rewrite_rules');
/**
* Generate date archive rewrite rules for a given custom post type
* @param string $cpt slug of the custom post type
* @return rules returns a set of rewrite rules for Wordpress to handle
*/
function cpt_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) {
// Here's my edit to the original function, let's pick up
// custom slug from the post type object if user has
// specified one.
$slug_archive = $post_type->rewrite['slug'];
}
$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."/?$"] = $query;
$rules[$rule."/feed/(feed|rdf|rss|rss2|atom)/?$"] = $query."&feed=".$wp_rewrite->preg_index($i);
$rules[$rule."/(feed|rdf|rss|rss2|atom)/?$"] = $query."&feed=".$wp_rewrite->preg_index($i);
$rules[$rule."/page/([0-9]{1,})/?$"] = $query."&paged=".$wp_rewrite->preg_index($i);
}
return $rules;
}
NOTE: permalinks needed to be flashed after this, meaning that go to Settings > Permalinks and click the "Save Changes" button.
Now you should be able to access monthly archives from example.com/post-type-slug/2015/01
and so on. You'd use the archive-{cpt-name}.pgp
template to display the entries.
wp_get_archives()
work with CPTsNext, we need to get links to the archive pages, we can modify wp_get_archives()
to do that. Let's use the getarchives_where
filter:
function example_getarchives_where($where)
{
return str_replace("WHERE post_type = 'post'", "WHERE post_type IN ('article')", $where);
}
add_filter('getarchives_where', 'example_getarchives_where');
Note the second argument to str_replace
, there we can specify what post types will be included. If we want to throw more post types into the mix. The following gets Posts and Articles:
WHERE post_type IN ('post', 'article')
See the getarchives_where
source code here, that might open things up a bit.
Now the wp_get_archives()
outputs posts from the right post type, but URLs it emits are still wrong: site.com/2015/01
we want site.com/cpt_slug/2015/01
. One more function is needed.
This could be done right in a template also, but I prefer keeping templates clean and abstracting all the functions in functions.php
. The following is just a wrapper around wp_get_archives()
. It it replaces site.com
with site.com/{cpturl}
. See comments for more info:
/**
* Get archives to custom post type
* @param string $cpt The wanted custom post type
* @return array A list of links to date archives
*/
function cpt_wp_get_archives($cpt)
{
// Configure the output
$args = array(
'format' => 'custom',
'before' => '<li class="post-list__item post-list__item--archive">',
'after' => '</li>',
'echo' => 0,
'show_post_count' => true
);
// Get the post type objest
$post_type_obj = get_post_type_object($cpt);
// Slug might not be the cpt name, it might have custom slug, so get it
$post_type_slug = $post_type_obj->rewrite['slug'];
// Domain of the current site
$host = $_SERVER['HTTP_HOST'];
// Replace `domain.tld` with `domain.tdl/{cpt-slug}`
$output = str_replace($host, "$host/$post_type_slug", wp_get_archives($args));
return $output;
}
Then use it:
echo cpt_wp_get_archives('custom-post-type-name');
This was meant to be one of those quick things, but ended up being a wild journey deep into the gut of WP. And don't forget to flash your permalinks.
Comments would go here, but the commenting system isn’t ready yet, sorry.