31

I have the following query which I call in my taxonomy.php template via query_brands_geo('dealers', 'publish', '1', $taxtype, $geo, $brands);

This function works perfectly. However after reading codex for query posts it mentioned pre_get_posts as a preferred way to alter the default query. Would the pre_get_posts be more efficient then my wp_query function below?

If so how would I construct the pre_get_posts and pass my variable and query below?

function my_custom_query($posttype, $poststatus, $paidvalue, $taxtype, $geo, $brands) {
   global $wp_query; 
   $wp_query = new WP_Query();
   $args = array( 
      'post_type' => $posttype, 
      'post_status' => array($poststatus), 
      'orderby' => 'rand', 
      'posts_per_page' => 30, 
      'meta_query' => array( 
         array( 
            'key' => 'wpcf-paid', 
            'value' => array($paidvalue), 
            'compare' => 'IN', 
            ) 
      ), 
      'tax_query' => array( 
         'relation' => 'AND', 
         array( 
            'taxonomy' => $taxtype, 
            'field' => 'slug', 
            'terms' => $geo 
         ), 
         array( 
            'taxonomy' => 'brands', 
            'field' => 'slug', 
            'terms' => $brands 
         ) 
      ) 
   ); 

   return $wp_query->query($args); 
} 

3 Answers 3

14

pre_get_posts will run the same query, so both will take same time. But, If you utilize pre_get_posts action you will save one or more SQL queries. Right now, WordPress is running default query and then you run your query with this function which replace the results of the default query (resulting, default query is of no use). Below is how you can move your $args to

function custom_pre_get_posts($query, $posttype='dealers', $poststatus='publish', $paidvalue='1', $taxtype='any_default_value', $geo='any_default_value', $brands='any_default_value') {

    // filter your request here.
    if($query->is_category) {

        $args = array(
            'post_type' => $posttype,
            'post_status' => array($poststatus),
            'orderby' => 'rand',
            'posts_per_page' => 30,
            'meta_query' => array(
                array(
                    'key' => 'wpcf-paid',
                    'value' => array($paidvalue),
                    'compare' => 'IN',
                )
            ),
            'tax_query' => array(
                'relation' => 'AND',
                array(
                    'taxonomy' => $taxtype,
                    'field' => 'slug',
                    'terms' => $geo
                ),
                array(
                    'taxonomy' => 'brands',
                    'field' => 'slug',
                    'terms' => $brands
                )
            )
        );
        $query->query_vars = $args;
    }
}
add_action('pre_get_posts', 'custom_pre_get_posts');
6
  • Thank you very much for the reply. It is very helpful. One quick question. I placed the function in my theme function.php file. I run this function custom_pre_get_posts($query) from my taxonomy.php. In the taxonomy.php I set up the variables, $posttype, $post_status, $geo, $brands, $taxtype and run two loops changing these variable. Is there a way to pass variable into the above function from taxonomy.php? When I try custom_pre_get_posts($query,'dealers', 'publish', '1', $taxtype, $geo, $brands); I get Missing argument 2 thru 7 for custom_pre_get_posts(). I assume due to add_action??? Dec 19, 2012 at 17:55
  • 1
    I am assuming you changed the custom_pre_get_posts to accept the remaining arguments. Yes, you are getting error because of add_action. add_action calls this function with single argument (i.e. $query), you should give default values to other arguments to avoid missing argument errors. like ($posttype=null, $poststatus=null...) so it can properly called by add_action.
    – M-R
    Dec 19, 2012 at 18:07
  • M-R Thank you for the reply. I read up on add action and I see I should be assigning a priority and arguments number. So I changed my add action to <code>add_action('pre_get_posts', 'custom_pre_get_posts', 10,7);</code> Then in my taxonomy.php page I <code> do_action('pre_get_post', $query, 'dealers', 'publish', '1', $taxtype, $geo, $brands);</code>. But I am still getting the same error. I was not sure where to put default values. I tried google but could not find a reference. Can you give me a little more information on where how to handle this? Dec 19, 2012 at 19:02
  • Thank you for the reply and example. But I think I might be trying to use pre_get_posts when I should just be doing a new wordpress query. I was trying to save a query, but in my case it might not be possible. The reason being is all the arguments you set in the parameter, I wanted to pass to the function from my taxonomy.php file. So $paidvalue=”1” might be 1 or 0 depending on which condition I am executing in taxonomy.php. It seems pre_get_posts above fires right when the page loads even if I don't call the function in my taxonomy.php file. Am I seeing this correct? Dec 21, 2012 at 0:56
  • 3
    This answer is non sense as it's currently written. You will effectivly overwrite any value inside the $wp_query object and things will completely fail. Aside from that it's simply not true that pre_get_posts will run an additional query...
    – kaiser
    Mar 10, 2013 at 15:04
10

Late answer as the most upvoted answer will break your query and simply isn't true in some major points.

The main WP_Query and it's filters

First, WordPress internally uses query_posts() (a thin wrapper around WP_Query that shouldn't be used in themes or plugins) to do a WP_Query. This WP_Query is acting as the main loop/query. This query will run through a lot of filters and actions until the actual SQL query string is built. One of those is pre_get_posts. Others are posts_clauses, posts_where, etc. that also allow you to intercept the query string building process.

An in depth look at what happens inside core

WordPress runs the wp() function (in wp-includes/functions.php), which calls $wp->main() ($wp is an object of class WP, which is defined in wp-includes/class-wp.php). This tells WordPress to:

  1. Parse the URL into a query specification using WP->parse_request() -- more on that below.
  2. Set all the is_ variables that are used by Conditional Tags using $wp_query->parse_query() ($wp_query is an object of class WP_Query, which is defined in wp-includes/query.php). Note that in spite of this function's name, in this case WP_Query->parse_query doesn't actually do any parsing for us, since that is done before-hand by WP->parse_request().
  3. Convert the query specification into a MySQL database query, and run the database query to get the list of posts, in function WP_Query->get_posts(). Save the posts in the $wp_query object to be used in the WordPress Loop.

Source Codex

Conclusion

If you really want to modify the main query, then you can use a wide variety of filters. Simply use $query->set( 'some_key', 'some_value' ); to change data there or use $query->get( 'some_key' ); to retrieve data to do conditional checks. This will save you from doing a second query, as you're altering the SQL query only.

If you have to do an additional query, then go with a WP_Query object. This will add another query to the DB.

Example

As answers always work better with an example, you here got one really nice one (props to Brad Touesnard), that simply extends the core object and therefore is pretty reusable (make a plugin out of it):

class My_Book_Query extends WP_Query
{
    function __construct( $args = array() )
    {
        // Forced/default args
        $args = array_merge( $args, array(
            'posts_per_page' => -1
        ) );

        add_filter( 'posts_fields', array( $this, 'posts_fields' ) );

        parent::__construct( $args );
    }

    public function posts_fields( $sql )
    {
        return "{$sql}, {$GLOBALS['wpdb']->terms}.name AS 'book_category'";
    }
}

You can then run your second/additional query like you can see in the following example. Don't forget to reset your query afterwards.

$book_query = new My_Book_Query();
if ( $book_query->have_posts() )
{
    while ( $book_query->have_posts() )
    {
        $book_query->the_post();
        # ...do stuff...
    } // endwhile;
    wp_reset_postdata();
} // endif;
6

Please check out the answers at When to use WP_query(), query_posts() and pre_get_posts.

It is a great ressource if you have any doubts in mind.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.