* REST API Leaderboards Controller
* Handles requests to /leaderboards
namespace Automattic\WooCommerce\Admin\API;
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Admin\API\Reports\Categories\DataStore as CategoriesDataStore;
use Automattic\WooCommerce\Admin\API\Reports\Coupons\DataStore as CouponsDataStore;
use Automattic\WooCommerce\Admin\API\Reports\Customers\DataStore as CustomersDataStore;
use Automattic\WooCommerce\Admin\API\Reports\Products\DataStore as ProductsDataStore;
* Leaderboards controller.
* @extends WC_REST_Data_Controller
class Leaderboards extends \WC_REST_Data_Controller {
protected $namespace = 'wc-analytics';
protected $rest_base = 'leaderboards';
public function register_routes() {
'methods' => \WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => $this->get_collection_params(),
'schema' => array( $this, 'get_public_item_schema' ),
'/' . $this->rest_base . '/allowed',
'methods' => \WP_REST_Server::READABLE,
'callback' => array( $this, 'get_allowed_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'schema' => array( $this, 'get_public_allowed_item_schema' ),
'/' . $this->rest_base . '/(?P<leaderboard>\w+)',
'enum' => array( 'customers', 'coupons', 'categories', 'products' ),
'methods' => \WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => $this->get_collection_params(),
'schema' => array( $this, 'get_public_item_schema' ),
* Get the data for the coupons leaderboard.
* @param int $per_page Number of rows.
* @param string $after Items after date.
* @param string $before Items before date.
* @param string $persisted_query URL query string.
protected function get_coupons_leaderboard( $per_page, $after, $before, $persisted_query ) {
$coupons_data_store = new CouponsDataStore();
$coupons_data = $per_page > 0 ? $coupons_data_store->get_data(
'woocommerce_analytics_coupons_query_args',
'orderby' => 'orders_count',
foreach ( $coupons_data as $coupon ) {
$url_query = wp_parse_args(
'filter' => 'single_coupon',
'coupons' => $coupon['coupon_id'],
$coupon_url = wc_admin_url( '/analytics/coupons', $url_query );
$coupon_code = isset( $coupon['extended_info'] ) && isset( $coupon['extended_info']['code'] ) ? $coupon['extended_info']['code'] : '';
'display' => "<a href='{$coupon_url}'>{$coupon_code}</a>",
'display' => wc_admin_number_format( $coupon['orders_count'] ),
'value' => $coupon['orders_count'],
'display' => wc_price( $coupon['amount'] ),
'value' => $coupon['amount'],
'label' => __( 'Top Coupons - Number of Orders', 'woocommerce' ),
'label' => __( 'Coupon code', 'woocommerce' ),
'label' => __( 'Orders', 'woocommerce' ),
'label' => __( 'Amount discounted', 'woocommerce' ),
* Get the data for the categories leaderboard.
* @param int $per_page Number of rows.
* @param string $after Items after date.
* @param string $before Items before date.
* @param string $persisted_query URL query string.
protected function get_categories_leaderboard( $per_page, $after, $before, $persisted_query ) {
$categories_data_store = new CategoriesDataStore();
$categories_data = $per_page > 0 ? $categories_data_store->get_data(
'woocommerce_analytics_categories_query_args',
'orderby' => 'items_sold',
foreach ( $categories_data as $category ) {
$url_query = wp_parse_args(
'filter' => 'single_category',
'categories' => $category['category_id'],
$category_url = wc_admin_url( '/analytics/categories', $url_query );
$category_name = isset( $category['extended_info'] ) && isset( $category['extended_info']['name'] ) ? $category['extended_info']['name'] : '';
'display' => "<a href='{$category_url}'>{$category_name}</a>",
'value' => $category_name,
'display' => wc_admin_number_format( $category['items_sold'] ),
'value' => $category['items_sold'],
'display' => wc_price( $category['net_revenue'] ),
'value' => $category['net_revenue'],
'label' => __( 'Top categories - Items sold', 'woocommerce' ),
'label' => __( 'Category', 'woocommerce' ),
'label' => __( 'Items sold', 'woocommerce' ),
'label' => __( 'Net sales', 'woocommerce' ),
* Get the data for the customers leaderboard.
* @param int $per_page Number of rows.
* @param string $after Items after date.
* @param string $before Items before date.
* @param string $persisted_query URL query string.
protected function get_customers_leaderboard( $per_page, $after, $before, $persisted_query ) {
$customers_data_store = new CustomersDataStore();
$customers_data = $per_page > 0 ? $customers_data_store->get_data(
'woocommerce_analytics_customers_query_args',
'orderby' => 'total_spend',
'order_before' => $before,
foreach ( $customers_data as $customer ) {
$url_query = wp_parse_args(
'filter' => 'single_customer',
'customers' => $customer['id'],
$customer_url = wc_admin_url( '/analytics/customers', $url_query );
'display' => "<a href='{$customer_url}'>{$customer['name']}</a>",
'value' => $customer['name'],
'display' => wc_admin_number_format( $customer['orders_count'] ),
'value' => $customer['orders_count'],
'display' => wc_price( $customer['total_spend'] ),
'value' => $customer['total_spend'],
'label' => __( 'Top Customers - Total Spend', 'woocommerce' ),
'label' => __( 'Customer Name', 'woocommerce' ),
'label' => __( 'Orders', 'woocommerce' ),
'label' => __( 'Total Spend', 'woocommerce' ),
* Get the data for the products leaderboard.
* @param int $per_page Number of rows.
* @param string $after Items after date.
* @param string $before Items before date.
* @param string $persisted_query URL query string.
protected function get_products_leaderboard( $per_page, $after, $before, $persisted_query ) {
$products_data_store = new ProductsDataStore();
$products_data = $per_page > 0 ? $products_data_store->get_data(
'woocommerce_analytics_products_query_args',
'orderby' => 'items_sold',
foreach ( $products_data as $product ) {
$url_query = wp_parse_args(
'filter' => 'single_product',
'products' => $product['product_id'],
$product_url = wc_admin_url( '/analytics/products', $url_query );
$product_name = isset( $product['extended_info'] ) && isset( $product['extended_info']['name'] ) ? $product['extended_info']['name'] : '';
'display' => "<a href='{$product_url}'>{$product_name}</a>",
'value' => $product_name,
'display' => wc_admin_number_format( $product['items_sold'] ),
'value' => $product['items_sold'],
'display' => wc_price( $product['net_revenue'] ),
'value' => $product['net_revenue'],
'label' => __( 'Top products - Items sold', 'woocommerce' ),
'label' => __( 'Product', 'woocommerce' ),
'label' => __( 'Items sold', 'woocommerce' ),
'label' => __( 'Net sales', 'woocommerce' ),
* Get an array of all leaderboards.
* @param int $per_page Number of rows.
* @param string $after Items after date.
* @param string $before Items before date.
* @param string $persisted_query URL query string.
public function get_leaderboards( $per_page, $after, $before, $persisted_query ) {
$this->get_customers_leaderboard( $per_page, $after, $before, $persisted_query ),
$this->get_coupons_leaderboard( $per_page, $after, $before, $persisted_query ),
$this->get_categories_leaderboard( $per_page, $after, $before, $persisted_query ),
$this->get_products_leaderboard( $per_page, $after, $before, $persisted_query ),
return apply_filters( 'woocommerce_leaderboards', $leaderboards, $per_page, $after, $before, $persisted_query );
* Return all leaderboards.
* @param WP_REST_Request $request Request data.
* @return WP_Error|WP_REST_Response
public function get_items( $request ) {
$persisted_query = json_decode( $request['persisted_query'], true );
switch ( $request['leaderboard'] ) {
$leaderboards = array( $this->get_customers_leaderboard( $request['per_page'], $request['after'], $request['before'], $persisted_query ) );
$leaderboards = array( $this->get_coupons_leaderboard( $request['per_page'], $request['after'], $request['before'], $persisted_query ) );
$leaderboards = array( $this->get_categories_leaderboard( $request['per_page'], $request['after'], $request['before'], $persisted_query ) );
$leaderboards = array( $this->get_products_leaderboard( $request['per_page'], $request['after'], $request['before'], $persisted_query ) );
$leaderboards = $this->get_leaderboards( $request['per_page'], $request['after'], $request['before'], $persisted_query );
if ( ! empty( $leaderboards ) ) {
foreach ( $leaderboards as $leaderboard ) {
$response = $this->prepare_item_for_response( $leaderboard, $request );
$data[] = $this->prepare_response_for_collection( $response );
return rest_ensure_response( $data );
* Returns a list of allowed leaderboards.
* @param WP_REST_Request $request Request data.
public function get_allowed_items( $request ) {
$leaderboards = $this->get_leaderboards( 0, null, null, null );
foreach ( $leaderboards as $leaderboard ) {
$data[] = (object) array(
'id' => $leaderboard['id'],
'label' => $leaderboard['label'],
'headers' => $leaderboard['headers'],
foreach ( $data as $item ) {
$prepared = $this->prepare_item_for_response( $item, $request );
$objects[] = $this->prepare_response_for_collection( $prepared );
$response = rest_ensure_response( $objects );
$response->header( 'X-WP-Total', count( $data ) );
$response->header( 'X-WP-TotalPages', 1 );
$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
* Prepare the data object for response.
* @param object $item Data object.
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response $response Response data.
public function prepare_item_for_response( $item, $request ) {
$data = $this->add_additional_fields_to_object( $item, $request );
$data = $this->filter_response_by_context( $data, 'view' );
$response = rest_ensure_response( $data );
* Filter the list returned from the API.
* @param WP_REST_Response $response The response object.
* @param array $item The original item.
* @param WP_REST_Request $request Request used to generate the response.
return apply_filters( 'woocommerce_rest_prepare_leaderboard', $response, $item, $request );
* Get the query params for collections.
public function get_collection_params() {
'description' => __( 'Current page of the collection.', 'woocommerce' ),
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
$params['per_page'] = array(