Arbitrarily exclude posts from displaying in WordPress

When hacking away at WordPress sites, often times you’ll find yourself in a situation where you need to filter out certain posts from displaying on some pages, such as the home page. There are a lot of ways to do this, but few are perfect. Recently, I had the need to do this and went searching for pre-existing solutions.

I came across Vaibhav’s post on the topic and noted that his solution uses the query_posts() function to alter WordPress’s query object before The Loop has run. While this is a great solution if your exclusion criteria is simple enough to be supported directly by the WordPress query object, other times the query_posts() function doesn’t provide you with the hook you need.

In these cases, you can run the original query, note any modifications you need to make, and then create a new, modified query and display the results you get from running that one instead. For instance, you might need to do this if you need to exclude posts based on category and, say, the beginning of their title, or their category and a certain piece of content in the post itself, or all three, or any other combination you can think of.

Another advantage of this technique over simpler ones is that this method maintains the same behavior you’d expect to see in every other way. Most notably, this means that if you’ve told WordPress to display the 10 most recent posts on the home page (in the WordPress settings), you’ll still see ten posts on that page even after you exclude some of them.

To do something like excluding posts if they are in the “Uncategorized” category (traditionally the category with an ID of 1 in WordPress) and their title begins with “Some title”, you can do this:

// original query runs in The (real) Loop first
while ( have_posts() ) : the_post();
    // detect pots matching our exclusion criteria
    if (in_category(1) && (0 === strpos(the_title('', '', false), 'Some title')) ) {
        $wp_query->post_count++; // increment the post counter
        continue;
    }
    endif;
endwhile;
// now make a new query and show the posts for real, with the adjusted post count and filtering
$my_new_query = new WP_Query($query_string.'&showposts='.$wp_query->post_count);
// do another The Loop (and display the results this time)
while ( $my_new_query->have_posts() ) : $my_new_query->the_post();
    // detect and exclude these same posts
    if (in_category(1) && (0 === strpos(the_title('', '', false), 'Some title')) ) { continue; }

// ...the rest of the WordPress template goes here...

This is neat because it gives you the capability to define arbitrarily complex exclusion patterns and directly modify your new query object however you like before you execute it. Once you know this works, you’ll probably want to extract the filtering code into a function. Using the above example, your new code might look like this:

// define criteria for filtering
function matches_filtering_criteria () {
    if (in_category(1) && (0 === strpos(the_title('', '', false), 'Some title')) ) {
        return true;
    } else {
        return false;
    }
}
// original query runs in The (real) Loop first
while ( have_posts() ) : the_post();
    // detect pots matching our exclusion criteria
    if (matches_filtering_criteria()) {
        $wp_query->post_count++; // increment the post counter
        continue;
    }
    endif;
endwhile;
// now make a new query and show the posts for real, with the adjusted post count and filtering
$my_new_query = new WP_Query($query_string.'&showposts='.$wp_query->post_count);
// do another The Loop (and display the results this time)
while ( $my_new_query->have_posts() ) : $my_new_query->the_post();
    //
    // detect and exclude these same posts
    if (matches_filtering_criteria()) { continue; }

// ...the rest of the WordPress template goes here...

For more information on these functions, see:

9 comments

  1. mccormicky says:

    Meitar Moscovitz,
    I’m confused a little about extracting the code to a function as you wrote.
    Is it as easy as copying and pasting into my functions.php file?
    Or do I still have to put the modified loop into the template?
    Basically I am trying to avoid showing so much duplicate content I do like to have custom templates but also want to have at least one page where everything can be found.But I read that having an archives page is bad for seo.
    Also the site I’m working on is not a blog even though it has a blog and posts appear on the home page(a static page using the loop with a custom query to show a few categories) and the same posts appear on the blog.I’d like to whittle it down to only one category showing on the “blog”.
    Maybe your loop is too much loop for what I need?
    The thing is, I can’t seem to get pagination is I use the get posts tag so I don’t want to use it.
    I guess I need the blog to be a mini blog and the home page to be the “home page blog that isn’t a blog”. If that makes sense I’ll buy you a tea.
    Any help or ideas would be really appreciated!

  2. Meitar says:

    @mccormicky:

    You can put the functions in a template file or in the functions.php. It doesn’t really matter as long as the place you put it is in scope so that they run from the template file. The best place is, indeed, probably the functions.php file since that’s the whole purpose of that file. :)

    Also I recently discovered that an update to WordPress 2.6 seems to have broken something in here, but I don’t know what. Maybe this technique doesn’t work anymore….

  3. Till says:

    Do you have an code example, that show the posts which have no comments? I want to install a place, where people can start a questions and the “comment” is the answer. and i want to list all questions, that have no answer (comment) an idea?

    many thanxs

    cu Till

Comments are closed.