Excluding a category in WordPress

Over on my beer blog I have an entire section of "Press Releases" that I post but which I don’t want to show up on the main page or in the RSS feed—I don’t want to spam readers with excessive marketing but I like have the repository.

Since I’m now using WordPress, I figured I’d simply grab a plugin that would do the work for me: exclude the category from my "content" portions of the site (like the front page and the feed) but still let people access that category directly so they could see the Press Releases if they so chose.

To that end, I’d been using the "Category Visibility-iPeat Rev" plugin which seemed to do just the trick: I could configure exactly what category shows up in what area: front page, sidebar listings, search results, feeds, and archives. So I’d been using that up until now when I noticed a couple of bugs:

  • The individual category feeds weren’t working; that is, when you went to a specific category and added "/feed" to the end of the URL, the RSS feed would load for that category but it would be empty. These normally work in WordPress, and it’s a nice feature to have, and I wanted/needed it to work.
  • The plugin specifically excludes tags entirely (though unintentionally): any defined tags in the system were getting recorded into the database with all options off. I’m not using "tag browsing" anywhere on the site (yet), so this didn’t affect me, but I might in the future and other people are complaining about it.

So I spent a little bit yesterday looking at the Category Visibility plugin code to see if I could fix the problem, and then looking at just adding the exclusion code myself to the theme so I could deactivate and remove the plugin entirely.

I couldn’t find anything overtly wrong with the plugin code itself, and I didn’t want to spend too much time digging around WordPress’s core and driving myself insane, so I turned to adding exclusion code to my theme files.

You want to exclude the category ID from the query before the code even runs what WordPress ominously calls "The Loop", so a call to query_posts() is in order. To exclude a specific category, you’ll do this:

query_posts('cat=-3');

The minus sign in front of the category ID (3 in this case) is the exclusion operator. If you omitted it, you’d be telling the query to only include category ID 3.

So I dropped this line inside some logic to check if it was on the home page or the archives page (is_home() and is_archive(), appropriately enough) and then ran into another problem: paging back through "Older entries" was broken.

Turns out just setting 'cat=-3' overrode the entire query WordPress was working with, so telling it to go to "/page/2" wasn’t registering. I dug around online and instead this is what you should do:

query_posts($query_string . '&cat=-3');

That preserves any other query variables that were passed to the system, like what page you were on, and still appends your category exclusion logic. Worked like a charm.

All that was left for my goals was to exclude the Press Releases category from the site’s main feed. After digging around online some more, I determined that a filter hook needed to be applied to the feed query, and found some example code which I adapted to this:

function exclude_pr_feed( $query )
{
  if ( $query->is_feed ) {
    $query->set('cat', '-3');
  }
  return $query;
}
add_filter('pre_get_posts', 'exclude_pr_feed');

That snippet was put into the theme’s functions.php file and performs the same exclusion logic as above, only when posts are being pulled and the feed output is being built. So far it suppresses Press Releases from the main feed but hasn’t affected the individual category feeds, including that for Press Releases. Which is perfect for my purposes.

The nice thing is that this is overall a relatively minimal impact to the system and I save the overhead of Yet Another Plugin. And hopefully this will prove useful to someone else who wants to accomplish the same thing.