Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I got to two loops in my template files. Both querying the same thing essentially.

This loop in my header:

$testimonial_head = new WP_Query(array(
    'posts_per_page' => 1,
    'post_type'     => 'testimonial',
    'post_status'   => 'published',
    'orderby'   => 'rand'     
));

if ($testimonial_head->have_posts()) :

and this loop in my side bar:

$testimonial_sidebar = new WP_Query(array(
    'posts_per_page' => 1,
    'post_type'     => 'testimonial',
    'post_status'   => 'published',
    'orderby'   => 'rand'     
));

if ($testimonial_sidebar->have_posts()) : 

When these are running that same time, what would be a method of stopping the sidebar query showing the same post as the first header query?

I just never ever want a duplicate to be shown.

share|improve this question
add comment

3 Answers

up vote 2 down vote accepted

Your two queries are identical, meaning you only need one of them. The basic solution is:

global $testimonials;
$testimonials = new WP_Query(array(
    'posts_per_page' => 2, /* <-- get both */
    'post_type'     => 'testimonial',
    'post_status'   => 'published',
    'orderby'   => 'rand',
    'ignore_sticky_posts' => true /* you need this to control post count accurately */     
));

// header
if ($testimonials->have_posts()) {
  while($testimonials->have_posts()) {
    $testimonials->the_post();
    // whatever you need to do with the post content
    break; // stop the Loop; no complex logic needed since you are printing one and moving on
  }
}

// sidebar
// pretty much the same as the above
global $testimonials;
// this Loop will pick  up where the other left off
if (!empty($testimonials) && $testimonials->have_posts()) {
  while($testimonials->have_posts()) {
    $testimonials->the_post();
    // whatever you need to do with the post content
  }
}

There are probably better ways to do this. I'd strongly consider rolling the whole thing into a class and avoiding the global altogether.

class Testimonial_Loop {
  static $testimonials = false;

  function get_testimonials() {
    if (empty(static::$testimonials)) {
      static::$testimonials = new WP_Query(array(
        'posts_per_page' => 2, /* <-- get both */
        'post_type'     => 'testimonial',
        'post_status'   => 'published',
        'orderby'   => 'rand',
        'ignore_sticky_posts' => true
      ));
    }
    return static::$testimonials;
  }

  function loop() {
    $testimonials = self::get_testimonials();
    if ($testimonials->have_posts()) {
      while($testimonials->have_posts()) {
        $testimonials->the_post();
        the_title();
        // whatever you need to do with the post content
        break; // stop the Loop; no complex logic needed since you are printing on and moving on
      }
    }
  }

}

Testimonial_Loop::loop();
Testimonial_Loop::loop();

And a third solution using a function and a static variable:

function testimonial_loop() {
  static $testimonials = false;

  if (empty($testimonials)) {
    $testimonials = new WP_Query(array(
      'posts_per_page' => 2, /* <-- get both */
      'post_type'     => 'post',
      'post_status'   => 'published',
      'orderby'   => 'rand',
      'ignore_sticky_posts' => true
    ));
  }

  if ($testimonials->have_posts()) {
    while($testimonials->have_posts()) {
      $testimonials->the_post();
      // whatever you need to do with the post content
      break; // stop the Loop; no complex logic needed since you are printing on and moving on
    }
  }
}
testimonial_loop();
testimonial_loop();
share|improve this answer
    
+1 great solution to this. 3 additional notes: the class approach is overkill, it will use more memory and generally be slower. The while loops are unnecessary, you just have to run $testimonials->the_post() if you're getting one post. Lastly, don't forget to run wp_reset_postdata() after you're done with your testimonial code. –  Matthew Boynes Jun 5 '13 at 13:33
    
Points worth considering but the class solution runs at worst about 1/10000th slower that the other option, but it stays out of global scope, it represents less code, more maintainable code, more extensible code, and more functional code (the Loop is populated the first time it is used; the sidebar Loop isn't dependent upon the header Loop). The whiles are unnecessary, but convenient if you wanted to extend the code to print, say, 3 posts per cycle instead of just one, and the execution time without those whiles is virtually indistinguishable from the execution time with them. –  s_ha_dum Jun 5 '13 at 14:18
    
The class route here is actually about 2x slower (data-supported, but nothing I can link to), and while it keeps $testimonials out of the global variable scope, it adds a class to the global class space. Six of one, yadda yadda. The while tip was for OP, since he specifically only wants 1. I didn't mean to offend or step on toes; just wanted to add to your answer in a constructive way to help OP. –  Matthew Boynes Jun 5 '13 at 22:41
    
@MatthewBoynes : No offense taken. Like I said, points worth considering. I did look into it after your previous comment and I am not getting 2x the execution time for the class. I get about 1.2x for the class relative to the bare loop solution (data-supported but nothing I can link to). –  s_ha_dum Jun 6 '13 at 0:07
    
Instead of using while loops, couldn't you just use the next_post() method of the WP_Query object? e.g. $testimonials->the_post(); $testimonials->next_post(); $testimonials->the_post(); –  Marc Dingena Jun 6 '13 at 9:50
show 2 more comments

In the header loop, store the post id in e.g. $header_post_id

In your sidebar:

$testimonial_sidebar = new WP_Query(array(
    'posts_per_page' => 1,
    'post__not_in' => array($header_post_id), // exclude the post in header
    'post_type'     => 'testimonial',
    'post_status'   => 'published',
    'orderby'   => 'rand'     
));

if ($testimonial_sidebar->have_posts()) : 
share|improve this answer
    
This also worked. Simple –  Joshc Jun 11 '13 at 9:46
add comment

"Not a tested answer": You can try using a global variable in single template file. This variable will be used to store the ID of the post which is displayed in the header loop. While using your sidebar loop use post__not_in parameter for your WP_Query which will exclude the post displayed in header.

Edit: Refer WP_Query for more information. Refer this answer for setting the global variable.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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