I've got an event
custom post type, and using filters, I've configured the archive page to show only event in the future, and in chronological order based on a meta value (start date).
I want to make a second archive page to show events in the past, complete with pagination, ordered reverse chronologically on the same meta value.
My first thought is to use a shortcode inside a normal page, but I'm not sure how to get the pagination working.
Alternatively, is there a way to create a "generic" archive page? Do I need to get into rewrite rules?
I've got an event
custom post type, and using filters, I've configured the archive page to show only event in the future, and in chronological order based on a meta value (start date).
I want to make a second archive page to show events in the past, complete with pagination, ordered reverse chronologically on the same meta value.
My first thought is to use a shortcode inside a normal page, but I'm not sure how to get the pagination working.
Alternatively, is there a way to create a "generic" archive page? Do I need to get into rewrite rules?
Share Improve this question asked Mar 3, 2012 at 0:32 MattMatt 4651 gold badge5 silver badges13 bronze badges 2- Maybe I reuse the archive page with a query param. I could htaccess rewrite the param to a pretty url. – Matt Commented Mar 3, 2012 at 0:34
- I've added my own solution. I'm open to improvements on it. – Matt Commented Mar 4, 2012 at 18:20
3 Answers
Reset to default 2I had this same issue of needing two archive pages for a custom post type. I was able to accomplish this fairly cleanly using four WP hooks
For the example my custom post type is "member". I need a second archive url for members that have a custom meta_key "plus" set to true.
First we need to define the URL for the second archive url. Notice passing the "plus_member" var in the query string.
add_filter('rewrite_rules_array', function($rules) {
return array_merge(array(
'plus-members/?$' => 'index.php?post_type=member&archive_type=archive&plus_member=1',
'plus-members/page/([0-9]{1,})/?$' => 'index.php?post_type=member&archive_type=archive&plus_member=1&paged=$matches[1]',
), $rules);
});
Next we need to allow the "plus_member" we are passing along
add_filter('query_vars', function($vars) {
$vars[] = 'plus_member';
return $vars;
});
Next we need to tell WP to use a different template file for this request instead of the default "archive-member.php"
add_filter('template_include', function($template) {
global $wp_query;
if ($wp_query->get('post_type') == 'member' && $wp_query->get('plus_member')) {
$template = locate_template('archive-plus-member.php');
}
return $template;
});
Finally we need to alter the main query so we are not showing all members but only plus members.
add_filter('pre_get_posts', function($query) {
if ($query->is_main_query() && $query->is_archive() && $query->get('post_type') == 'business') {
if ($query->get('plus_member')) {
$query->set('meta_key', 'plus');
$query->set('meta_value', true);
}
}
return $query;
});
This should produce the result:
/members/ (loads) archive-member.php (showing) all members
/plus-members/ (loads) archive-plus-member.php (showing) all members where meta_key plus == true
I would use a query parameter, something like ?time=future
maybe, to shift your query. The difference between the future posts query and the past posts query should just be the orderby
and the meta_query
, so it should be relatively easy to change that based on the URL parameter. You also have the added benefit of reducing overhead by keeping it to one page. My only concern with this would be linking in the menu, as you'd have to use custom links, and those are less flexible.
This is what I went with. It creates a second archive for past events. It shows upcoming events in the main event archive, and old events in the past events page. Sorting is ascending for the main archive (so you see the next upcoming event first), and descending for the past events page, so you see the most recent event first. It allows for paging on the past events page. Note that is does not modify the query in the admin system.
new My_Events_Are_Special;
class My_Events_Are_Special {
function __construct() {
add_filter('rewrite_rules_array', array($this, 'insert_rewrite_rules'));
add_filter('query_vars', array($this, 'insert_query_vars'));
add_action('wp_loaded', array($this, 'flush_rules'));
add_filter('posts_join', array($this, 'posts_join'));
add_filter('posts_where', array($this, 'posts_where'));
add_filter('posts_orderby', array($this, 'posts_orderby'));
}
function can_modify_query() {
return !is_admin() && is_post_type_archive('event');
}
// create rules for the archived events page
var $rewrite_rules = array(
'events/archive$' => 'index.php?post_type=event&archive_type=archive',
'events/archive/page/([0-9]+)$' => 'index.php?post_type=event&archive_type=archive&paged=$matches[1]',
);
// insert rules into rewrite system
function insert_rewrite_rules($rules) {
return $this->rewrite_rules + $rules;
}
// add special query var to system
function insert_query_vars($vars) {
array_push($vars, 'archive_type');
return $vars;
}
// flush rules if any are new
function flush_rules() {
$rules = get_option('rewrite_rules');
$flush = false;
foreach ($this->rewrite_rules as $rule => $rewrite) {
if (!isset($rules[$rule])) {
global $wp_rewrite;
$wp_rewrite->flush_rules();
break;
}
}
}
// add start and end type to query for events (not in admin)
function posts_join($join) {
global $wpdb;
if ($this->can_modify_query()) {
$join .= " JOIN $wpdb->postmeta starts on ($wpdb->posts.ID = starts.post_id AND starts.meta_key = '_starts') ";
$join .= " JOIN $wpdb->postmeta ends on ($wpdb->posts.ID = ends.post_id AND ends.meta_key = '_ends') ";
}
return $join;
}
// only show future events for the main archive, only past events for the "archive archive"
function posts_where($where) {
global $wpdb;
if ($this->can_modify_query()) {
$compare = get_query_var('archive_type') == 'archive' ? '<' : '>';
$where .= " AND ends.meta_value $compare ".time();
}
return $where;
}
// main archive is ordered ascending on event start date, "archive archive" is ordered descending
function posts_orderby($orderby) {
global $wpdb;
if ($this->can_modify_query()) {
$order = get_query_var('archive_type') == 'archive' ? 'DESC' : 'ASC';
$orderby = "starts.meta_value $order, $wpdb->posts.post_date $order";
}
return $orderby;
}
}