You Don't Know Query (WordCamp Netherlands 2012)

58,243 views
59,366 views

Published on

An update to a talk I gave at WordCamp Portland 2011, "You Don't Know Query" is an advanced development talk from March 25, 2012, in Utrecht, Netherlands.

Published in: Technology
10 Comments
69 Likes
Statistics
Notes
No Downloads
Views
Total views
58,243
On SlideShare
0
From Embeds
0
Number of Embeds
14,049
Actions
Shares
0
Downloads
278
Comments
10
Likes
69
Embeds 0
No embeds

No notes for slide

You Don't Know Query (WordCamp Netherlands 2012)

  1. 1. WordCamp Netherlands 2012
  2. 2. Andrew NacinCore Developer of WordPressand Tech Ninja at Audrey Capital@nacin on Twitternacin@wordpress.org
  3. 3. You Don’t Know Query
  4. 4. What do you know?
  5. 5. Conditional Tagsis_author( ), is_home( ), etc.
  6. 6. query_posts( )
  7. 7. Ways to queryquery_posts( )new WP_Query( )get_posts( )
  8. 8. The Loopwhile ( have_posts( ) ) : the_post( );endwhile;
  9. 9. A secondary loop$query = new WP_Query( … );while ( $query->have_posts( ) ) : $query->the_post( );endwhile;
  10. 10. An array of posts$result = get_posts( … );foreach ( $result as $post_obj ) {}
  11. 11. What don’t you know?
  12. 12. Every query object has itsown methodsis_author( ) is the same as calling$wp_query->is_author( )
  13. 13. function is_author( ) { global $wp_query; return $wp_query->is_author( );}
  14. 14. With the regular loopwhile ( have_posts( ) ) : the_post( ); if ( is_author( ) ) echo "An author query.";endwhile;
  15. 15. With the regular loopwhile ( have_posts( ) ) : the_post( ); if ( $wp_query->is_author( ) ) echo "An author query.";endwhile;
  16. 16. A secondary loop$query = new WP_Query( … );while ( $query->have_posts( ) ) : $query->the_post( ); if ( $query->is_author( ) ) echo "An author query.";endwhile;
  17. 17. A secondary loop$query = new WP_Query( … );while ( $query->have_posts( ) ) : $query->the_post( ); if ( $query->is_author( ) ) echo "An author query.";endwhile;
  18. 18. A secondary loop$query = new WP_Query( … );while ( $query->have_posts( ) ) : $query->the_post( ); if ( $query->is_author( ) ) echo "An author query.";endwhile;
  19. 19. If you do:$my_query = new WP_Query( $query );You can do:while ( $my_query->have_posts( ) ) : $my_query->the_post( );endwhile;wp_reset_postdata( );
  20. 20. Why do we call functions likewp_reset_postdata( ) andwp_reset_query( )?What about using query_posts( )?How can you alter a query? Howcan you alter the main query?
  21. 21. What is the main query,and why should I care?
  22. 22. wp-blog-header.php// Load the WordPress bootstraprequire ./wp-load.php;// Do magicwp( );// Decide which template files to loadrequire WPINC . /template-loader.php;
  23. 23. Lets look in the bootstrap:$wp_the_query = new WP_Query();$wp_query =& $wp_the_query;
  24. 24. Quick lesson on PHP references$a = 4;$b =& $a;$b = 2;var_dump( $a ); // int(2)$a = 6;var_dump( $b ); // int(6)
  25. 25. So:So the real main query is in$wp_the_query.And a live copy of it is stored in$wp_query.
  26. 26. wp-blog-header.php// Load the WordPress bootstraprequire ./wp-load.php;// Do magicwp( );// Decide which template files to loadrequire WPINC . /template-loader.php;
  27. 27. wp-blog-header.php// Load the WordPress bootstraprequire ./wp-load.php;// Do magicwp( );// Decide which template files to loadrequire WPINC . /template-loader.php;
  28. 28. What is that wp( ) call?function wp( $query_vars = ) { global $wp; $wp->main( $query_vars );}
  29. 29. Holy $!@?, what justhappened?
  30. 30. In the bootstrap:$wp = new WP( );So theres a wp( ) function,and a WP class.
  31. 31. class WP { . . . function main( ) { $this->init( ); $this->parse_request( ); $this->send_headers( ); $this->query_posts( ); $this->handle_404( ); $this->register_globals( ); . . .
  32. 32. class WP { . . . function main( ) { $this->init( ); $this->parse_request( ); $this->send_headers( ); $this->query_posts( ); $this->handle_404( ); $this->register_globals( ); . . .
  33. 33. WP::parse_request( )— Parses the URL using WP_Rewrite— Sets up query variables for WP_QueryWP::query_posts( ) { global $wp_the_query; $wp_the_query->query( $this->query_vars );}
  34. 34. Boom.SELECT SQL_CALC_FOUND_ROWS wp_posts.*FROM wp_postsWHERE 1=1 AND wp_posts.post_type = post AND wp_posts.post_status = publishORDER BY wp_posts.post_date DESCLIMIT 0, 10
  35. 35. wp-blog-header.php// Load WordPress.require ./wp-load.php;// Parse what to query. Then query it.wp( );// Load the theme.require WPINC . /template-loader.php;
  36. 36. Before we get to the theme,we have your posts.Got it?
  37. 37. Then why do we do this?query_posts( author=-5 );get_header( );while( have_posts( ) ) : the_post( );endwhile;get_footer( );
  38. 38. Thats running 2* queries!One, the query WordPressthought we wanted.Two, this new one youreactually going to use.
  39. 39. * Actually, WP_Querydoesnt run just one query.It usually runs four.
  40. 40. 1. Get me my posts: SELECT SQL_CALC_FOUND_ROWS … FROM wp_posts LIMIT 0, 102. How many posts exist? SELECT FOUND_ROWS( )3. Get all metadata for these posts.4. Get all terms for these posts.
  41. 41. (You can turn these off selectively…)$my_query = new WP_Query( array( no_found_rows => true, update_post_meta_cache => false, update_post_term_cache => false,) );
  42. 42. </aside>
  43. 43. PROTIP‘Measure twice, cut once’is bad for performance.
  44. 44. Other problems withquery_posts( )
  45. 45. Pagination breaks.WordPress calculatedpaging using the query itdid, not the query you did.
  46. 46. query_posts( array( author => -5, posts_per_page => 25,) );This will not work well.
  47. 47. You easily mess up globals.This can break widgets andmore.
  48. 48. query_posts( ) is bad.Do we agree?
  49. 49. Introducing pre_get_postsclass WP_Query { . . . function &get_posts() { $this->parse_query(); // Huzzah! do_action_ref_array( pre_get_posts, array( &$this ) ); . . .
  50. 50. A truly awesome hook.function nacin_alter_home( $query ) { if ( $query->is_home( ) ) $query->set( author, -5 );}add_action( pre_get_posts, nacin_alter_home );
  51. 51. Still with us?Good, ‘cause here’s wherethings get complicated.
  52. 52. pre_get_posts fires for every postquery: — get_posts( ) — new WP_Query( ) — That random recent posts widget your clientinstalled without you knowing. — Everything.
  53. 53. What if I just want it on themain query?
  54. 54. $wp_the_query makes atriumphant return.
  55. 55. Main query only!function nacin_alter_home( $query ) { global $wp_the_query; if ( $wp_the_query === $query && $query->is_home() ) $query->set( author, -5 );}add_action( pre_get_posts, nacin_alter_home );
  56. 56. Hmm. How does this work?$wp_the_query should never be modified. Itholds the main query, forever.$wp_query keeps a live reference to$wp_the_query, unless you usequery_posts( ).
  57. 57. query_posts( author=-5 );while ( have_posts( ) ) : the_post( );endwhile;wp_reset_query( );
  58. 58. query_posts( author=-5 );while ( have_posts( ) ) : the_post( );endwhile;wp_reset_query( );
  59. 59. function query_posts( $query ) { // Break the reference to $wp_the_query unset( $wp_query ); $wp_query =& new WP_Query( $query ); return $wp_query;}
  60. 60. query_posts( author=-5 );while ( have_posts( ) ) : the_post( );endwhile;wp_reset_query( );
  61. 61. function wp_reset_query( ) { // Restore reference to $wp_the_query unset( $wp_query ); $wp_query =& $wp_the_query; // Reset the globals, too. wp_reset_postdata( );}
  62. 62. Calling the_post( )? wp_reset_query( ) will reset $wp_query and the globals.Calling $my_query->the_post( )? wp_reset_postdata( ) will reset the globals.
  63. 63. New in WordPress 3.3!Rather than: $wp_the_query === $other_query_object  You can call: $other_query_object->is_main_query( )  is_main_query( ), the function, will act on$wp_query, like any other conditional tag.
  64. 64. What about pagetemplates?
  65. 65. /* Template: My Template */query_posts( $query_string . &author=-5&posts_per_page=25 );get_header( );while ( have_posts( ) ) : the_post( );endwhile;
  66. 66. function nacin_my_template( $query ) { if ( ! $query->is_main_query( ) ) return; if ( ! is_page_template( my-template.php ) ) return; $query->set( author, -5 ); $query->set( posts_per_page, 25 );}add_action( pre_get_posts, nacin_my_template );
  67. 67. Some LessonsEvery WP_Query object has methods thatmimic the global conditional tags.The global conditional tags apply to$wp_query, the main or current query.$wp_query is always the main query, unlessyou use query_posts( ). Restore it withwp_reset_query( ).
  68. 68. And Finallypre_get_posts is a powerful and flexible hook.Just use it properly.Always check if youre modifying the mainquery using $query->is_main_query( )
  69. 69. Thanks! Questions?@nacin

×