A Deep Dive into
WordPress Loops
Micah Wood
@wpscholar
wpscholar.com
What is the WordPress Loop?
The Loop iterates over each post retrieved and sets up the context for template tags.
The Loop - Pseudo Code
If we have one or more posts:
For each post:
Otherwise:
Show a ‘No Posts Found’ message
WordPress Loop Examples
Standard Loop
<?php
if ( have_posts() ) :
while ( have_posts() ) :
the_post();
the_content();
endwhile;
endif;
Standard Loop
<?php
if ( have_posts() ) :
while ( have_posts() ) :
the_post();
the_content();
endwhile;
endif;
have_posts()
function
Determines whether there are more posts available in the loop.
the_post()
function
Moves to the next post and sets up global post data.
Standard Loop with Conditional
<?php
if ( have_posts() ) :
while ( have_posts() ) :
the_post();
the_content();
endwhile;
else:
_e(‘No posts found.’, ‘text-domain’);
endif;
Custom Query Loop
<?php
$query = new WP_Query({query args});
if ( $query->have_posts() ) :
while ( $query->have_posts() ) :
$query->the_post();
the_content();
endwhile;
endif;
wp_reset_postdata();
Custom Query Loop
<?php
$query = new WP_Query({query args});
if ( $query->have_posts() ) :
while ( $query->have_posts() ) :
$query->the_post();
the_content();
endwhile;
endif;
wp_reset_postdata();
WP_Query
class
Handles requesting posts from the database.
wp_reset_postdata()
function
Restores the global post and sets up the global post data.
Custom Collection Loop
<?php
global $post;
$posts = get_posts();
if ( $posts ) :
foreach ( $posts as $post ) :
setup_postdata( $post );
the_content();
endforeach;
endif;
wp_reset_postdata();
Custom Collection Loop
<?php
global $post;
$posts = get_posts();
if ( $posts ) :
foreach ( $posts as $post ) :
setup_postdata( $post );
the_content();
endforeach;
endif;
wp_reset_postdata();
get_posts()
function
Returns an array of posts based on the provided arguments.
Note: Bypasses query filters by default.
setup_postdata()
function
Sets up the global post data.
The Anti-Loop
<?php
the_post();
the_content();
The Evil Loop
<?php
query_posts({query args});
if ( have_posts() ) :
while ( have_posts() ) :
the_post();
the_content();
endwhile;
endif;
wp_reset_query();
query_posts()
function
A function you should never use.
Runs a custom query and overrides the global query after WordPress has already run the global query.
wp_reset_query()
function
Another function you should never have to use.
Only used to properly clean up after query_posts().
Why query_posts() is Evil
query_posts() function
pre_get_posts hook
pre_get_posts
hook
An action hook that allows you to manipulate a query for posts before it is run.
Customizing the Global Query
<?php
add_action(‘pre_get_posts’, function( $query ){
if( !is_admin() && $query->is_main_query() ) {
if( $query->is_tax( ‘category’ ) ) {
$query->set( ‘posts_per_page’, 15 );
}
}
});
Custom WordPress Loop Optimizations
Rule #1
Always restrict the number of posts returned.
Always restrict the number of posts returned
<?php
$query = new WP_Query([
‘posts_per_page’ => 100, // Good
‘posts_per_page’ => -1, // Bad
]);
Rule #2
Limit the number of queries run when possible.
Queries Run by WP_Query
Limit the number of queries when possible
<?php
$query = new WP_Query([
// Add if sticky posts aren’t needed
‘ignore_sticky_posts’ => true,
]);
Limit the number of queries when possible
<?php
$query = new WP_Query([
// Add if pagination isn’t needed
‘no_found_rows’ => true,
]);
Limit the number of queries when possible
<?php
$query = new WP_Query([
// Add if post meta isn’t needed
‘update_post_meta_cache’ => false,
]);
Limit the number of queries when possible
<?php
$query = new WP_Query([
// Add if term data isn’t needed
‘update_post_term_cache’ => false,
]);
Limit the number of queries when possible
<?php
$query = new WP_Query([
// Add if post and term data isn’t needed
‘cache_results’ => false,
]);
Rule #3
Limit the number of joins in the query.
Limit the number of joins when possible
<?php
$query = new WP_Query([
// Each meta query dimension adds a join
‘meta_query’ => [[
‘key’ => ‘color’,
‘value’ => ‘blue’,
]],
]);
Limit the number of joins when possible
<?php
$query = new WP_Query([
// Each tax query dimension adds a join
‘tax_query’ => [[
‘taxonomy’ => ‘people’,
‘field’ => ‘name’,
‘terms’ => ‘Bob’,
]],
]);
Rule #4
Only return the required fields.
Only return the required fields
<?php
$query = new WP_Query([
// Only return the post IDs
‘fields’ => ‘ids’,
]);
$ids = $query->posts; // Array of post IDs
// NOTE:
// Looping through this query will trigger
// a new query for each post via get_post().
A Deep Dive into
WordPress Loops
Micah Wood
@wpscholar
wpscholar.com
Resources