I'm getting a bunch of paginated variable products using wc_get_products(). And I would like to only get products in a given price-range. The problem is that there are a lot of products, so only 20 products are fetched at a time.
According to the documentation, there are no meta_query()-option when using wc_get_products().
So how do I get this?
Here is my current query:
$products_query = [
'category' => [ get_queried_object()->slug ],
'limit' => 20,
'paginate' => true,
'paged' => (get_query_var('paged')) ? get_query_var('paged') : 1,
];
$products = wc_get_products( $products_query );
Now remember, that it's something that needs to be implemented before the products are queried from the database.
Let's say that I have 100 products total. Since the products are paginated, then if I have ordered the products after _price (ASC), then page 1 should return the 20 cheapest products. And page 3 should return the products 40-60 (after they have been sorted).
Attempt 1 - Try using a meta_query anyways
I figured that wc_get_products was inspired and built on top of WP_Query, so I tried this:
$products_query = [
'category' => [ get_queried_object()->slug ],
'limit' => 20,
'paginate' => true,
'paged' => (get_query_var('paged')) ? get_query_var('paged') : 1,
'meta_query' => [
'relation' => 'AND',
[
'key' => '_price',
'value' => 100,
'compare' => '<=',
'type' => 'NUMERIC'
]
]
];
$products = wc_get_products( $products_query );
It just ignores the meta_query-part and returns a result as if it wasn't there.
Attempt 2 - Using WP_Query
Everywhere I look, there are arrows pointing towards wc_get_products and away from wp_query.
So I haven't pursued this.
Attempt 3 - Raw SQL
One solution would be to make a raw SQL-query. But since the products are variable products, then the SQL-query would be quite a few joins, since it should first find all products - and then find all variations. And then sort the products after the lowest variation-price (if the price-sort is ascending) - and after the highest variation-price (if the price-sort is descending). It's obviously possible, but I was hoping to find a more WordPress-like solution. And I'm not good at SQL.
Attempt 4 - Using a plugin
I look around a bit - and sort'n'filter-plugins for WooCommerce are monsters, since they slab scripts and styles left, right and center. So this seems like a poor solution.
Attempt 5 - Using WooCommerce API
I can see that min_price and max_price are mentioned under List all products. So this might actually work.
Variable products are something complicated to filter by price… So the following is not perfect, but show you a way to enable a price range in WC_Product_Query.
So the following function will enable a custom price range query on WC_Product_Query:
add_filter( 'woocommerce_product_data_store_cpt_get_products_query', 'handle_price_range_query_var', 10, 2 );
function handle_price_range_query_var( $query, $query_vars ) {
if ( ! empty( $query_vars['price_range'] ) ) {
$price_range = explode( '|', esc_attr($query_vars['price_range']) );
if ( is_array($price_range) && count($price_range) == 2 ) {
$query['meta_query']['relation'] = 'AND';
$query['meta_query'][] = array(
'key' => '_price',
'value' => reset($price_range), // From price value
'compare' => '>=',
'type' => 'NUMERIC'
);
$query['meta_query'][] = array(
'key' => '_price',
'value' => end($price_range), // To price value
'compare' => '<=',
'type' => 'NUMERIC'
);
$query['orderby'] = 'meta_value_num'; // sort by price
$query['order'] = 'ASC'; // In ascending order
}
}
return $query;
}
Code goes in functions.php file of the child theme (or in a plugin). Tested and works.
EXAMPLE USAGE
|Here we query products from $10.25 to $50 (price range):
$products = wc_get_products( [
'limit' => 20,
'status' => 'publish',
'price_range' => '10.25|50', // The first price is separated from the 2nd one with a pipe
] );
echo '<ul>';
foreach( $products as $product ) {
echo '<li><a href="'.$product->get_permalink().'">'.$product->get_name().' '.$product->get_price_html().'</a></li>';
}
echo '</ul>';
To filter only variable products, you can add the following line to the query:
'type' => 'variable',
Documentation: wc_get_products and WC_Product_Query
(thanks to @ChadReitsma)
add_filter( 'woocommerce_product_data_store_cpt_get_products_query', 'handle_price_range_query_var', 10, 2 );
function handle_price_range_query_var( $query, $query_vars ) {
if ( ! empty( $query_vars['price_range'] ) ) {
$price_range = explode( '|', esc_attr($query_vars['price_range']) );
if ( is_array($price_range) && count($price_range) == 2 ) {
$query['meta_query'][] = array(
'key' => '_price',
'value' => $price_range, // Price range array
'compare' => 'BETWEEN'
'type' => 'NUMERIC'
);
$query['orderby'] = 'meta_value_num'; // sort by price
$query['order'] = 'ASC'; // In ascending order
}
}
return $query;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With