WordPress pre_get_posts + Combined Category Archives
This is a quick post following from the latest redesign of my WordPress plugins website, Plugin Planet. There, I make use of WordPress great query functionality to display archives of multiple categories. For example, here is the combined archive for BBQ Pro and Docs. So that category archive displays all documentation posts for BBQ Pro. Likewise for other categories, for example here is the combined archive for Banhammer Pro and Tutorials. That category archive displays all tutorial posts for Banhammer Pro. These combined category archives are used for all pro plugins.
Contents
- Combining categories via URL
- Modifying combined category queries
- Problem with combined categories
- Workaround solution
- Better solutions?
Combining categories via URL
To get a better idea of how it works, consider the following URL:
https://plugin-planet.com/category/docs+bbq-pro/
Combining the categories docs
and bbq-pro
, gives us the archive view for BBQ Pro Documentation. Another quick example:
https://plugin-planet.com/category/tuts+banhammer-pro/
Combining the categories tuts
and banhammer-pro
, gives us the archive view for Banhammer Pro Tutorials.
The trick here is to use the plus sign +
to combine category slugs in the URL. You can combine any number of categories, and awesome WordPress will output the correct posts. Incidentally, WordPress also will output RSS and Atom feeds when /feed/
is appended to the URL. For example:
https://plugin-planet.com/category/tuts+blackhole-pro/feed/
That calls up the RSS feed for the combined category archive for all Blackhole Pro tutorials. For more information, check out What is My WordPress Feed URL?
Modifying combined category queries
All good so far. But I also need to modify the query of my combined category archives. Specifically, I want to customize the number of posts that are displayed for various category views. For single-category archives, WordPress makes this easy:
function shapeSpace_set_posts_per_category($query) {
if (!is_admin() && $query->is_main_query() && $query->is_category()) {
if (is_category('news')) $query->set('posts_per_page', 5);
if (is_category('docs')) $query->set('posts_per_page', 6);
if (is_category('tuts')) $query->set('posts_per_page', 8);
if (is_category('forum')) $query->set('posts_per_page', 8);
}
}
add_filter('pre_get_posts', 'shapeSpace_set_posts_per_category');
This code snippet uses the filter hook pre_get_posts and the function is_category() to match specific individual categories and set the desired number of posts displayed per page. This is routine stuff. No surprises here.
Problem with combined categories
The problem is when you try using is_category()
with multiple category names. It just doesn’t work. So you can check if the category is any single category slug like docs
or tuts
. But you can’t check if the category is a combined category like docs+bbq-pro
or tuts+bbq-pro
. For example if we try the following query:
https://plugin-planet.com/category/tuts+blackhole-pro/
..the result is a bunch of errors like:
PHP Notice: Trying to get property 'slug' of non-object in /wp-includes/class-wp-query.php on line 3765
PHP Notice: Trying to get property 'name' of non-object in /wp-includes/class-wp-query.php on line 3763
PHP Notice: Trying to get property 'term_id' of non-object in /wp-includes/class-wp-query.php on line 3761
Workaround solution
So back to Plugin Planet and my silly desire to customize the number of posts per combined-category archive page. How to make it work? The solution is to workaround is_category()
with some alternate PHP logic:
function shapeSpace_set_posts_per_category($q) {
if (!is_admin() && $q->is_main_query()) {
$vars = $q->query_vars;
$cat = isset($vars['category_name']) ? $vars['category_name'] : null;
if (stripos($cat, 'news') !== false) $q->set('posts_per_page', 5);
if (stripos($cat, 'docs') !== false) $q->set('posts_per_page', 6);
if (stripos($cat, 'tuts') !== false) $q->set('posts_per_page', 8);
if (stripos($cat, 'forum') !== false) $q->set('posts_per_page', 8);
}
}
add_filter('pre_get_posts', 'shapeSpace_set_posts_per_category');
Basically this alternate technique replaces is_category()
with query_vars
category_name
, which then is checked using PHP’s stripos(). I encourage you to compare this alternate code with the original technique. This code has been in place at Plugin Planet for over a year now and seems to be working perfectly.
Better solutions?
If you have a better solution for modifying combined category queries with WordPress, please share in the comments below (if open), or send a quick email via my contact form. Thank you kindly :)