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.
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?
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 usingpre_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:
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