Keep Pagination in the Same Taxonomy
Join your posts together by category (or tag), rather than relying on the default published date order when paging forwards or backwards.
Intro
So, for example, if you’ve written a series of “how to do X” articles over a period of time, this plugin will let you easily link them all together by virtue of sharing a term in any taxonomy.
Aside from the usual admin code (adding the config for this plugin to the existing Reading settings page), we use two hooks; one for the <link rel="prev">
and <link rel="next">
links generated by WP in the HTML head, and one for the visible Previous/Next links that would get displayed after the Post.
add_action('get_header', array($this, 'same_pagination'));
add_action('the_post', array($this, 'same_pagination'));
same_pagination()
The common function that only alters the relevant SQL when we’re on a webpage displaying a single post that has a taxonomy that we’ve previously chosen in the admin area.
public function same_pagination($post = null) {
if (! is_singular()) return; // bail if we're not looking at a single post
if (is_null($post)) global $post; // need this for the get_header call here
// get the taxonomies we've ticked
$selected = array_filter($this->settings);
// and the ones this post has
$taxonomy_names = array_flip(get_object_taxonomies($post));
// then see which ones apper in both lists
$matching_taxonomies = array_intersect($selected, $taxonomy_names);
if (sizeof($matching_taxonomies) > 0) {
// single request only cache (ie. caches the get_header call for use in the_post)
$taxonomy_termids = wp_cache_get($post->ID.'_kpist_taxonomy_termids');
if ($taxonomy_termids === false) {
$taxonomy_termids = array();
// retrieve all the terms this post has in the taxonomies we're interested in
$terms = wp_get_object_terms($post->ID, array_keys($matching_taxonomies));
// list_pluck can't handle multiple values per key
$terms = wp_list_pluck($terms, 'taxonomy', 'term_id');
// so we need to turn them into an array ourselves
foreach ($terms as $term_id => $taxonomy) {
if (! isset($taxonomy_termids[$taxonomy])) $taxonomy_termids[$taxonomy] = array();
$taxonomy_termids[$taxonomy][] = $term_id;
}
wp_cache_add($post->ID.'_kpist_taxonomy_termids', $taxonomy_termids);
}
$this->taxonomy_termids = $taxonomy_termids;
add_filter('get_next_post_join', array($this, 'filter_join_clause'), 10, 5);
add_filter('get_previous_post_join', array($this, 'filter_join_clause'), 10, 5);
add_filter('get_next_post_where', array($this, 'filter_where_clause'), 10, 5);
add_filter('get_previous_post_where', array($this, 'filter_where_clause'), 10, 5);
}
}
filters
Prepare our SQL joins for the following WHERE clause…
public function filter_join_clause($join, $in_same_term, $excluded_terms, $taxonomy, $post) {
global $wpdb;
$until = sizeof($this->taxonomy_termids);
for ($i=0; $i<$until; $i++) {
$join.= " INNER JOIN {$wpdb->term_relationships} AS kpist_tr{$i} ON p.ID = kpist_tr{$i}.object_id
INNER JOIN {$wpdb->term_taxonomy} AS kpist_tt{$i} ON kpist_tr{$i}.term_taxonomy_id
= kpist_tt{$i}.term_taxonomy_id";
}
return $join;
}
And here, filter the get_adjacent_post
query accordingly.
public function filter_where_clause($where, $in_same_term, $excluded_terms, $taxonomy, $post) {
global $wpdb;
$clauses = array(); $i = 0;
foreach ($this->taxonomy_termids as $taxonomy => $term_ids) {
// not that we use them, but if anything else is asking to remove terms, then honour that here
$term_ids = array_map('intval', array_diff($term_ids, (array) $excluded_terms));
// then check we have some terms left to try matching
if (sizeof($term_ids)) {
$clauses[]= $wpdb->prepare("(kpist_tt{$i}.term_id IN (".implode(',', $term_ids).")
AND kpist_tt{$i}.taxonomy = %s)", $taxonomy);
}
$i++;
}
if (sizeof($clauses)) {
// join the taxonomy clauses together
switch ($this->settings['kpist_toggle']) {
case 'any': $toggle = ' OR ';
break;
case 'all':
default: $toggle = ' AND ';
}
$where.= ' AND ('. implode($toggle, $clauses). ')';
}
return $where;
}
Note: the final SQL query made by WP does not contain a GROUP BY clause - and nor is there a direct filter to call - so if a post has multiple taxonomies in common, then we’ll return multiple identical Post IDs. Fortunately, it does have an ORDER BY and a LIMIT clause, so it’s not (yet) a problem.