0
votes

I have tried to get list of top sales product by Woocommerce API, I'm using version 1 of WooCommerce API. I tried this following endpont, but it does not work:

My Request:

https://DOMAINNAME.com/wp-json/wc/v1/products?consumer_key=CONSUMER_KEY&consumer_secret=CONSUMER_SECRET&category=CATEGORY_ID&order=asc&orderby=total_sales

But it Responses:

{
    "code": "rest_invalid_param",
    "message": "Invalid parameter(s): orderby",
    "data": {
        "status": 400,
        "params": {
            "orderby": "orderby is not one of date, id, include, title, slug."
        }
    }
}

orderby is not one of date, id, include, title, slug. So, I can order by date, ID, include, title and slug only. How can I order by total_sales . Is it possible to do? Or can you suggest any other way?

4

4 Answers

1
votes

You'll need to add a REST API filter via functions.php in your theme (or a plugin file) in order to make sure that meta_key and meta_value are allowed to be used.


add_filter('rest_endpoints', function ($routes) {
    // I'm modifying multiple types here, you won't need the loop if you're just doing WooCommerce products
    foreach (['product', 'another_type'] as $type) {
        if (!($route =& $routes['/wp/v2/' . $type])) {
            continue;
        }

        // Allow ordering by my meta value
        $route[0]['args']['orderby']['enum'][] = 'meta_value_num';

        // Allow only the meta keys that I want
        $route[0]['args']['meta_key'] = array(
            'description'       => 'The meta key to query.',
            'type'              => 'string',
            'enum'              => ['my_meta_key', 'another_key'],
            'validate_callback' => 'rest_validate_request_arg',
        );
    }

return $routes;
});

you can add any meta keys you'd like added to the enum values. Doing it this way (instead of adding a filter to add endpoints for all meta_key) keeps potentially sensitive information saved as metadata from being publicly readable.

Then, try this for your URL:

https://DOMAINNAME.com/wp-json/wc/v1/products?consumer_key=CONSUMER_KEY&consumer_secret=CONSUMER_SECRET&category=CATEGORY_ID&order=asc&filter[meta_key]=total_sales&orderby=meta_value_num

0
votes

For People Who are Still Stumbling upon this Question

As per WC v3 API. You can now pull Most Popular products through the API (which is the top selling product list as per my understanding);

GET /wp-json/wc/v3/products?orderby=popularity&order=desc

This should get you the most popular products in descending order by the number of sales. This is not currently documented (as of Feb 2, 2021).

Total list of possible orderby params are: date, id, include, title, slug, modified, menu_order, price, popularity, rating

-1
votes

I know this question is old, but I wrote this function because I had the same issue as this question.

The total_sales meta data for products is never correct. Don't use it.

This function sums total sales by product ID for only processing and completed orders, so it will not count any cancelled, refunded, pending, etc.

It is based on this answer: Woocommerce: Get all orders for a product

function get_total_sales_product_id( $product_id, $order_status = array( 'wc-completed', 'wc-processing' ) ){
global $wpdb;

$order_ids = $wpdb->get_col("
    SELECT order_items.order_id
    FROM {$wpdb->prefix}woocommerce_order_items as order_items
    LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
    LEFT JOIN {$wpdb->posts} AS posts ON order_items.order_id = posts.ID
    WHERE posts.post_type = 'shop_order'
    AND posts.post_status IN ( '" . implode( "','", $order_status ) . "' )
    AND order_items.order_item_type = 'line_item'
    AND order_item_meta.meta_key = '_product_id'
    AND order_item_meta.meta_value = '$product_id'
");

$unique_order_ids = array_unique($order_ids);

$total_sales = 0;
foreach ($unique_order_ids as $order_id) {
    $order = wc_get_order($order_id);
    foreach ($order->get_items() as $item_key => $item ) {  
        if ($item->get_product()->get_id() == $product_id) {
            $total_sales = $total_sales +  $item->get_quantity();
        }
    }
}

return $total_sales;

}

-1
votes

I think the best way is not to edit your functions.php it is not really clean.

In general the best way to work correctly with your API Data is to make your big data call from your API and after organize your data from your front app. This is what I did to get all WooCommerce products sorted by total_sales on my mobile app directly.

I used it in JS by an array sort and get first 10 top sales products:

const productsTopSales = products.sort((a, b) => a.total_sales < b.total_sales).slice(0,10)

I think in PHP is like this:

$products_sorted = usort($products, function($a, $b){
 return $a["total_sales"] < $b["total_sales"];
};
$products_top_sales = array_slice($products_sorted, 0, 10);

Tell me if it is good for you!

Cheers 🍻