[Solved] When to use WP_query(), query_posts() and pre_get_posts


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

  1. Use pre_get_posts to alter your main query. Use a separate WP_Query object (method 2) for secondary loops in the template pages.
  2. If you want to alter the query of the main loop, use pre_get_posts.

13

solved When to use WP_query(), query_posts() and pre_get_posts