Keep Pagination in the Same Taxonomy
![Header image for Keep Pagination in the Same Taxonomy](/assets/images/stock/wordpress-pexels-markus-winkler-4152505.webp)
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.