Multiple orderby parameters in pre_get_posts() action

Referencing @Otto’s response to a question I also had about ordering by multiple fields, here is what he said:

Can’t do it with a naive WP_Query. Use the posts_orderby filter to add
your own ordering string.

Read More
function my_order($orderby) { 
    global $wpdb; 
    return "{$wpdb->posts.post_author} ASC, {$wpdb->posts.post_date} DESC"; 
} 
add_filter( 'posts_orderby', 'my_order' ); 

$blah = new WP_Query(...); 

remove_filter( 'posts_orderby', 'my_order' ); 

-Otto

This appears to be the way it would be done in a new call to WP_Query –> how would I go about this same thing in a pre_get_posts() action with two meta fields, with a default sort to?:

function mouldings_sort($query) {
    if ($query->is_main_query() && is_tax(array('wood_types','profile_categories','combination_categories'))) {

        $query->set('meta_key', '_mouldings_dimensions_height');
        $query->set('order', 'DESC');
        $query->set('orderby','meta_value_num');
    }

}
add_action('pre_get_posts','mouldings_sort');

I had previously tried simply adding in another meta field like so:

$query->set('meta_key', array('_mouldings_dimensions_height', '_mouldings_dimension_width'));
$query->set('orderby','meta_value_num');

with a default sortback of title as so:

 $query->set('orderby','meta_value_num title');

but it doesn’t look like meta_key can accept arrays and my title fallback goes back to Otto’s original response on the matter. Any help would be a greatly appreciated. Thanks!

Related posts

Leave a Reply

2 comments

  1. Never forget that there’re actually two filters

    // Add additional query args that are allowed by the API by default
    pre_get_posts
    // Modify the query herself
    posts_clauses
    // Inspect the resulting string - test this one in for e.g. phpMyAdmin
    posts_request
    

    So everything you can achieve using the pre_get_posts filter should be done there. The rest should be modified using the posts_clauses (or one of the more specific filters before).

    // Modify the original query parts
    function wpse70214_pre_get_posts( $query )
    {
        var_dump( $query );
        return $query;
    }
    add_filter( 'pre_get_posts', 'wpse70214_pre_get_posts' );
    
    // Modify the resulting query string parts
    function wpse70214_posts_clauses( $pieces )
    {
        var_dump( $pieces );
        return $pieces;
    }
    add_filter( 'posts_clauses', 'wpse70214_posts_clauses' );
    
    // Then check the result
    function wpse70214_posts_request( $query_string )
    {
        var_dump( $query_string );
        return $query_string;
    }
    add_action( 'posts_request', 'wpse70214_posts_request' );
    
  2. Yes like Otto said you can’t have a secondary ORDER BY clause without a a custom posts_orderby filter. If you need to know what query your on a la “pre_get_posts” you can create a function that adds the orderby filter to posts_orderby and call it from pre_get_posts.

    /**
     * Posts orderby filter.  The filter will be added using pre_get_posts outside the class
     *  using the pre_get_posts action allows us to do checks for what page etc...
     * @return string, new MySQL ORDER BY clause
     */
    function wpse_order_by() {
        global $wpdb;
        return $wpdb->prepare( "$wpdb->postmeta.meta_value+0 DESC, post_title DESC" );
    }
    
    /**
     * Pre get posts filter for adding secondary fall back ORDER BY clause to MySql query
     * @uses remove_filter(), This filter removes itself after it runs to prevent it from affecting  other queries on the same page.
     * @uses add_filter()
     * @param object|array $query the current $query object
     */
    function wpse_post_order_pre( $query ) {
        if ($query->is_main_query() && is_tax(array('wood_types','profile_categories','combination_categories'))) {
    
        /** remove_filter() is used to prevent this affecting additional queries on the page */
        remove_filter( current_filter(), __FUNCTION__ );
    
        add_filter( 'posts_orderby', 'wpse_order_by' );
        }
    }
    
    add_action( 'pre_get_posts', 'wpse_post_order_pre' );