I’ve been using the following query to try to output only posts that are marked as stickies:

<?php
    $args = array(
        'post_type' => 'post',
        'posts_per_page' => 2,
        'post__in' => get_option('sticky_posts')
    );
?>

<?php query_posts($args); ?>
<?php if(have_posts()) : ?>

    <?php get_template_part('loop', 'feed-top-stories' ); ?>

<?php endif; ?>
<?php wp_reset_query(); ?>

I currently have no posts set as stickies on the website. Which tells me that nothing should show up in the loop. However, I found that it was pulling in 2 posts anyway (even though they weren’t stickies)

So then I switched to a WP_Query object (as I was advised to avoid query_posts() in the past)

<?php

    $args = array(
            'post_type' => 'post',
            'post__in' => get_option( 'sticky_posts' ),
            'posts_per_page' => 2
    );

    $the_query = new WP_Query($args);

    if ( $the_query->have_posts() ) : while ( $the_query->have_posts() ) : $the_query->the_post();

        get_template_part('loop', 'feed-top-stories' );

    endwhile; endif;

    wp_reset_postdata();

?>

But now it doesn’t appear to be working at all, the loop seems to be outputting 2 posts, however both of them are the page that is being viewed, despite me setting the post_type parameter

1
1

I currently have no posts set as stickies on the website. Which tells me that nothing should show up in the loop.

Exactly where you are wrong when passing an empty array to post__in. WordPress has some silly bugs which has no proper workaround and will most probably stay active bugs for a very long time. This is one of them.

When we pass a valid array of posts ID’s to post__in, we get the following resultant SQL WHERE clause (NOTE: All test are done on a page)

AND wp_posts.ID IN (59,45) 
AND wp_posts.post_type = \'post\' 
AND (wp_posts.post_status = \'publish\' OR wp_posts.post_status = \'private\')

Now, if we have no stickies, thus passing an empty array to post__in, the following SQL is wrongly generated because the post__in parameter as seen as not set.

AND wp_posts.ID IN (59) 
AND wp_posts.post_type = \'post\' 
AND (wp_posts.post_status = \'publish\' OR wp_posts.post_status = \'private\')

This is an epic fail with unexpected output. Depending on the parameters you passed you get totally unrelated posts or no posts at all.

That is why you should always validate what you are passing to post__in if the passed values are a dynamic array to avoid this epic failure.

Just another note which might not make any sense at all, if you just need to query sticky posts, always set ignore_sticky_posts to 1 (true). The reason for this is, should you require to not query all the sticky posts or just stickies from a certain category, you get just the ones you need. If you do not ignore stickies, all stickies will be returned regardless of what you have queried (yet another silly bug in my opinion).

You can rewrite your code as follow

$stickies = get_option( 'sticky_posts' );
// Make sure we have stickies to avoid unexpected output
if ( $stickies ) {
    $args = [
        'post_type'           => 'post',
        'post__in'            => $stickies,
        'posts_per_page'      => 2,
        'ignore_sticky_posts' => 1
    ];
    $the_query = new WP_Query($args);

    if ( $the_query->have_posts() ) { 
        while ( $the_query->have_posts() ) { 
            $the_query->the_post();

            get_template_part('loop', 'feed-top-stories' );

        }    
        wp_reset_postdata();    
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *