WordPress: different WP_Query usage: Which is correct, in which context?

I have been creating custom WordPress loops with two different approaches, both which involve creating a new instance of the WP_Query object. I typically have multiple loops on a single page.

I do not understand how these two approaches differ, and which is the correct context to use each.

Read More

Approach 1: http://codex.wordpress.org/Class_Reference/WP_Query

$the_query = new WP_Query( $args );

if ( $the_query->have_posts() ) :
    while ( $the_query->have_posts() ) : $the_query->the_post();
        // output
    endwhile;
endif;

wp_reset_postdata();

Approach 2: http://codex.wordpress.org/Function_Reference/wp_reset_postdata

$original_query = $wp_query;
$wp_query = null;

$wp_query = new WP_Query( $args );
if ( have_posts() ) :
    while ( have_posts() ) : the_post();
        // output
    endwhile;
endif;

$wp_query = null;
$wp_query = $original_query;
wp_reset_postdata();

Both appear to yield the same results, however when I turn on WP_DEBUG, I see errors with the second approach such as:

Notice: is_singular was called incorrectly. Conditional query tags do not work before the query is run.

My questions are these:

  • when should I use the $original_query = $wp_query; approach?
  • what is the relevance of storing and restoring $wp_query?
  • why does is return the error message when I use it?

Related posts

Leave a Reply

1 comment

  1. Some core functions– some pagination ones, for example (here is an example where this seems to have been a factor)– assume the $wp_query value. If that isn’t set those functions don’t work. You can cheat around this by saving the original $wp_query, running the custom loop, and putting $wp_query back. I would argue that this is rarely the best way to do it, and that if you do think you need to do this chances are you should be using pre_get_posts, or other filters, to alter the main query itself instead of making a new query. I won’t swear that there absolutely are no cases where this would be appropriate, but I do think there is usually a better way.

    Your code does not trigger any notices when I try it but if you did something like this it would:

    $original_query = $wp_query;
    $wp_query = null;
    if (is_single()) {
      echo 'single post';
    }
    $wp_query = new WP_Query( array('posts_per_page' => 5) );
    

    Function like is_single, is_archive, etc. rely on the global $wp_query object, which has been unset. I expect it is in cases like that where you see the notices.

    Most everything you need for reference is in http://core.trac.wordpress.org/browser/trunk/wp-includes/query.php and http://core.trac.wordpress.org/browser/trunk/wp-includes/post-template.php