WooCommerce – add ‘Sort by On Sale’ option on archive pages

For a recent project I’m working on I needed an additional option adding to the ‘Sort by’ dropdown that WooCommerce comes with out the box on archive pages.

What I found was, that with 3 filters, it was possible but it only returned Simple product results and skipped Variable ones.

Below is my modified version that includes Variable products – it may need tweaking to allow for other $_GET variables to be in the current URL (like native filter options) but as it stands, it orders the products by that are on sale first…

functions.php

/**
* Add the 'Sort by Sale' option to the select box
*/
add_filter( 'woocommerce_catalog_orderby', 'add_order_by_on_sale');
add_filter( 'woocommerce_default_catalog_orderby_options', 'add_order_by_on_sale' );
function add_order_by_on_sale($sortby) {
    $sortby['on_sale'] = 'Sort by on sale';
    return $sortby;
}

/**
* Modify archive query for products on sale
*/
add_filter( 'woocommerce_get_catalog_ordering_args', 'order_by_on_sale');
function order_by_on_sale($args) {
    $orderby_value = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ));

    if ( 'on_sale' == $orderby_value ) {
        $args['meta_query'] = array(
            'relation' => 'OR',
            array( // Simple products type
                'key'           => '_sale_price',
                'value'         => 0,
                'compare'       => '>',
                'type'          => 'numeric'
            ),
            array( // Variable products type
                'key'           => '_min_variation_sale_price',
                'value'         => 0,
                'compare'       => '>',
                'type'          => 'numeric'
            )
         );
    }
    return $args;
}