skip to Main Content

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).


Solution attempts

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.

2

Answers


  1. 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 active child theme (or active theme). Tested and works.

    EXAMPLE USAGE

    • Handle float numbers.
    • The 1st price is separated from the 2nd one by a pipe |

    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

    Login or Signup to reply.
  2. function handle_custom_query($query, $query_vars)
    {
      if (!empty($query_vars['meta_query'])) {
        foreach ($query_vars['meta_query'] as $q) {
          $query['meta_query'][] = $q;
        }
      }
      return $query;
    }
    add_filter('woocommerce_product_data_store_cpt_get_products_query', 'handle_custom_query', 10, 3);
    

    usage:

    $query = new WC_Product_Query(
      array(
        // ...standard queries
        // https://github.com/woocommerce/woocommerce/wiki/wc_get_products-and-WC_Product_Query
    
        'meta_query'     => array(
          // ...array of meta queries
        ),
    
      )
    );
    $products = $query->get_products();
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search