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/420175/<?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 separateWP_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
.