Oddness in The WordPress Posts Loop and WP Query

While working on the new version of Shortcode Revolution we faced a couple of confusing behaviors of WordPress functions. I’ll document them here and will keep adding such information. Hopefully it can help other developers and save them a lot of time and frustration.

Excluding posts from WP Query (using post__not_in)

There is a good comment here explaining that post__not_in wouldn’t work when used with post__in in the same query. This is a good note but our query did not include post__in and post__not_in was still simply ignored. I did not dig deep into the code to see why this happens – the conclusion was simply not to use it.

Here is an example how we get 3 random related posts in Shortcode Revolution:

$query = [];
// we add 1 to the default number of posts which is 3 or to the user-passed number or posts
$query['posts_per_page'] = (empty($atts['num']) or !is_numeric($atts['num'])) ? 4 : intval($atts['num']) + 1;       
$query['orderby'] = 'rand';

// getting the posts here
$wp_query = new WP_Query($query);
$posts = $wp_query->posts;

// now because this is "related posts" query, we don't want the current post to be repeated below it. So let's filter it out
// $post_id is a variable we have defined earlier in the shortcode and it contains the current post ID
// we are using array_filter with a closure here. You can do it inside a loop with a counter but this solution is more elegant
$posts = array_filter($posts, function($p) use($post_id) {				
  return ($p->ID != $post_id);					
});

// if the current post was not there we have one more than needed
if($query['posts_per_page'] < count($posts)) array_pop($posts);

 

So that’s it – you just get more posts than you need and then remove the unwanted ones in PHP.

Showing the excerpt outside of the post loop – get_the_excerpt

If you look at the documentation it sounds like you can call get_the_excerpt with any post ID and receive the excerpt for that post id. Good luck! You may get the proper excerpt (sometimes!) but won’t get the automatically generated “read more” link properly. The first version of our code was this and it did not work:

foreach($posts as $p):
   $background_image =  has_post_thumbnail( $p->ID )  ?  get_the_post_thumbnail_url($p->ID) : ''; 
   $excerpt = get_the_excerpt($p->ID);
.....

I intentionally did not use $posts as $post to avoid overriding the global $post variable but apparently this was wrong. Fortunately this comment gave me the hint how to fix it:

foreach($posts as $p):
   // yes, do override the global!
   $post = $p;
   // and you need to setup_postdata($post)
   setup_postdata($post);
   $background_image =  has_post_thumbnail( $p->ID )  ?  get_the_post_thumbnail_url($p->ID) : ''; 
   $excerpt = get_the_excerpt($p->ID);
......
endforeach;

And after the end of the loop don’t forget to reset the post data:

wp_reset_postdata();

 


Leave a Reply

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