0

Say I'm doing a query with ordering by meta field:

new WP_Query( array(
    'meta_query' => array(
        'state_clause' => array(
            'key' => 'state'
        ),
    ),
    'orderby' => array( 
        'state_clause' => 'DESC',
    )
) );

Can I apply an SQL function before ordering (without manual SQL query)? Say order by not state but by LTRIM(state)?

5
  • Have you tried? (I would guess that it doesn't work, but would have to go through WP_(Meta_)Query, you can easily find this out by just trying)
    – kero
    Commented Feb 5, 2018 at 8:59
  • You'll have to use filters to change the SQL that WP_Query generates - are you OK with that or does that fall under manual SQL?
    – janh
    Commented Feb 5, 2018 at 9:19
  • 1
    @kero tried what? Replace state by LTRIM(state)? Doesn't work. SQL is meaningless in that cases :-(
    – Kuraga
    Commented Feb 5, 2018 at 10:02
  • well @janh it will be ok if nothing else does work :-)
    – Kuraga
    Commented Feb 5, 2018 at 10:03
  • @kero, if you meant "tried something" or "tried to google" - yes :-)
    – Kuraga
    Commented Feb 5, 2018 at 10:08

1 Answer 1

1

You can use the filter posts_orderby. For a simple example:

add_filter('posts_orderby', 'apply_sql_on_order', 10, 2 );
function apply_sql_on_order($orderby, $query) {
    if(is_array($query->query_vars["orderby"]) && array_key_exists("apply_sql", $query->query_vars["orderby"])) {
        $orderby = preg_replace("/(.*) (ASC|DESC)/", $query->query_vars["orderby"]["apply_sql"] . "($1) $2", $orderby);
    }
    return $orderby;
}

$query = new WP_Query( array(
    'meta_query' => array(
        'state_clause' => array(
            'key' => 'state'
        ),
    ),
    'orderby' => array( 
        'state_clause' => 'DESC',
        "apply_sql" => "LTRIM"
    )
) );

Will get you from

SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) WHERE 1=1  AND ( 
  wp_postmeta.meta_key = 'state'
) AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish') GROUP BY wp_posts.ID ORDER BY CAST(wp_postmeta.meta_value AS CHAR) DESC LIMIT 0, 10

to

SELECT SQL_CALC_FOUND_ROWS  wp_posts.ID FROM wp_posts  INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id ) WHERE 1=1  AND ( 
  wp_postmeta.meta_key = 'state'
) AND wp_posts.post_type = 'post' AND (wp_posts.post_status = 'publish') GROUP BY wp_posts.ID ORDER BY LTRIM(CAST(wp_postmeta.meta_value AS CHAR)) DESC LIMIT 0, 10

WP will simply ignore "apply_sql" in the orderby array, as it's not a valid order clause in the context of this WP_Query.

This is obviously a very rough version and will probably do weird things if you add multiple clauses into your orderby. In that case, it might be easier not to go for some kind of abstraction and simply have the filter callback just manipulate the SQL.

If you'd like to expand on this, looking at the source will probably help. Most of the magic is happening in parse_orderby in WP_Query. You could create your own version of this that will basically emulate it and add some fancy stuff on the side (possibly unset orderby on the query with pre_get_posts and then add the fancyfied order clauses back in with posts_orderby).

Your Answer

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

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