Make meta query treat meta_value as 1 or 0
-
22-04-2021 - |
Вопрос
My products have no in-stock status, just a quantity in stock. Is there any way to make meta query to treat its value as 0 or 1? Like, first the values that are not 0, then that are 0.
My query as it is right now:
'meta_query' => array(
'relation' => 'AND',
'quantity_total' => array(
'key' => 'total_quantity',
'type' => 'NUMERIC',
'compare' => 'EXISTS',
),
'price_lowest_first' => array(
'key' => 'main_price',
'type' => 'NUMERIC',
'compare' => 'EXISTS',
)
),
'orderby' => array(
'quantity_total' => 'DESC',
'price_lowest_first' => 'ASC'
)
EDIT:
For now, I ended up using an 'in-stock' flag, and here's my code:
//sort by total quantity
if($sort !== '4'){
$args['meta_query']['in_stock'] = array(
'key' => '1C_in_stock',
'type' => 'NUMERIC',
'compare' => 'EXISTS'
);
$args['orderby'] = array(
'in_stock' => 'DESC'
);
}
//apply sort preferences to arguments
switch($sort){
#by stock status
case '1':
// '1' is default, do not want to move it to the default clause yet.
break;
#sort by price: lower first
case '2':
$args['meta_query']['price_lowest_first'] = array(
'key' => '1C_price'
'type' => 'NUMERIC',
'compare' => 'EXISTS'
);
$args['orderby']['price_lowest_first'] = 'ASC';
break;
#sort by price:higher first
case '3':
$args['meta_query']['price_highest_first'] = array(
'key' => '1C_price',
'type' => 'NUMERIC',
'compare' => 'EXISTS',
);
$args['orderby']['price_highest_first'] = 'DESC';
break;
#sort randomly
case '4':
$args['orderby'] = 'rand';
break;
}
But is there a query to order items by non-zero and zero quantity? And then, order each group by price.
Решение
If I understand it correctly..
What you're looking for can be achieved using the CASE
statement in MySQL, where you would use the following in the ORDER BY
clause:
# group the quantity meta into two groups
CASE
# group one - all quantities that are 1 or more; we flag them as "1"
WHEN wp_postmeta.meta_value+0 > 0 THEN 1
# group two - all quantities that are 0 or less; we flag them as "2"
WHEN wp_postmeta.meta_value+0 <= 0 THEN 2
END ASC
Where wp_postmeta.meta_value
is the value of the total_quantity
meta which is the first one in your meta query, and the +0
means we're casting the meta value as a number.
And to add that CASE
statement to the ORDER BY
clause, you can use the posts_orderby
hook. Here's an example using closure with a private variable:
// *Private variable used with the closure below.
$_do_filter = true;
add_filter( 'posts_orderby',
function ( $orderby ) use ( &$_do_filter ) {
if ( $_do_filter ) {
$orderby = "CASE\n" .
" WHEN wp_postmeta.meta_value+0 > 0 THEN 1\n" .
" WHEN wp_postmeta.meta_value+0 <= 0 THEN 2\n" .
"END ASC, $orderby";
}
return $orderby;
}
);
$query = new WP_Query( array(
'meta_query' => array(
'relation' => 'AND',
'quantity_total' => array(
'key' => 'total_quantity',
'type' => 'NUMERIC',
'compare' => 'EXISTS',
),
'price_lowest_first' => array(
'key' => 'main_price',
'type' => 'DECIMAL(5, 2)',
'compare' => 'EXISTS',
),
),
'orderby' => array(
// we already have the CASE statement, so this is no longer needed
// 'quantity_total' => 'DESC',
'price_lowest_first' => 'ASC',
),
// ... your other args.
) );
// Disable the filter.
$_do_filter = false;
// Then run your loop here..
// while ( $query->have_posts() ) ...
And note that for the main_price
meta, I set the type
to DECIMAL(5, 2)
so that the prices would be sorted by taking into account the decimal.