SlideShare a Scribd company logo
1 of 45
Advanced Post Queries
Best Practices for Accessing your
              Data

         @TomAuger
    WordCamp Montreal 2012
What's in the database?*
•   Posts                • Users
•   Pages                • User meta
•   Attachment records   • Site Options
•   Revisions               –   Your URL
•   Nav Menus               –   Widget settings
                            –   Theme options
•   Custom Post Types       –   User capabilities
•   Post Meta                   (maybe)
•   Categories
•   Tags
•   Custom Taxonomies
                         • * basically, everything
Post Queries in the URL
           (look Ma, no code!)

• Settings > Reading
    – "Your Latest Posts"       – / (index.php)
    – Front page                – /        (front-page.php)
    – Posts page                – /posts-page-slug/
•   Page / post slug        •   /post-or-page-slug/
•   Category Archive        •   /category/term/
•   Date                    •   /year/[month/]
•   Author Archive          •   /author/author-nicename/
•   post_type               •   /post-type-slug/
•   Search                  •   /search/term/
Post Queries (code)


•   query_posts( $args )
•   new WP_Query( $args )
•   get_posts( $args )
•   get_pages( $args )
And where does it go?



• Most often on the PHP page template
• Widgets defined in plugins
• Hooks declared in functions.php or include
query_posts() usage
                Old Skool


• Looks like URL query parameters
• Mimics the browser address, so may feel more familiar
• Kinda hard to read…


  query_posts( ‘cat=6&post_type=product&numberposts=5’ );
query_posts() usage
             The Cool Way…
• Supply arguments as array of key => value pairs
• Lots more brackets and stuff, but…
• Kinda looks cool once you get used to it




  query_posts( array(
         ‘cat’ => 6,
         ‘post_type’ => ‘product’,
         ‘posts_per_page’ => 5
  ) );
What can we query?
    http://codex.wordpress.org/Class_Reference/WP_Query


•   author                    • custom fields (meta)
•   category / tag / tax      • sticky
•   post name, ID(s)          • add pagination
•   page parent               • set sort order and
•   post type                   criteria
•   post status               • comment count
•   date & time               • even random posts!
query_posts Example
              Movie of the Day!

•   Create a new Page
•   Only the slug is important
•   Add query_posts() before any HTML
•   Current page content is clobbered
    query_posts( array (
           'orderby' => 'rand',
           'posts_per_page' => 1
    ) );


• http://localhost/projects/wordcamp/query_posts-example/
query_posts to Limit Page Count?
• Common example is to change # posts
  displayed
 query_posts( ‘posts_per_page=20’ );


• Loads the posts twice*!
• Consider Settings > Reading > Blog Pages
  Show At Most…

• * and then some…
What’s up with
                query_posts()?
• http://codex.wordpress.org/Function_Reference/query_posts

• Only useful for altering the Main Loop
• Can break pagination
   – (but can be fixed with effort)
• Still executes the original query!
   – (and then some)
• Don't use in a subquery!
Why Subqueries?


•   Add related posts
•   Add child pages
•   Add other types of content (eg: News)
•   Widgets!
Is there a template tag for that?
• http://codex.wordpress.org/Template_Tags

• Author info (the_author(),
  the_author_meta() )
• Metadata (the_meta() )
• Categories, tags
• Navigation (eg: next_post_link() )
Template Tag Example
• http://localhost/projects/wordcamp/template-tags/


• Inside "The Loop", powered by
  the_post()
  <div class="post-meta"><?php the_meta() ?></div>
  <div class="categories"><?php the_category(); ?></div>
  <div class="tags"><?php the_tags(); ?></div>

  <div class="nav">
         <?php previous_post_link() ?>
         |
         <?php next_post_link() ?>
  </div>
"Real" Sub-queries: WP_Query

• The brains behind all queries on the wp_posts
  table
• Defined in wp-includes/query.php
• All queries go through WP_Query
• "Get your own" with


 new WP_Query( $args );
WP_Query usage

<?php
  $my_sub_query = new WP_Query( array (
     'post_type' => 'quotes',
     'order' => 'rand',
     'posts_per_page' => 1
  ) );

    while ( $my_sub_query->have_posts() ){
      $my_sub_query->the_post();
      get_template_part( 'my_loop_template' );
    }

    wp_reset_postdata();
}
WP_Query Examples



• http://localhost/projects/wordcamp/date-query/

• http://localhost/projects/wordcamp/sort-by-post-meta/
Meta Queries

• Make your postmeta work (harder) for you!
• At first blush, a little funky

 <?php
  $meta_query = new WP_Query( array(
    'meta_query' => array(
       array(
         // meta query here
       )
     )
  ) );
More Meta Query Examples


• http://localhost/projects/wordcamp/meta-queries/

• http://localhost/projects/wordcamp/multiple-meta-queries-and/

• http://localhost/projects/wordcamp/multiple-meta-queries-or/
Taxonomy Queries

• As with meta_query, so with tax_query
  <?php
   $tax_query = new WP_Query( array(
     'taz_query' => array(
        array(

          )
      )
   ) );


• http://localhost/projects/wordcamp/tax-query-simple/
wp_reset_postdata()?

• If you use the_post(), clean up after
  yourself
• the_post() sets lots of $_GLOBALs
• Could break templates, footers, widgets
  etc…

• http://localhost/projects/wordcamp/wp_reset_postdata/
Global Query Manipulation
        (The Good Stuff!)



• Modify the Main Query
• Modify the way all queries work site-wide
• Modify queries for a specific page
Case Study: amovo.ca

•   Furniture retailer with multiple brands
•   Custom post type: "amovo-products"
•   Custom post type: "manufacturer"
•   Custom taxonomies: "product-categories"
    and "product-applications"

• http://localhost/projects/amovo/
amovo.ca: Custom Global Sort

• Wanted to "feature" certain products
• Wherever they might be listed
• eg:
  http://localhost/projects/amovo/product-
  categories/executive-conference-seating/

• Best match: custom field "_zg_priority"
amovo.ca: Category Archive



• Uses the standard custom tax query
• Need to ORDER BY wp_postmeta for ALL
  queries
Filter: 'posts_clauses'
• Allows us to manipulate SQL before it is
  sent to the database
• For ALL queries (not just main query)
 if (! is_admin()){
  add_filter(
   'posts_clauses', 'sort_by_priority', 10, 2
  );
 }

 function sort_by_product_priority( $clauses, $query ){
  global $wpdb;

  //… more to come
 }
Keeping it lean
• If query has nothing to do with products,
  don’t add the overhead
• Only affect product-related tax queries

 function sort_by_product_priority( $clauses, $query ){
   global $wpdb;

     if ( in_array( $query->query_vars['taxonomy'],
       array( 'product-category', 'product-application'))
     ){
        // …
     }
 }
Make sure it's the right query!

• These hooks affect EVERY query
• Add conditionals to reduce overhead
  – is_admin() during hook registration
  – is_main_query()
  – $query->query_vars (eg: "taxonomy",
    "post_type", "orderby")

if (
 'page-hijacking' == $query_obj->query_vars['pagename']
){ … }
JOIN your tables
• LEFT JOIN doesn't exclude missing meta
• Alias your JOINs to avoid collision
 function sort_by_product_priority( $clauses, $query ){
  global $wpdb;
     …
   // JOIN on postmeta to get priority
   $clauses['join'] .= "
      LEFT JOIN {$wpdb->postmeta} PM
        ON ({$wpdb->posts}.ID = PM.post_id
        AND PM.meta_key = '_zg_priority')
   ";
     …
  return $clauses;
 }
Modify ORDER BY
• PREPEND your sort criteria
• Don't forget to add comma if you need
  one!
 function sort_by_product_priority( $clauses, $query ){
      …
   $join .= "LEFT JOIN {$wpdb->postmeta} PM...";
      …
   $orderby = &$clauses['orderby']; // by reference!
   if (! empty( $orderby ) ) $orderby = ', ' . $orderby;

   $orderby = "
     IFNULL(P.meta_value, 5) ASC,
     {$wpdb->posts}.post_title ASC" . $orderby;

  return $clauses;
 }
Manipulating the Main Query
           (main filters)
• Manipulating the URI query variables
   – before the query is parsed
   – add_filter( 'request' )

• Manipulating the query object
   – before the SQL is generated
   – add_filter( 'parse_query' ) / 'pre_get_posts'

• Manipulating the SQL
   – before the SQL is run
   – add_filter( 'posts_clauses' ) 2 arguments

• Manipulating the results
   – before stickies and status
   – add_filter( 'posts_results' )
Case Study: Date Range

• WP_Query doesn't (currently) support date
  ranges
• Let's create 'before_date' and 'after_date'

 $date_query = new WP_Query( array(
        'after_date' => '2012-08-10',
        'before_date' => '2012-08-14'
 ) );
Exploiting the Query Object
add_filter( 'posts_where', 'date_ranges', 10, 2 );
function 'date_ranges( $where_clause, $query_obj ){
  global $wpdb;

    if ( $before_date = $query_obj->query['before_date'] ){
      $where_clause .= "
         AND {$wpdb->posts}.post_date < '{$before_date}'
      ";
    }

    if ( $after_date = $query_obj->query['after_date'] ) ){
      $where_clause .= "
         AND DATE({$wpdb->posts}.post_date) > '{$after_date}'
      ";
    }

    return $where_clause;
}
Improvements & Security
• Make sure we're actually getting a Date
• Cast the time() object to a mySQL DATE
  string
• Cast mySQL TIMESTAMP to DATE portion
  only
if ( $after_date = strtotime(
  $query_obj->query['after_date']
) ){
  $after_date = date( 'Y-m-d', $after_date );
  $where_clause .= "
     AND DATE({$wpdb->posts}.post_date) > '{$after_date}'
  ";
}
Conclusions

•   TMTOWTDI
•   Go as high-level as you can
•   Use query_posts() sparingly
•   wp_reset_postdata()!
•   Modify the Main Query with filters / actions

• Make WordPress sing!
Working with $wpdb
• global $wpdb
• Always use $wpdb->table_name
• Always use $wpdb->prepare with tainted
  data
• Use the right get_* method for the job
  – get_var()
  – get_row()
  – get_col()
  – get_results()
Please avoid inserting /
              updating…
•   Posts
•   Users
•   Meta data (usermeta or postmeta)
•   Options
•   Taxonomies and Terms (especially!)

• Basically, if there's a default wp_* table, there
  are core methods for inserting, updating and
  deleting
When to extend schema

• Primary object has tons of metadata
• Highly relational data
• Plugin tables (be sure to offer DROP
  option!)
  – register_deactivation_hook();
• Can't map to post, meta and taxonomy
Final observations… 'n stuff

• There are actually very few reasons to
  generate your own SQL
• There's probably a core function for that
• Due diligence and research
  – Google search "wordpress [keywords]"!
  – Get a good IDE (Jer's talk about NetBeans!)
Thank You!
updates: @TomAuger
 www.tomauger.com
Extra Slides




• Ah, time is thy enemy…
More Template Tags


<div class="author-info">
  <p><strong>Posted by: </strong>
  <?php the_author(); ?>
  (<?php the_author_meta( 'first_name' ) ?>
  <?php the_author_meta( 'last_name' ) ?>)

  <?php echo apply_filters(
    'the_content', get_the_author_meta( 'description' )
  ); ?>
</div>
Anatomy of a Query
• $wp->main() is called after everything is loaded
    – Parses the browser query string and applies rewrite rules
    – Rebuilds the query string for main query
    – Runs get_posts() on the main $wp_the_query (WP_Query instance)
• WP_Query then
    – Reparses the query string
         • Request for single page short-circuits and uses cache if possible
    –   Parses the search
    –   Parses any taxonomy queries
    –   Generates the SQL clauses
    –   Gives cacheing plugins a chance to get involved
    –   Generates the SQL request and executes it!
    –   Processes comments
    –   Processes stickies
    –   Updates caches
Plugin Authors


• Consider require_once( wp-admin/install-
  helper.php ) within your
  register_activation_hook()
  – helper functions like maybe_create_table(),
    maybe_add_column()

More Related Content

Recently uploaded

Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxHarnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
FIDO Alliance
 

Recently uploaded (20)

ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...
ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...
ASRock Industrial FDO Solutions in Action for Industrial Edge AI _ Kenny at A...
 
Event-Driven Architecture Masterclass: Integrating Distributed Data Stores Ac...
Event-Driven Architecture Masterclass: Integrating Distributed Data Stores Ac...Event-Driven Architecture Masterclass: Integrating Distributed Data Stores Ac...
Event-Driven Architecture Masterclass: Integrating Distributed Data Stores Ac...
 
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptxHarnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
Harnessing Passkeys in the Battle Against AI-Powered Cyber Threats.pptx
 
2024 May Patch Tuesday
2024 May Patch Tuesday2024 May Patch Tuesday
2024 May Patch Tuesday
 
Your enemies use GenAI too - staying ahead of fraud with Neo4j
Your enemies use GenAI too - staying ahead of fraud with Neo4jYour enemies use GenAI too - staying ahead of fraud with Neo4j
Your enemies use GenAI too - staying ahead of fraud with Neo4j
 
FDO for Camera, Sensor and Networking Device – Commercial Solutions from VinC...
FDO for Camera, Sensor and Networking Device – Commercial Solutions from VinC...FDO for Camera, Sensor and Networking Device – Commercial Solutions from VinC...
FDO for Camera, Sensor and Networking Device – Commercial Solutions from VinC...
 
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdfLinux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
Linux Foundation Edge _ Overview of FDO Software Components _ Randy at Intel.pdf
 
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdfHow Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
How Red Hat Uses FDO in Device Lifecycle _ Costin and Vitaliy at Red Hat.pdf
 
Where to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdfWhere to Learn More About FDO _ Richard at FIDO Alliance.pdf
Where to Learn More About FDO _ Richard at FIDO Alliance.pdf
 
Overview of Hyperledger Foundation
Overview of Hyperledger FoundationOverview of Hyperledger Foundation
Overview of Hyperledger Foundation
 
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdfIntroduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
Introduction to FDO and How It works Applications _ Richard at FIDO Alliance.pdf
 
Collecting & Temporal Analysis of Behavioral Web Data - Tales From The Inside
Collecting & Temporal Analysis of Behavioral Web Data - Tales From The InsideCollecting & Temporal Analysis of Behavioral Web Data - Tales From The Inside
Collecting & Temporal Analysis of Behavioral Web Data - Tales From The Inside
 
Easier, Faster, and More Powerful – Notes Document Properties Reimagined
Easier, Faster, and More Powerful – Notes Document Properties ReimaginedEasier, Faster, and More Powerful – Notes Document Properties Reimagined
Easier, Faster, and More Powerful – Notes Document Properties Reimagined
 
Intro to Passkeys and the State of Passwordless.pptx
Intro to Passkeys and the State of Passwordless.pptxIntro to Passkeys and the State of Passwordless.pptx
Intro to Passkeys and the State of Passwordless.pptx
 
TopCryptoSupers 12thReport OrionX May2024
TopCryptoSupers 12thReport OrionX May2024TopCryptoSupers 12thReport OrionX May2024
TopCryptoSupers 12thReport OrionX May2024
 
WebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM PerformanceWebAssembly is Key to Better LLM Performance
WebAssembly is Key to Better LLM Performance
 
Microsoft CSP Briefing Pre-Engagement - Questionnaire
Microsoft CSP Briefing Pre-Engagement - QuestionnaireMicrosoft CSP Briefing Pre-Engagement - Questionnaire
Microsoft CSP Briefing Pre-Engagement - Questionnaire
 
AI mind or machine power point presentation
AI mind or machine power point presentationAI mind or machine power point presentation
AI mind or machine power point presentation
 
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
Human Expert Website Manual WCAG 2.0 2.1 2.2 Audit - Digital Accessibility Au...
 
Extensible Python: Robustness through Addition - PyCon 2024
Extensible Python: Robustness through Addition - PyCon 2024Extensible Python: Robustness through Addition - PyCon 2024
Extensible Python: Robustness through Addition - PyCon 2024
 

Featured

How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
ThinkNow
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Kurio // The Social Media Age(ncy)
 

Featured (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

Advanced Post Queries

  • 1. Advanced Post Queries Best Practices for Accessing your Data @TomAuger WordCamp Montreal 2012
  • 2. What's in the database?* • Posts • Users • Pages • User meta • Attachment records • Site Options • Revisions – Your URL • Nav Menus – Widget settings – Theme options • Custom Post Types – User capabilities • Post Meta (maybe) • Categories • Tags • Custom Taxonomies • * basically, everything
  • 3. Post Queries in the URL (look Ma, no code!) • Settings > Reading – "Your Latest Posts" – / (index.php) – Front page – / (front-page.php) – Posts page – /posts-page-slug/ • Page / post slug • /post-or-page-slug/ • Category Archive • /category/term/ • Date • /year/[month/] • Author Archive • /author/author-nicename/ • post_type • /post-type-slug/ • Search • /search/term/
  • 4. Post Queries (code) • query_posts( $args ) • new WP_Query( $args ) • get_posts( $args ) • get_pages( $args )
  • 5. And where does it go? • Most often on the PHP page template • Widgets defined in plugins • Hooks declared in functions.php or include
  • 6. query_posts() usage Old Skool • Looks like URL query parameters • Mimics the browser address, so may feel more familiar • Kinda hard to read… query_posts( ‘cat=6&post_type=product&numberposts=5’ );
  • 7. query_posts() usage The Cool Way… • Supply arguments as array of key => value pairs • Lots more brackets and stuff, but… • Kinda looks cool once you get used to it query_posts( array( ‘cat’ => 6, ‘post_type’ => ‘product’, ‘posts_per_page’ => 5 ) );
  • 8. What can we query? http://codex.wordpress.org/Class_Reference/WP_Query • author • custom fields (meta) • category / tag / tax • sticky • post name, ID(s) • add pagination • page parent • set sort order and • post type criteria • post status • comment count • date & time • even random posts!
  • 9. query_posts Example Movie of the Day! • Create a new Page • Only the slug is important • Add query_posts() before any HTML • Current page content is clobbered query_posts( array ( 'orderby' => 'rand', 'posts_per_page' => 1 ) ); • http://localhost/projects/wordcamp/query_posts-example/
  • 10. query_posts to Limit Page Count? • Common example is to change # posts displayed query_posts( ‘posts_per_page=20’ ); • Loads the posts twice*! • Consider Settings > Reading > Blog Pages Show At Most… • * and then some…
  • 11. What’s up with query_posts()? • http://codex.wordpress.org/Function_Reference/query_posts • Only useful for altering the Main Loop • Can break pagination – (but can be fixed with effort) • Still executes the original query! – (and then some) • Don't use in a subquery!
  • 12. Why Subqueries? • Add related posts • Add child pages • Add other types of content (eg: News) • Widgets!
  • 13. Is there a template tag for that? • http://codex.wordpress.org/Template_Tags • Author info (the_author(), the_author_meta() ) • Metadata (the_meta() ) • Categories, tags • Navigation (eg: next_post_link() )
  • 14. Template Tag Example • http://localhost/projects/wordcamp/template-tags/ • Inside "The Loop", powered by the_post() <div class="post-meta"><?php the_meta() ?></div> <div class="categories"><?php the_category(); ?></div> <div class="tags"><?php the_tags(); ?></div> <div class="nav"> <?php previous_post_link() ?> | <?php next_post_link() ?> </div>
  • 15. "Real" Sub-queries: WP_Query • The brains behind all queries on the wp_posts table • Defined in wp-includes/query.php • All queries go through WP_Query • "Get your own" with new WP_Query( $args );
  • 16. WP_Query usage <?php $my_sub_query = new WP_Query( array ( 'post_type' => 'quotes', 'order' => 'rand', 'posts_per_page' => 1 ) ); while ( $my_sub_query->have_posts() ){ $my_sub_query->the_post(); get_template_part( 'my_loop_template' ); } wp_reset_postdata(); }
  • 17. WP_Query Examples • http://localhost/projects/wordcamp/date-query/ • http://localhost/projects/wordcamp/sort-by-post-meta/
  • 18. Meta Queries • Make your postmeta work (harder) for you! • At first blush, a little funky <?php $meta_query = new WP_Query( array( 'meta_query' => array( array( // meta query here ) ) ) );
  • 19. More Meta Query Examples • http://localhost/projects/wordcamp/meta-queries/ • http://localhost/projects/wordcamp/multiple-meta-queries-and/ • http://localhost/projects/wordcamp/multiple-meta-queries-or/
  • 20. Taxonomy Queries • As with meta_query, so with tax_query <?php $tax_query = new WP_Query( array( 'taz_query' => array( array( ) ) ) ); • http://localhost/projects/wordcamp/tax-query-simple/
  • 21. wp_reset_postdata()? • If you use the_post(), clean up after yourself • the_post() sets lots of $_GLOBALs • Could break templates, footers, widgets etc… • http://localhost/projects/wordcamp/wp_reset_postdata/
  • 22. Global Query Manipulation (The Good Stuff!) • Modify the Main Query • Modify the way all queries work site-wide • Modify queries for a specific page
  • 23. Case Study: amovo.ca • Furniture retailer with multiple brands • Custom post type: "amovo-products" • Custom post type: "manufacturer" • Custom taxonomies: "product-categories" and "product-applications" • http://localhost/projects/amovo/
  • 24. amovo.ca: Custom Global Sort • Wanted to "feature" certain products • Wherever they might be listed • eg: http://localhost/projects/amovo/product- categories/executive-conference-seating/ • Best match: custom field "_zg_priority"
  • 25. amovo.ca: Category Archive • Uses the standard custom tax query • Need to ORDER BY wp_postmeta for ALL queries
  • 26. Filter: 'posts_clauses' • Allows us to manipulate SQL before it is sent to the database • For ALL queries (not just main query) if (! is_admin()){ add_filter( 'posts_clauses', 'sort_by_priority', 10, 2 ); } function sort_by_product_priority( $clauses, $query ){ global $wpdb; //… more to come }
  • 27. Keeping it lean • If query has nothing to do with products, don’t add the overhead • Only affect product-related tax queries function sort_by_product_priority( $clauses, $query ){ global $wpdb; if ( in_array( $query->query_vars['taxonomy'], array( 'product-category', 'product-application')) ){ // … } }
  • 28. Make sure it's the right query! • These hooks affect EVERY query • Add conditionals to reduce overhead – is_admin() during hook registration – is_main_query() – $query->query_vars (eg: "taxonomy", "post_type", "orderby") if ( 'page-hijacking' == $query_obj->query_vars['pagename'] ){ … }
  • 29. JOIN your tables • LEFT JOIN doesn't exclude missing meta • Alias your JOINs to avoid collision function sort_by_product_priority( $clauses, $query ){ global $wpdb; … // JOIN on postmeta to get priority $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} PM ON ({$wpdb->posts}.ID = PM.post_id AND PM.meta_key = '_zg_priority') "; … return $clauses; }
  • 30. Modify ORDER BY • PREPEND your sort criteria • Don't forget to add comma if you need one! function sort_by_product_priority( $clauses, $query ){ … $join .= "LEFT JOIN {$wpdb->postmeta} PM..."; … $orderby = &$clauses['orderby']; // by reference! if (! empty( $orderby ) ) $orderby = ', ' . $orderby; $orderby = " IFNULL(P.meta_value, 5) ASC, {$wpdb->posts}.post_title ASC" . $orderby; return $clauses; }
  • 31. Manipulating the Main Query (main filters) • Manipulating the URI query variables – before the query is parsed – add_filter( 'request' ) • Manipulating the query object – before the SQL is generated – add_filter( 'parse_query' ) / 'pre_get_posts' • Manipulating the SQL – before the SQL is run – add_filter( 'posts_clauses' ) 2 arguments • Manipulating the results – before stickies and status – add_filter( 'posts_results' )
  • 32. Case Study: Date Range • WP_Query doesn't (currently) support date ranges • Let's create 'before_date' and 'after_date' $date_query = new WP_Query( array( 'after_date' => '2012-08-10', 'before_date' => '2012-08-14' ) );
  • 33. Exploiting the Query Object add_filter( 'posts_where', 'date_ranges', 10, 2 ); function 'date_ranges( $where_clause, $query_obj ){ global $wpdb; if ( $before_date = $query_obj->query['before_date'] ){ $where_clause .= " AND {$wpdb->posts}.post_date < '{$before_date}' "; } if ( $after_date = $query_obj->query['after_date'] ) ){ $where_clause .= " AND DATE({$wpdb->posts}.post_date) > '{$after_date}' "; } return $where_clause; }
  • 34. Improvements & Security • Make sure we're actually getting a Date • Cast the time() object to a mySQL DATE string • Cast mySQL TIMESTAMP to DATE portion only if ( $after_date = strtotime( $query_obj->query['after_date'] ) ){ $after_date = date( 'Y-m-d', $after_date ); $where_clause .= " AND DATE({$wpdb->posts}.post_date) > '{$after_date}' "; }
  • 35. Conclusions • TMTOWTDI • Go as high-level as you can • Use query_posts() sparingly • wp_reset_postdata()! • Modify the Main Query with filters / actions • Make WordPress sing!
  • 36. Working with $wpdb • global $wpdb • Always use $wpdb->table_name • Always use $wpdb->prepare with tainted data • Use the right get_* method for the job – get_var() – get_row() – get_col() – get_results()
  • 37. Please avoid inserting / updating… • Posts • Users • Meta data (usermeta or postmeta) • Options • Taxonomies and Terms (especially!) • Basically, if there's a default wp_* table, there are core methods for inserting, updating and deleting
  • 38. When to extend schema • Primary object has tons of metadata • Highly relational data • Plugin tables (be sure to offer DROP option!) – register_deactivation_hook(); • Can't map to post, meta and taxonomy
  • 39. Final observations… 'n stuff • There are actually very few reasons to generate your own SQL • There's probably a core function for that • Due diligence and research – Google search "wordpress [keywords]"! – Get a good IDE (Jer's talk about NetBeans!)
  • 40. Thank You! updates: @TomAuger www.tomauger.com
  • 41.
  • 42. Extra Slides • Ah, time is thy enemy…
  • 43. More Template Tags <div class="author-info"> <p><strong>Posted by: </strong> <?php the_author(); ?> (<?php the_author_meta( 'first_name' ) ?> <?php the_author_meta( 'last_name' ) ?>) <?php echo apply_filters( 'the_content', get_the_author_meta( 'description' ) ); ?> </div>
  • 44. Anatomy of a Query • $wp->main() is called after everything is loaded – Parses the browser query string and applies rewrite rules – Rebuilds the query string for main query – Runs get_posts() on the main $wp_the_query (WP_Query instance) • WP_Query then – Reparses the query string • Request for single page short-circuits and uses cache if possible – Parses the search – Parses any taxonomy queries – Generates the SQL clauses – Gives cacheing plugins a chance to get involved – Generates the SQL request and executes it! – Processes comments – Processes stickies – Updates caches
  • 45. Plugin Authors • Consider require_once( wp-admin/install- helper.php ) within your register_activation_hook() – helper functions like maybe_create_table(), maybe_add_column()