How to get last month’s bestsellers in WooCommerce.

While WooCommerce does have a way to get all-time bestseller products, it doesn’t have a way to do it with a time constraint. Or none that I know of (and I’ve searched the internet for a few hours for that).

In case this is what you need, here are the WP_Query arguments that will get you the first 10 bestselling products of all time.

  $args = array(
    'post_type' => 'product',
    'meta_key' => 'total_sales',
    'orderby' => 'meta_value_num',
    'posts_per_page' => 1,
  );

But in case you want to get the best selling products from a specific time period, like the last 30 days, March or April 2021, or whatever date, we have to go a different route.

Here’s a quick run-through of what we need to do:

  • Get all orders in a speciffic time period
  • Run trough each order and push to an array the items ordered
  • Count how many times each item is in this array (using PHP function array_count_values() )
  • Sort them from the most frequest to the least frequent
  • Take the first 10 items (or however many we need)
  • Return the items IDs

Let’s go through each one now.

The first thing we have to do is to get all the orders within a certain time period. For this example, we will use the last 30 days (or the last month). We will also need to include parameters so that we get all of the completed orders. Here we go:

$args = array(
  'date_created' => '>' . ( time() - MONTH_IN_SECONDS  ),
  'limit' => -1,
  'status' => 'wc-completed'
);


$orders = wc_get_orders( $args );

And after that, we use wc_get_orders() with the args to get them and store them in a variable.

The next step is to loop through the orders and then loop through each item of the order.

$arr = [];

foreach ( $orders as $order ) {
  foreach ( $order->get_items() as $item_id => $item ) {
    array_push( $arr, $item->get_product_id() );
  }
}

Before the loop, we need to create an array to push our items into. We are pushing only the ID, as it’s the only thing we will need at the moment.

Now comes the counting part. No fancy algorithm is needed, only a simple PHP function:

$vals = array_count_values($arr);
arsort($vals);

$relevant = array_slice($vals, 0, 10, true);

So, what we did there. First, we counted the items. Basically, this function returns an associative array that looks like this: [“ID”] => [3]. How many times each item was in the array.

Obviously, after that, we need to sort the array from the highest to the lowest. And then we will take only as many items as we need. In our case, it was 10. You can put whatever you want there.

Now, there are just two more lines of code needed:

$prod_ids = array_keys($relevant);
    
return $prod_ids;

You can combine those two lines, but I keep them separated in order to be easier understood. First, we need to take the keys (or the IDs) of the items in the $relevant array and put them into another one. And then, we will return the $prod_ids array.

This will return an array with the top 10 bestselling products in the last month. Now you can use this array to call different queries anywhere you need.

Obviously, this code would go into your theme functions.php file. I have encapsulated it as a function so I could reuse it with ease anywhere on the website.

Here’s how the final code would look like:

  function get_bestsellers_last_month(){
    
      $args = array(
          'date_created' => '>' . ( time() - MONTH_IN_SECONDS  ),
          'limit' => -1,
          'status' => 'wc-completed'
      );
      $orders = wc_get_orders( $args );
      $arr = [];
    
      foreach ( $orders as $order ) {
          foreach ( $order->get_items() as $item_id => $item ) {
              array_push( $arr, $item->get_product_id() );
          }
      }
     
      $vals = array_count_values($arr);
      arsort($vals);
      $relevant = array_slice($vals, 0, 10, true);
    
      $prod_ids = array_keys($relevant);
    
      return $prod_ids;  
  }

That’s all folks. Hope this snippet will help you. If you have any questions or you need any help, don’t hesitate to contact me.