I’m experimenting with searches and multiple criterias and ordering. Let’s say we have a real estate site, all posts have 2 custom fields (price
and surface
), and we want the visitor to be able to search by entering a :
- minimum price
- maximum price
- minimum surface
- maximum surface
This would do the job :
function mwm_register_search_filters( $query ) {
// Only on archive post listings
if( $query->is_archive ) {
$filters_a = array();
if( !empty( $_GET['price_min'] ) ) $filters_a[] = array( 'key' => 'price', 'value' => $_GET['price_min'], 'compare' => '>=', 'type' => 'numeric' );
if( !empty( $_GET['price_max'] ) ) $filters_a[] = array( 'key' => 'price', 'value' => $_GET['price_max'], 'compare' => '<=', 'type' => 'numeric' );
if( !empty( $_GET['surface_min'] ) ) $filters_a[] = array( 'key' => 'surface', 'value' => $_GET['surface_min'], 'compare' => '>=', 'type' => 'numeric' );
if( !empty( $_GET['surface_max'] ) ) $filters_a[] = array( 'key' => 'surface', 'value' => $_GET['surface_max'], 'compare' => '<=', 'type' => 'numeric' );
if( !empty( $filters_a ) ) $query->set( 'meta_query' , $filters_a );
}
return $query;
}
add_filter( 'pre_get_posts' , 'mwm_register_search_filters' );
That’s great and it works well. But then comes ordering..
AFAIK WordPress only allows ordering by one meta_key / meta_value. That’s nice but not good enough when you need to order by multiple criterias. Is there a way to order for example :
- First by price ASC
- And within equal prices, order by surface ASC
As of WordPress 3.4.1, there is no way, out of the box, to order by multiple meta values.
Since we can’t do it cleanly, we’ll have to hack something together. We can hook into
posts_orderby
to manually modify the ordering. It’s not altogether pretty, but it will get the job done. Here’s an example:Most importantly, this assumes that “surface” is the second meta_key in your query (that’s where mt1 comes from; mt1 is the second meta key, mt2 is the third, and so on), which in your case won’t always be correct. Therefore, the
add_filter
call should go behind three checks:surface
meta_key is being queried (if the $_GET param is present), andsurface
meta_key is in the join sequence, then adjustmt1
accordinglyIt’s janky, but again, it will work.