17

I need to create a products archive page (usually the Shop page in WooCommerce) but displays ONLY the ON SALE products. Basically it should use the same template layout as that in the archive-product.php. There will be a link in the main menu that will direct to this page. How do I go about this?

UPDATE

I managed to filter out the ON SALE products with the code below placed just above the if ( have_posts() ) : line...

$args = array(
    'post_type'      => 'product',
    'order'          => 'ASC',
    'paged'          => $paged,
    'meta_query'     => array(
        array(
            'key'           => '_sale_price',
            'value'         => 0,
            'compare'       => '>',
            'type'          => 'numeric'
        )
    )
);

query_posts( $args );

The code is placed in a copy of archive-product.php which I named archive-product_sale.php and made as a page template.

However, this only works for Simple products type and I need it to work for both Simple products and Variable products type.

Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
Giraldi
  • 16,451
  • 6
  • 33
  • 52

5 Answers5

19

@mirus' answer regarding the shortcode gave me the idea to check out how WooCommerce is querying only the on-sale items. Apparently WooCommerce has a wc_get_product_ids_on_sale() function that will return the IDs of the on-sale items. Then we can easily adjust the query using the post__in parameter to only return those specific items.

WooCommerce has a woocommerce_product_query hook in the class-wc-query.php class that allows for us to modify the query before it is run.... it is run on pre_get_posts which is the usual place for modifying the query. Using Woo's hook just means you let them handle the majority of the conditional logic about when this query modification should be applied.

add_action( 'woocommerce_product_query', 'so_20990199_product_query' );

function so_20990199_product_query( $q ){

    $product_ids_on_sale = wc_get_product_ids_on_sale();

    $q->set( 'post__in', $product_ids_on_sale );

}
helgatheviking
  • 25,596
  • 11
  • 95
  • 152
  • 1
    This should be the correct answer. It runs before the main query (better performance), the question seemed to be oriented to the main query (what this method is affecting) and it takes advantage of the work done by Woo. – Loque Nov 17 '15 at 15:40
  • 1
    Right, this is the correct answer. Meta key '_min_variation_sale_price' doesn't work for me for 'variable products' - I use '$q->set('post__in', (array) $product_ids_on_sale);' 'post__in' is much faster than 'meta_query' and use cache 'transient' and find 'vairiable products' too. – Laguna Web Design May 13 '17 at 09:50
  • This worked for me too, somehow some products didn't have "_min_variation_sale_price" meta key – Guilherme Vaz Nov 28 '20 at 20:24
  • 1
    JEEEZUS .. been looking for this answer for 5 hours. THANK YOU!! WooCommerce [changed the way they store sale prices](https://github.com/woocommerce/woocommerce/issues/10651), [get_on_sale_products](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php) – Mihai Aug 25 '22 at 15:09
11

I managed to filter out the ON SALE products with the code below placed just above the if ( have_posts() ) : line...

$args = array(
    'post_type'      => 'product',
    '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'
        )
    )
);

query_posts( $args );

The code is placed in a copy of archive-product.php which I renamed archive-product_sale.php and made as a page template.

Giraldi
  • 16,451
  • 6
  • 33
  • 52
  • 1
    Have you ever come across a problem where the check if sale price is more than 0 meta query returns products *scheduled* to go on sale, but are not currently on sale at the moment? These products should not be returned in the results, but I don't know how to write the meta query to exclude/include scheduled sale products. Cheers – patrickzdb Jul 28 '14 at 16:42
  • No, I have never come across such scenario before. Maybe you should create a new question and hopefully someone would be able to help. I'm sure alot of us would like to find out too. – Giraldi Jul 30 '14 at 00:21
  • How to make it work with built-in woocommerce filters? I have already page template displaying all products on sale, but I would like to filter by category, price, etc. (using default woocommerce)? When I change category now, ALL products are shown (not only with sale badge). – szymonm Oct 23 '14 at 19:55
  • I would assume adding the category to the argument would be a start. Try creating a new question, though, that is specific to your case. It might come in handy for many of us. – Giraldi Oct 24 '14 at 01:22
  • 2
    Dont forget to add `'paged' => $paged,` as @Giraldi has added in his edit. Otherwise the pagination will not work! – AfromanJ Oct 13 '15 at 19:32
6

@gmaggio using query_posts() will break your site. Use pre_get_posts

add_filter( 'pre_get_posts', 'catalog_filters' );
function catalog_filters( $query ) {
    if ( $query->is_main_query() && $query->post_type = 'product' ) {
        if(isset($_GET['onsale'])) {
            $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'
                )
            ); $query->set('meta_query', $meta_query);
        }
        if(isset($_GET['bestsellers'])) {
            $meta_query     = array(
            array( 
                'key'           => 'total_sales',
                'value'         => 0,
                'compare'       => '>',
                'type'          => 'numeric'
                )
            );
        }
    }

return $query;
}
Skovsgaard
  • 339
  • 2
  • 19
2

Create a new page using shortcode [sale_products per_page="12"]

List of available shortcodes and their parameters is here: http://docs.woothemes.com/document/woocommerce-shortcodes/

mirus
  • 377
  • 1
  • 2
  • 1
    He only wants to show items on sale. This will still keep the original shop page... and items that are not on sale – danyo Jan 08 '14 at 14:54
  • oh, i see now what do you mean – mirus Jan 08 '14 at 16:16
  • 3
    Thank you for the answer. However, I am aware of this **shortcode** but I needed it to be in the same **template layout** as that of the `archive-product.php` complete with the result count, sorting filter, pagination, etc. – Giraldi Jan 08 '14 at 17:23
  • @gmaggio, sorry, had a bit misunderstandings regards your question – mirus Jan 09 '14 at 15:49
1

Solution for variable and simple products:

add_action( 'save_post_product', 'update_product_set_sale_cat_var' );

function update_product_set_sale_cat_var( $post_id ) {

    $sales_ids = wc_get_product_ids_on_sale();

    foreach ( $sales_ids as $sale_id ) :
        if ($sale_id == $post_id) :
            wp_set_object_terms($post_id, 'sale', 'product_cat', true );
        else :
            if ( has_term( 'sale', 'product_cat', $post_id ) ) {
                wp_remove_object_terms( $post_id, 'sale', 'product_cat' );
            }
        endif;
    endforeach; 

}
Butiri Dan
  • 1,759
  • 5
  • 12
  • 18
  • While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – xiawi Oct 04 '19 at 09:54
  • For some reason I can't get this to work. The category is created if it doesn't exist, but the product is not assigned to it. The category remains empty without any associated products, even if I try to save again. – Skovsgaard Aug 30 '20 at 14:14