What you want can be done also filtering 'month_link'
for monthly archives, 'year_link'
for yearly archives and 'day_link'
for daily archives.
You can also write a function that extends wp_get_archives
to work with CPTs, adding filters to 'getarchives_where'
and to the archive link.
function wp_get_cpt_archives( $cpt = 'post', $args = array() ) {
// if cpt is post run the core get archives
if ( $cpt === 'post' ) return wp_get_archives($args);
// if cpt doesn't exists return error
if ( ! post_type_exists($cpt) ) return new WP_Error('invalid-post-type');
$pto = get_post_type_object($cpt);
// if cpt doesn't have archive return error
if ( ! $pto = get_post_type_object( $cpt ) ) return false;
$types = array('monthly' => 'month', 'daily' => 'day', 'yearly' => 'year');
$type = isset( $args['type'] ) ? $args['type'] : 'monthly';
if ( ! array_key_exists($type, $types) ) {
// supporting only 'monthly', 'daily' and 'yearly' archives
return FALSE;
}
$done_where = $done_link = FALSE;
// filter where
add_filter( 'getarchives_where' , function( $where ) use (&$done_where, $cpt) {
if ( $done_where ) return $where;
return str_replace( "post_type = 'post'" , "post_type = '{$cpt}'" , $where );
});
// filter link
add_filter( "{$types[$type]}_link", function( $url ) use (&$done_link, $pto, $cpt) {
if ( $done_link ) return $url;
// if no pretty permalink add post_type url var
// if ( get_option( 'permalink_structure' ) || ! $pto->rewrite ) {
if ( ! get_option( 'permalink_structure' ) || ! $pto->rewrite ) {
return add_query_arg( array( 'post_type' => $cpt ), $url );
} else { // pretty permalink
global $wp_rewrite;
$slug = is_array( $pto->rewrite ) ? $pto->rewrite['slug'] : $cpt;
$base = $pto->rewrite['with_front'] ? $wp_rewrite->front : $wp_rewrite->root;
$home = untrailingslashit( home_url( $base ) );
return str_replace( $home, home_url( $base . $slug ), $url );
}
});
// get original echo arg and then set echo to false
$notecho = isset($args['echo']) && empty($args['echo']);
$args['echo'] = FALSE;
// get archives
$archives = wp_get_archives($args);
// prevent filter running again
$done_where = $done_link = TRUE;
// echo or return archives
if ( $notecho ) {
return $archives;
} else {
echo $archives;
}
}
Please read inline comments for more info on how function works.
Now there is a problem. The url like /events/2014/04/
are not recognized by WordPress, so you need to add the reqrite rules that handle that sort of urls.
Again you can write a function that add that rules for you:
function generate_cpt_archive_rules( $cpt ) {
if ( empty($cpt) || ! post_type_exists($cpt) ) return;
global $wp_rewrite;
$pto = get_post_type_object($cpt);
if ( ! $pto->has_archive ) return;
$base = $pto->rewrite['with_front'] ? $wp_rewrite->front : $wp_rewrite->root;
$base = trailingslashit( $base );
$slug = is_array( $pto->rewrite ) ? $pto->rewrite['slug'] : $cpt;
$year = ltrim( $base . $slug . '/([0-9]{4})/?$', '/' );
$month = ltrim( $base . $slug . '/([0-9]{4})/([0-9]{2})/?$', '/' );
$day = ltrim( $base . $slug . '/([0-9]{4})/([0-9]{2})/([0-9]{2})/?$', '/' );
$index = 'index.php?post_type=' . $cpt;
$rules[$year] = $index . '&m=$matches[1]';
$rules[$month] = $index . '&m=$matches[1]$matches[2]';
$rules[$day] = $index . '&m=$matches[1]$matches[2]$matches[3]';
$page = 2;
foreach ( array( $year, $month, $day ) as $rule ) {
$paged = str_replace( '/?$', '/page/([0-9]{1,})/?$', $rule);
$rules[$paged] = $rules[$rule] . '&paged=$matches[' . $page . ']';
$page++;
}
return $rules;
}
This function generate the rules, however, you also need to add and flush them.
Now is recommend to flush rules on theme (or plugin) activation and register them on init, so you should do something like:
function register_cpt_dates_rules() {
$cpts = array( 'events' );
foreach ( $cpts as $cpt ) {
foreach ( generate_cpt_archive_rules( $cpt ) as $rule => $rewrite ) {
add_rewrite_rule( $rule, $rewrite, 'top' );
}
}
}
// flushing on theme switch
add_action('after_switch_theme', function() {
register_cpt_dates_rules();
$GLOBALS['wp_rewrite']->flush_rules();
});
// registering on init
add_action( 'init', 'register_cpt_dates_rules' );
Once you have added these functions to your functions.php
(and you have deactivated and activated again your theme to make 'after_switch_theme'
run) you can display the archives using:
wp_get_cpt_archives( 'events' );
And it will display the archives for event CPT, and the link will be something /events/2014/04/
.
It works also for yearly or daily archives:
wp_get_cpt_archives( 'events', array( 'type'=>'yearly' ) );
wp_get_cpt_archives( 'events', array( 'type'=>'daily' ) );
Using my two functions you have a super-easy way to make wp_get_archives
works with CPTs.
Note that all the code here require PHP 5.3+.