How to alter the main WordPress query the correct way. No more query_posts() pwease!
by Scott Cariss of Philosophy Design
Philosophy is a thought-led brand and digital consultancy based in London.
2. Scott Cariss aka Brady
Scott Cariss aka Brady
• Lead developer at Philosophy Design.
dd l hil h i
• Moderator at WordPress Answers
( p
(http://wordpress.stackexchange.com/)
p g )
• WordPress plugin developer and enthusiast
WordPress plugin developer and enthusiast
scott@philosophydesign
@ hil h d i
@l3rady on Twitter
12. If you do:
$my_query new WP Query( $query );
$my query = new WP_Query( $query );
You can do:
while ( $my_query >have_posts( ) ) :
while ( $my query‐>have posts( ) ) :
$my_query‐>the_post( ); endwhile;
wp_reset_postdata( );
13. But why do we call things like
h d ll hi lik
wp_reset_postdata( ) and
p_ _p ()
wp_reset_query( )?
What about using query_posts( )?
How can you alter a query? What about
How can you alter a query? What about
the main query?
16. wp‐blog‐header.php
wp blog header php
// Load the WordPress bootstrap require
// d h d b i
dirname( __FILE__ ) . '/wp‐load.php';
( __ __ ) p p p
// Decide which template files to load
// Decide which template files to load
ABSPATH . WPINC . '/template‐loader.php';
17. Let s look in the bootstrap:
Let’s look in the bootstrap:
$wp_the_query = new WP_Query();
$wp_query & $wp_the_query;
$wp query =& $wp the query;
18. Quick lesson on PHP references
Quick lesson on PHP references
// References in PHP are a means to access the same variable content
// R f i PHP t th i bl t t
by different names.
$a = 4;
$b =& $a;
// It means that $a and $b now point to the same content.
$b = 2;
$b 2
var_dump( $a ); // int(2)
$a = 6;
var_dump( $b ); // int(6)
20. wp‐blog‐header.php
wp blog header php
// Load the WordPress bootstrap require
// d h d b i
dirname( __FILE__ ) . '/wp‐load.php';
( __ __ ) p p p
// Do magic
// Do magic
wp();
// Decide which template files to load
// Decide which template files to load
ABSPATH . WPINC . '/template‐loader.php';
21. What is that wp() call?
What is that wp() call?
function wp( $query_vars = '' )
{
global $wp;
$wp >main( $query_vars
$wp‐>main( $query vars );
}
23. In the bootstrap:
In the bootstrap:
$wp = new WP()
So there’s a wp() function, and a WP class.
24. class WP
class WP
{
...
function main( )
f ()
{
$this‐>init( );
$this‐>parse_request( );
$this‐>send_headers( );
$
$this‐>query posts( );
q y_p ( );
$this‐>handle_404( );
$this‐>register_globals( );
}
. . .
}
25. class WP
class WP
{
...
function main( )
f ()
{
$this‐>init( );
$this‐>parse_request( );
$this‐>send_headers( );
$
$this‐>query posts( );
q y_p ( );
$this‐>handle_404( );
$this‐>register_globals( );
}
. . .
}
26. WP::parse_request( )
WP t( )
Parses the URL using WP_Rewrite
Sets up query variables for WP_Query
WP::query_posts( )
{
global $wp_the_query;
$ p_
$wp the_q y q y( $
query‐>query( $this‐>query vars );
q y_ );
}
27. What do we get?
What do we get?
SELECT SQL_CALC_FOUND_ROWS
wp_posts. FROM
wp posts.* FROM
wp_posts WHERE 1=1
AND wp_posts.post_type = 'post‘
AND wp_posts.post_status = publish ORDER
AND wp posts post status = 'publish' ORDER
BY wp_posts.post_date DESC LIMIT 0, 10
28. wp‐blog‐header.php
wp blog header php
// Load WordPress
// d d
dirname( __FILE__ ) . '/wp‐load.php';
( __ __ ) p p p
// Parse what to query, and query it.
// Parse what to query and query it
wp();
// Load the theme.
// Load the theme
ABSPATH . WPINC . '/template‐loader.php';
30. Then why do we do this?
Then why do we do this?
query_posts( 'author=5' );
(' h ')
g _
get_header( );
()
while( have_posts( ) ) :
while( have posts( ) ) :
the_post( );
endwhile;
get_footer( );
31. That s running 2 queries!
That’s running 2* queries!
One, the query WordPress
thought we wanted.
thought we wanted.
Two, this new one you’re
actually going to use.
actually going to use
33. 1. Get me my posts: SELECT
_ _ _
SQL_CALC_FOUND_ROWS …
FROM wp_posts LIMIT 0, 10
2. How many posts exist?
2 How many posts exist?
SELECT FOUND_ROWS()
3. Pull down all metadata for these posts.
4. Pull down all terms for these posts.
4 Pull down all terms for these posts
34. Instead of query_posts()?
Instead of query posts()?
We can use this:
// In WP::parse_request()
$this >query_vars = apply_filters( request
$this‐>query vars = apply filters( 'request',
$this‐>query_vars );
35. We can modify query variables in mid
air:
function brady_filter_out_author( $qvs )
{
if( ! Isset( $qvs[‘author’] ) )
$qvs[‘author’] = ‘‐5’;
return $qvs;
}
36. Powerful, but lacks context.
Powerful but lacks context
Problems:
1. Conditional tags don’t work yet.
2. Only works on the main query.
2 Only works on the main query
3. WP_Query is way cooler.
37. Introducing pre_get_posts
Introducing pre get posts
class WP_Query
l WP Q
{
. . .
function &get_posts()
{
{
$this‐>parse_query();
// OMG! Conditional tags are available!!
do_action_ref_array( 'pre_get_posts', array( &$this ) );
}
. . .
}
38. Lets kill off query_posts()!
Lets kill off query posts()!
function brady_alter_home( $query )
{
if ( $query‐>is_home( ) )
$query‐>set( 'author', '‐5' );
}
add_action( 'pre_get_posts',
‘brady_alter_home' );
39. Still with us?
Still with us?
Good ‘cause here’s where things get hairy.
40. ‘request’ fires for the main query only.
‘ ’ fi f h i l
‘pre_get_posts’ fires for every post query:
• get_posts()
• new WP_Query()
• That random recent posts widget.
That random recent posts widget
• Everything.
43. Main query only!
Main query only!
function brady_alter_home( $query )
f i b d l h ($ )
{
if ( $query‐>is_home( ) &&
$wp_the_query === $query )
$wp the query === $query )
$query‐>set( 'author', '‐5' );
}
add_action( pre_get_posts
add action( 'pre get posts',
‘brady_alter_home' );
44. Hmm. How does this work?
Hmm How does this work?
$wp_the_query should never be modified. It
q y
holds the main query, forever.
$wp_query k
$ keeps a live reference to
li f
$wp_the_query, unless you use query_posts().
46. class WP_Query
l WP Q
{
. . .
function &query_posts( $query )
{
// Break the reference to $wp_the_query
// Break the reference to $wp the query
unset( $wp_query );
$wp_query =& new WP_Query( $query );
...
}
...
}
50. Since WordPress 3.3!
Since WordPress 3 3!
Rather than:
$wp_the_query
$wp the query === $other_query_object
$other query object
You‘re able to call:
$other_query_object >is_main_query( )
$other query object‐>is main query( )
51. Some Lessons
Some Lessons
Every WP_Query object has methods that mimic
g g
the global conditional tags.
The global conditional tags apply to $wp_query,
Th l b l di i l l $
the main or current query.
$wp_query i l
$ is always the main query, unless you
th i l
use query_posts( ). Restore it with
wp_reset_query( ).
52. And finally
And finally
request is a nice hook. pre_get_posts is more
p
powerful and flexible. Just use it properly.
p p y
Always check if you're modifying the main query
Al h k if ' dif i h i
using $query‐>is_main_query( )
$query === $wp_the_query b f
$ $ th before 3.3
33
53. Thank you! Any questions?
Thank you! Any questions?
Further in‐depth discussion on query_posts():
• @l3rady on Twitter.
@l3rady on Twitter.
• Down the pub after presentations.