I read @nacin’s You don’t know Query yesterday and was sent down a bit of a querying rabbit hole. Before yesterday, I was (wrongly) using query_posts()
for all my querying needs. Now I’m a little bit wiser about using WP_Query()
, but still have some gray areas.
What I think I know for sure:
If I’m making additional loops anywhere on a page—in the sidebar, in a footer, any kind of “related posts”, etc—I want to be using WP_Query()
. I can use that repeatedly on a single page without any harm. (right?).
What I don’t know for sure
- When do I use @nacin’s
pre_get_posts
vs. WP_Query()
? Should I use pre_get_posts
for everything now?
- When I want to modify the loop in a template page — lets say I want to modify a taxonomy archive page — do I remove the
if have_posts : while have_posts : the_post
part and write my own WP_Query()
? Or do I modify the output using pre_get_posts
in my functions.php file?
tl;dr
The tl;dr rules I’d like to draw from this are:
- Never use
query_posts
anymore
- When running multiple queries on a single page, use
WP_Query()
- When modifying a loop, do this __________________.
Thanks for any wisdom
Terry
ps: I have seen and read: When should you use WP_Query vs query_posts() vs get_posts()? Which adds another dimension — get_posts
. But doesn’t deal with pre_get_posts
at all.
You are right to say:
Never use query_posts
anymore
pre_get_posts
pre_get_posts
is a filter, for altering any query. It is most often used to alter only the ‘main query’:
add_action('pre_get_posts','wpse50761_alter_query');
function wpse50761_alter_query($query){
if( $query->is_main_query() ){
//Do something to main query
}
}
(I would also check that is_admin()
returns false – though this may be redundant.). The main query appears in your templates as:
if( have_posts() ):
while( have_posts() ): the_post();
//The loop
endwhile;
endif;
If you ever feel the need to edit this loop – use pre_get_posts
. i.e. If you are tempted to use query_posts()
– use pre_get_posts
instead.
WP_Query
The main query is an important instance of a WP_Query object
. WordPress uses it to decide which template to use, for example, and any arguments passed into the url (e.g. pagination) are all channelled into that instance of the WP_Query
object.
For secondary loops (e.g. in side-bars, or ‘related posts’ lists) you’ll want to create your own separate instance of the WP_Query
object. E.g.
$my_secondary_loop = new WP_Query(...);
if( $my_secondary_loop->have_posts() ):
while( $my_secondary_loop->have_posts() ): $my_secondary_loop->the_post();
//The secondary loop
endwhile;
endif;
wp_reset_postdata();
Notice wp_reset_postdata();
– this is because the secondary loop will override the global $post
variable which identifies the ‘current post’. This essentially resets that to the $post
we are on.
get_posts()
This is essentially a wrapper for a separate instance of a WP_Query
object. This returns an array of post objects. The methods used in the loop above are no longer available to you. This isn’t a ‘Loop’, simply an array of post object.
<ul>
<?php
global $post;
$args = array( 'numberposts' => 5, 'offset'=> 1, 'category' => 1 );
$myposts = get_posts( $args );
foreach( $myposts as $post ) : setup_postdata($post); ?>
<li><a href="https://wordpress.stackexchange.com/questions/50761/<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php endforeach; wp_reset_postdata(); ?>
</ul>
In response to your questions
- Use
pre_get_posts
to alter your main query. Use a separate WP_Query
object (method 2) for secondary loops in the template pages.
- If you want to alter the query of the main loop, use
pre_get_posts
.