Technically Feasible

Filter a category from your WordPress Blog

Likeness of Michael Oldroyd
Michael Oldroyd
⚠️ This content was imported from a previous incarnation of this blog, and may contain formatting errors

I'm pretty new to working with the internals of WordPress. It seems very easy to settle for installing hundreds of plug-ins to achieve the simplest of tasks. Plug-ins are awkward for me, as they have the tendancy to not quite do what I want them to. I end up messing with them in some way which breaks updates.

A few days ago, I decided that I would like to replicate my Twitter content onto my site. I added a plug-in which facilitated the import, but I was finding that my main page was being swamped by Tweets. This was drowning out the actual post content (which coincidently I take a lot more effort to write!).

I did a little research, and found what on the surface seems like a good idea. The problem with this method is that it occurs in "the loop". Once you are in "the loop", the post query has been performed - and paginated. So you can only exclude results, and for each excluded result you have one less post on the page. For example: if there were 10 excluded posts on a page and a post limit per page of 10, the home page would be blank.

The solution was to formulate a little post query filter which does the task. The post query filters are great; they allow us to alter the post query prior to getting the post list and applying pagination. The function is currently hard-coded to the category I wanted to filter out; just copy-paste into functions.php of your theme, changing the number '30' for the ID of your category:

function filter_home_where($where) {
global $wpdb;
if (is_home() || is_feed()) {
$where .= " AND {$wpdb->posts}.ID NOT IN (
SELECT object_id
FROM {$wpdb->term_relationships} as tr
LEFT JOIN {$wpdb->term_taxonomy} as tt
ON tr.term_taxonomy_id = tt.term_taxonomy_id
WHERE tt.term_id = 30)";
return $where;
add_filter('posts_where', 'filter_home_where');

The function adds an additional WHERE clause to the query, in the form of a sub-query. This SELECTs a list of post id's from the term_relationships table (which contains the object_id, which is basically post_id for our use case), joined to the term_taxonomy table (for term_id, which is essentially category_id). Any post ID returned by the sub query will be excluded in the post list.

This isn't just limited to Categories. You can filter on any taxonomy data, such as tag, post formats, etc. It would be simple to tweak the query to apply multiple taxonomy exclusions in one pass too. I have been working on some form of admin area, which I am adding to the theme, so I hope to follow up with that once it's in place.

The only thing I noticed is that it doesn't filter the category from /feed, whereas it does filter /feed/atom and /feed/rss. I don't know if this is intentional or a bug. It's something I plan to investigate a little more. I haven't tried this where the home page has been overridden as a static page, either.

Image of me

Michael Oldroyd

Michael is a Software Engineer working in the North West of England. Michael spends his days building hand-crafted PHP applications. Rumours of his super-hero status are currently unconfirmed. He savours his victories when solving difficult programming challenges; occasionally writing about them here, on his personal blog.