0

So I have categories A, B, C and D. And also categories 1, 2, 3 and 4. I have posts that can belong to any four of these categories. But I want to exclude anything in categories B, 4 and D from the homepage (and only from the homepage), even though they are also in other categories.

I've been trying plugins and custom code and googling for hours, and I just can't get this to work. The last batch of attempts were variations on this:

 <?php $query = new WP_Query( 'cat=-32,-99' ); ?>
 <?php if ( $query->have_posts() ) : while ( $query->have_posts() ) : $query->the_post(); ?>
 <?php if (! in_category ('-32')); ?>
 <?php x_get_view( 'ethos', 'content', get_post_format() ); ?>
 <?php endif; ?>
 <?php endwhile; 
 wp_reset_postdata();
 else : ?>
 <p><?php _e( 'Sorry, no posts matched your criteria.' ); ?></p>
 <?php endif; ?>

Any suggestions would be most welcome.

1

Your problem is your query, but before I cover that, there are some concerns that are getting in the way of seeing the problem.

Firstly, every single line has . It's like hanging up and dialling the number again at the end of every sentence in a telephone conversation, and it's so much more effort to type and read. So lets fix that:

<?php
$query = new WP_Query( 'cat=-32,-99' );
if ( $query->have_posts() ) :
    while ( $query->have_posts() ) :
        $query->the_post();
        if (! in_category ('-32')) ;
        x_get_view( 'ethos', 'content', get_post_format() );
        endif;
    endwhile;
    wp_reset_postdata();
else :
    ?> <p><?php _e( 'Sorry, no posts matched your criteria.' ); ?></p><?php
endif;

This is much easier to read, and reveals a new bug you were unaware of. You've been using the shorthand syntax here:

if (! in_category ('-32')) ;
x_get_view( 'ethos', 'content', get_post_format() );
endif;

But notice the if statement has a ; not a :? This evaluates to:

if ( ! in_category( '-32' ) ) {
    // do nothing
}
x_get_view( 'ethos', 'content', get_post_format() );// this always happens

You can also pass -32 rather than '-32' into in_category. I strongly recommend using if () {} syntax as it's more widespread, better supported in tools and editors, and easier to type. Don't make extra work for yourself.

You're also checking if the post is inside the category -32, do you mean to check against 32?

Moving on to your original problem:

$query = new WP_Query( 'cat=-32,-99' );

Here cat is being used, however this isn't the best way to define it, and technically cat should take a single parameter. Instead define it this way:

$query = new WP_Query( array(
    'category__not_in' => array( 32, 99 )
) );

My final concern is that you've hardcoded the IDs of the category terms. This code will fail spectacularly if you ever tried an import/export. This is a bad case of magic numbers

Instead, perhaps use the category slugs instead:

$example_term = get_term_by( 'name', 'example1', 'category' );
$example_term_2 = get_term_by( 'name', 'example2', 'category' );
$query = new WP_Query( array(
    'category__not_in' => array( $example_term->term_id, $example_term_2->term_id )
) );

While category slugs/names are still bad, they're much more portable and robust than category term IDs. Ideally you would pull these from an option value or a post meta value instead.

The Final and Most Important Issue

All of this, is exactly the same as doing this:

query_posts( array(
    'category__not_in' => array( 32, 99 )
) );

You've discarded all the work WordPress did then did it a second time yourself.Instead, you should be using the pre_get_posts filter to tell WordPress what you actually wanted, putting it all together giving us:

add_filter( 'pre_get_posts', function ( $query ) {
    if ( $query->is_home() && $query->is_main_query() ) {
        $example_term = get_term_by( 'name', 'example1', 'category' );
        $example_term_2 = get_term_by( 'name', 'example2', 'category' );
        $query->set( 'category__not_in', $example_term->term_id, $example_term_2->term_id );
    }
});

This allows you to use the main loop rather than a custom WP_Query loop, simplifying your original code even further

Further reading:

  • Le sigh. I need to learn to walk away from a problem earlier, instead of tangling myself in it like a kitten with a ball of yarn. I've never used the pre_get_posts filter before, thanks for pointing that out! – Megan Taylor Aug 10 '14 at 21:06
  • Tom, I'm having trouble figuring out exactly how to use pre_get_posts. I've added it to my functions.php file, but am not sure how to modify my query in index.php to call it...? – Megan Taylor Aug 10 '14 at 21:33
  • It doesn't sound like you've learnt how hooks and filters work, pre_get_posts modifies queries before they happen, replace your custom query with a normal standard post loop – Tom J Nowell Aug 10 '14 at 22:13
  • Nice answer - I like the telephone metaphor ;-) ps: it looks like you still got these minuses in category__not_in. – birgire Aug 10 '14 at 22:29
  • Nice catch, I fixed that and added a further reading section – Tom J Nowell Aug 10 '14 at 22:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.