I recently ha a use case where I needed to prevent a certain category from being displayed in the front-end. In detail I needed to exclude posts assigned to that category from
- the main query (“the Loop”)
- search queries
- category queries
- archive queries
- post navigation (
next/prev_post_link()
)
and the category name itself from being displayed as item of
the_category()
in theme templates
- the Categories Widget (list and drop-down)
As a consequence I pretty soon found myself on a quest the results of which I’d like to share here and expose to further feedback.
Excluding categories with pre_get_posts()
I found that excluding a category via pre_get_posts()
and set_query_var()
would work fine except for widgets. The Recent Post Widget would only exclude the category when using $query->set()
instead.
<?php
/**
* Does NOT apply to the Recent Posts widget.
*/
function glck1403271109_exclude_categories( $query ) {
$excluded = array( '1', '2' );
if( ! is_admin() )
set_query_var( 'category__not_in', $excluded );
}
add_filter( 'pre_get_posts', 'glck1403271109_exclude_categories' );
/**
* Does apply to the Recent Posts widget.
*/
function glck1403271122_exclude_categories( $query ) {
$excluded = array( '1', '2' );
if( ! is_admin() )
$query->set( 'category__not_in', $excluded );
}
add_filter( 'pre_get_posts', 'glck1403271122_exclude_categories' );
Excluding categories from queries and widgets
Excluding categories from queries and widgets took me a couple of functions I wrapped into a mini plugin. The code can be found in a Gist here.
Excluding categories from post navigation (next/prev_post_link()
)
next/prev_post_link()
and their underlying get_
functions all rely on get_adjacent_post()
which up to today (WordPress 3.9.1) does not use WP-Query
, but fixes its own SQL query. Trac Ticket #26937 aims to get get_adjacent_post()
to use WP_Query
, but it might take a while until we’re there.
Excluding categories from next/prev_post_link()
in my case could sufficiently be achieved passing category IDs to be excluded directly to the function. I’m sure this could also be down from a plugin interacting with get_adjacent_post()
directly. Here’s my take for the template tag. Checking for glckprss_exclude_categories__category_names()
from the previously mentioned mini plugin, of course, makes only sense when the latter is in use.
<?php
/**
* Exclude categories from prev/next post links.
*
* $exclude (array) - category slugs to retrieve IDs from
* $excluded (array) - category IDs to be excluded
*/
$exclude = array();
$excluded = array();
// Mini plugin active?
if( function_exists( 'glckprss_exclude_categories__category_names' ) ) {
$exclude = glckprss_exclude_categories__category_names();
else {
$exclude = array(
get_category_by_slug( 'my-category' ),
get_category_by_slug( 'my-other-category' )
);
}
// Retrieve IDs
foreach( $exclude as $category ) {
if( $category )
$excluded[] = absint( $category->term_id );
}
/* Next Post */
next_post_link( '%link', '%title', false, $excluded );
/* Previous post */
previous_post_link( '%link', '%title', false, $excluded );
Menus
For my use case it wasn’t necessary to exclude categories from menus as I had custom menus only, so I haven’t investigated in that direction. I’m pretty sure pre_get_posts()
gets a grip on menu items, though, doesn’t it?
Thanks for reading all this, appreciate any comments!