The next thing to do is something we haven’t talked about yet and that is to exclude categories from your Posts Page. This is a common request and is done frequently incorrectly. In fact, if you’ve been watching me for awhile, you have seen me demonstrate this incorrectly. Now I want to show you the right way to exclude categories from your Posts Page.
Do Not Show Uncategorized Posts
Right now, what happens when we look at our Articles page here and we scroll down, we’ve got this Hello World post that shows up and it’s showing up in uncategorized. You don’t want uncategorized to show up at all. If we’ve got a post that’s not properly categorized then you don’t want it to show up and this way, you can exclude this whole uncategorized notion from the display of this page.
Now, the way that you may have seen it demonstrated in the past prevents pagination from happening correctly which you never notice when you’re first starting a site with a blog. We only have a few blog posts so it doesn’t show up right away but if you don’t use this system I’m going to show you right now, your blog page won’t paginate correctly. When you go to the previous posts link, it will just show you the same post you’ve already seen.
Use a Filter to Exclude the Categories from Posts Page
The way we’re going to exclude the categories from posts is by using the pre_get_posts filter. Actually, I should probably just show you first here. We really need to just start this whole thing off by creating a new file and adding it to our functions page. Come over here to functions and we’ve got our footer, our front page, our header and our sidebars.
Now, we’re going to create a new PHP file called crtc_ posts_page_html. That’s going to do exactly the same thing as our front page does here, it’s going to lay out the changes we’re making to HTML.
Before this will ever work, obviously, we have to include it in our functions.php file and let’s go ahead and include once, copy this, include_once (CRTC FUNCTIONS . ‘/crtc_front_page_html.php’); so now we have that included. We could save it and we’re going to add our first function here.
Wrong Way of Excluding Categories from Posts Page
Let’s take a look at the pre_get_posts filter and I should show you what people often do. We’ll create a function, function wrong_way_to_do_this and it’ll say, if (is_home), that is the same thing as if is the posts page. The front page is our static front page, home in WordPress is the posts page. Then you would call query_posts and you’d have some query string so it would probably look like this, cat = -1. Let’s look it up under WP query which is where we really should be looking at anyway.
It’s definitely, cat = -1, this the category, minus means exclude and 1 is the category id for that category. So, if (is_home) query_posts(‘cat = -1’); and what this does is theoretically, this changes the post query so that it excludes number 1 from the categories.
If you add_action(‘thesis_hook_before_content’, ‘wrong_way_to_do_this’); this is the way you’ll see this described very regularly. This is going to appear to produce appropriate results but it’s going to fail. Let’s go back to query_posts for a second, this is what it looks like, query_posts($query_string . ‘&cat=-1’).
Actually, it’s probably best to do this thesis_hook_before_html and we want to get rid of those spaces. I forgot about that, I don’t use this notation very often. What this is doing is this is taking query_posts, grabbing the global query_string and adding this value to the query_string which is category=-1.
Upload that and you can see now the number 1 has been excluded and you’d say, “fine, that’s working great”. Let’s go back over here to the site for a second and let’s go to Reading Settings again then come over to our Design Options and change that to 3.
You can see what happens if we have Home Page Displays 3. Come back over here to our Articles page, 1, 2, 3 posts showing up. If we click on our previous entries, we have the same 1, 2, 3 posts showing up. It doesn’t take us over to our fourth entry and that is one of the foibles of modifying the query.
Back to the Correct Way of Excluding Categories
Instead of doing it the wrong way, we’re going to do it the right way and that is to use the WordPress pre_get_posts filter. What the pre_get_posts filter does is allow you to change the query because it’s a filter on the query. You make the change after the query is constructed but before the query is executed which is what’s critical for making the pagination work because pagination is part of the query.
When you modify the query this way, what it ends up doing is resetting the pagination every time but if you modify the query by filtering the query, the pagination values are preserved.
We’re going to take a look at this pre_get_posts filter for a second at a WordPress section. There’s a pre_get_posts action and a pre_get_posts filter and they have the same name. What we’re going to do is use the filter version of this. There’s no documentation on the filter version so that’s why we are looking at the actual reference because there’s still plenty of good information here.
What this does is it takes the query and it allows you to modify, change, add to the query and it does so in a variety of ways. One of the things that it lets you do is use this set value inside the query. If we go back to WP_Query for a second, WP_Query is the class that creates the query and every single query on your page runs through WP_Query.
Query has properties and it has methods and this pre_get_posts filter allows you to access the methods in here. You can also access the properties, so you can access either the properties or the methods. When you’re accessing the methods, you can set any of the named query variables by using the set command then the query variable you want to set and the value you want to set for that.
That’s what’s going on here, we are first testing to make sure that this is the home page so if the query is home and the other thing we want to do is do this on the main query. In fact, you might not ever want the uncategorized to show up on your home page whether it’s in a widget or it’s in some other secondary loop.
Setup Variable in the Main Query
If that’s the case, you would simply say, if (is_home) and query set-> cat does not include uncategorized. This will make it work not only on the main query but on every single query on the page. Every time the query is called, whether it’s in a widget or a secondary loop or anyplace else, it will exclude category number 1 from that query.
However, if you only want to do this from the main page loop or the main query on the page and not any of the secondary queries, you can use this conditional is_main_query. What we’re doing here is, if the query is home and if it’s the main query, then set the query here. The named variable that we are setting is cat in this case and the value we want to apply to cat is -1 so exclude that one.
Then we return the query just like in any other filter, we’ll get the filter here, as in query. This is the value of this filter before it’s edited and this is our edited filter then we return the filter back. That’s how this filter works then we just add the filter to the pre_get_posts and call our function name.
Now, I just want to draw your attention to a couple of things here on the codex. The first thing I want to draw your attention is the fact that it says, the $query object is passed to your function by reference. You don’t need to declare variables and you don’t need to return value. Some place up here it says, no return value is necessary. Except that this is the pre_get_posts action, not the pre_get_posts filter. The pre_get_postsfilter, you always return the value of the filter.
Difference Between the Action and the Filter
The first time I worked with this it confused me. I didn’t recognize the difference between the action and the filter and I couldn’t figure out why it didn’t my thing work. Now, I get it, hopefully you’ll get it too. This is after I recognized that there is a difference between the pre_get_posts action and the pre_get_posts filter.
One of the things that doesn’t change in the pre_get_posts action and filter are the named variables. These are all the named variables you could set, you could set page, page name, attachments or post parent or tag id or author name. You could set really any of these potential variables with that set statement.
We happen to be setting cat which is this one here but you could set any of them if you chose. The full list of the possible things that you could set with a pre_get_posts filter shows up here on this WP_Query Object List.
Anyway, there’s our function. Let’s save our function, upload it and test it. Now, because we’re trying to see page 2, the paginated system is working. Here we are at our first 3 entries and if we hit previous entries, it takes us to page 2 and now it shows us that fourth entry but it’s still excluding the uncategorized category.
You could use this to exclude any kinds of categories from your post or any tags from your post. There are all kinds of things that you can exclude that you can use this for. I wanted to show you this system because the pre_get_posts filter is very poorly documented.
I see this all the time where people are trying to exclude a category or exclude something from their Posts page and it just doesn’t work for them, it fails to do the pagination correctly. The reason is because they are using the wrong way to modify the query. Using the pre_get_posts filter is the only right way to do this.