WOOT - WooCommerce Active Products Tables

WooCommerce product table sorting: default order, columns and sales

WooCommerce product table sorting has three parts: the order the table starts with, the column headers visitors can click, and an optional sorting drop-down. Each one is set in a different place and reads its data differently, which explains most "wrong order" questions: a taxonomy column without sort arrows, a calculated column that cannot be sorted, best sellers that do not match real sales. Below is how each part works in WOOT, how to sort by your own fields, and how to switch column sorting off.

Three ways a WooCommerce product table is sorted

The default order. In the table's Options tab, Default order by sets the field the table is sorted by when it loads, and Default order sets the direction, asc or desc. New tables start with ID, descending. The shortcode can override both with the orderby and order attributes, using the field keys of the columns:

[woot id=12 orderby="menu_order" order="asc"]
[woot id=12 orderby="total_sales" order="desc"]

Other keys are post_title, price, sku, stock_quantity, post_date and, for a field from the table's Meta tab, its meta key. The attribute is orderby, in one word: order_by is not recognised and is silently ignored. Keep the direction in mind with Menu order: the menu order runs from low to high, so it needs asc, while the default direction of a new table is desc, which turns the list upside down.

Column headers. Sortable columns show two small arrows in the header. A click sorts by that column, the next click reverses the direction, and the table goes back to page one.

The Sorting Dropdown. Turn on Show Sorting Dropdown in Options and choose the entries in Sorting Dropdown Fields (press and wait on an entry to reorder). The list contains the familiar WooCommerce sortings (popularity, average rating, latest, price low to high and high to low) plus name, SKU, stock quantity, total sales, number of comments, last modified date, menu order, sales products first, availability, random and product ID. Every field from the table's Meta tab is added as Title: Ascending and Title: Descending. The first entry, Sorted by table, returns to the header sorting. In a shortcode, list the keys with the orderby_select_fields attribute, for example orderby_select_fields="popularity,price,price-desc,date". The drop-down is available in tables that load their rows by AJAX, the normal mode of [woot].

Two drop-down entries have no direction of their own: Sort by latest and Sort by menu order use the table's current direction. With Default order set to desc, "latest" shows the newest products first and "menu order" runs backwards, so add only the one that matches your default direction.

Which columns can be sorted by clicking the header

A column is sortable when its data is a field of the product post or a single meta value that the database can order: ID, title, status, post date, modified date, comment count, price, regular and sale price, SKU, weight and dimensions, stock status, stock quantity, total sales, review count, rating, menu order, and the text and number fields of the Meta tab. The first click uses the column's own starting direction: ID, dates, comment count and number fields of the Meta tab start from the highest value; title, prices, SKU, stock, total sales, ratings and menu order start from the lowest.

Taxonomy columns (categories, tags, attributes such as length or color) cannot be sorted. A product can have several terms in one taxonomy, and WordPress's product query has no way to order products by the names of their terms. Columns that are drawn by code, such as the thumbnail, the add-to-cart button or a value changed in a filter, cannot be sorted either, because the database does not hold that value.

Variable products need one more note. WooCommerce stores one _price value per distinct variation price, so in a header sort by Price a variable product is not guaranteed to sit at its lowest price. For shops with variable products, offer Sort by price: low to high and Sort by price: high to low in the Sorting Dropdown: these use WooCommerce's own lowest and highest price of each product.

Sorting by a custom field or a calculated value

To sort by any custom field, add it in the Meta tab with its exact meta key and the type text (alphabetical) or number (numeric), then add it as a column. Sorting by a meta key shows only products that have a value for that key, which is how WordPress builds the query; this FAQ shows how to include the others.

A value that you calculate in the woot_meta_data_cell hook is display only: the hook changes the text of the cell after the products have been loaded, so sorting still uses whatever is stored under that meta key, and if nothing is stored, nothing can be sorted. The fix is to store the calculated value as real product meta and sort by it. Example: a stock value (price × stock quantity) kept up to date when a product is saved and when its stock changes after an order.

/*
 * Store "stock value" (price x stock quantity) as product meta _stock_value,
 * so a WOOT Meta field with this key and the type "number" can be sorted.
 * Child theme functions.php or a code snippets plugin.
 */
function my_store_stock_value($product) {
    if (!is_a($product, 'WC_Product')) {
        $product = wc_get_product($product);
    }
    if (!$product) {
        return;
    }

    $value = (float) $product->get_price() * (int) $product->get_stock_quantity();
    update_post_meta($product->get_id(), '_stock_value', round($value, 2));
}
add_action('woocommerce_new_product', 'my_store_stock_value');
add_action('woocommerce_update_product', 'my_store_stock_value');
add_action('woocommerce_product_set_stock', 'my_store_stock_value');

Existing products get the value the next time they are saved. To fill it for all of them at once, select them in Products, choose Edit in Bulk actions, click Apply and then Update without changing any field. The same approach makes a taxonomy sortable: inside the function, read the term with wp_get_post_terms() and store its name or number in a meta key.

Total sales and popularity: the same number from two places

The Total sales column, Default order by → Total sales and the Total Sales: Ascending/Descending drop-down entries sort by the product meta field total_sales (the drop-down entries also leave out grouped and external products). Sort by popularity uses WooCommerce's product lookup table, the same sorting as the shop page: total_sales from high to low, then newer product IDs first. Normally both hold the same number.

WooCommerce raises total_sales when an order reaches Processing, On hold or Completed. Sales history that was imported, or orders created without these status changes, never reach that field, so "best sellers" follow only the sales made since. To sort by full history, write the totals into total_sales (most import tools can map this field), then run WooCommerce → Status → Tools → Product lookup tables → Regenerate so that popularity uses the same numbers. For a fixed best-sellers table, see tables with best selling products.

Turning column sorting off

For a table created in WOOT → Tables, select the columns in Disable columns order-by in the Options tab. Their arrows disappear and the headers are no longer clickable; the default order still applies.

The [woot_upsells], [woot_cross_sells], [woot_related], [woot_grouped] and [woot_variations] shortcodes are not tied to a table, so they have no such option and no attribute for it. This code switches header sorting off in those tables only:

/*
 * WOOT: no header sorting in the upsells, cross-sells, related,
 * grouped and variations tables. Other tables keep their sorting.
 * Child theme functions.php or a code snippets plugin.
 */
add_filter('woot_profile_extend', function ($profile, $action, $shortcode_args = []) {
    $no_sorting = ['woot_upsells', 'woot_cross_sells', 'woot_related', 'woot_grouped', 'woot_variations'];

    if (isset($shortcode_args['filter_provider']) && in_array($shortcode_args['filter_provider'], $no_sorting, true)) {
        foreach ($profile as $key => $field) {
            if (is_array($field) && !empty($field['order'])) {
                $profile[$key]['order'] = false;
            }
        }
    }

    return $profile;
}, 10, 3);

Remove the shortcodes you want to keep sortable from the $no_sorting list. The table also repeats the column titles in a footer row. There is no option for it; hide it with CSS. In the table's Custom CSS tab (the plugin limits the rules to that table):

table tfoot { display: none; }

For every table on the site, put this into Appearance → Customize → Additional CSS instead:

.woot-data-table table tfoot { display: none; }

Sorting upsells, cross-sells and related product tables

These shortcodes sort by product title, A to Z, unless you set orderby and order:

[woot_upsells columns="thumbnail,sku,title,price" orderby="menu_order"]
[woot_upsells columns="thumbnail,sku,title,price" orderby="post__in"]

menu_order uses the products' menu order (drag and drop in the Sorting view of the Products list, or the Menu order field in the product's Advanced tab). post__in keeps the order in which the products are saved in the product's Upsells or Cross-sells field, or as children of a grouped product. Related products are picked at random by WooCommerce, so for them use a field: orderby="menu_order", or orderby="total_sales" order="desc" for best sellers first (the direction of these tables is asc unless you set it). These tables load all their rows at once, and a header click re-sorts the loaded rows in the browser by the content of the cells. That works for plain values such as SKU or stock, but prices and linked titles are compared as text, which is one more reason to switch header sorting off in these tables with the code above.

The shortcodes find the product from the page they are on. Used elsewhere (a normal page, a tab built by code, a PDF generated by another plugin), they print WOOT: Set product id please in the shortcode attribute!. Pass the product ID: [woot_upsells id=123]. The [woot_upsells] page lists the other attributes.

If the order still looks wrong

  • Check the shortcode: an orderby or order attribute overrides the table's Options.
  • Check the direction: Default order applies to every field, including menu order.
  • For a meta field, confirm that the products have a value under that exact key; products without it are not shown while sorting by it.
  • For best sellers, compare the total_sales value of two products with their real sales, and regenerate the product lookup tables after correcting it.
  • Clear the page cache after changing Options, then reload the page logged out.

If a column still sorts in an order you cannot explain, open a topic on the WOOT support forum with the table shortcode, the column and two products that appear in the wrong order.