Giraldi
Giraldi

Reputation: 17331

WooCommerce: Display ONLY on-sale products in Shop

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.

Upvotes: 20

Views: 61792

Answers (7)

Valeh Salimov
Valeh Salimov

Reputation: 31

After reading all the options, it seems I came to the simplest solution for myself and it seems to work

$args => array(
    'type' => array( 'simple', 'variation' ) ,
    'include' =>  wc_get_product_ids_on_sale(), // only products from this array
)

$products = wc_get_products($args);

'type' => 'variation' If you need to display each variation as a separate product card

'type' => 'variable' If you need a parent variation object with a button, select options instead of adding to cart

For some reason I didn’t find anything about variation in the documentation wc_get_products()

I'm not a Woocommerce specialist, I'm only doing my second project on it. And at almost every stage I encounter difficulties. And in general, I'm more of a frontend developer. I literally just wrote the solution, at first glance it works as expected and I hope it will continue to do so.

Sorry if this code is incorrect. And sorry for my English, this is Google translator). I hope this helps at least someone

Upvotes: 2

Mika G.
Mika G.

Reputation: 132

@butiri-dan, your solution when we save/update a product to change the category sale is interresting but unfortunately "wrong" because the product is not yet in (or out) in sale when 'save_post_product' is triggered so wc_get_product_ids_on_sale() do not containt the current product id yet if we add a sale price at the first time. Finaly, your function works only at the second time we save the product and only if the product on sale value do not change.

My participation can be help (maybe). In my case this code works like a charm :

add_action('woocommerce_update_product', 'update_product_set_sale_cat_var');
add_action('woocommerce_new_product', 'update_product_set_sale_cat_var');

function update_product_set_sale_cat_var( $post_id ) {
    $saleCategorySlug = 'promos';
    $product = wc_get_product($post_id);
    if ($product->get_sale_price() === $product->get_price() && !has_term( $saleCategorySlug, 'product_cat', $post_id ) ) {
        wp_set_object_terms($post_id, $saleCategorySlug, 'product_cat', true );
    }
    else if($product->get_sale_price() !== $product->get_price() && has_term( $saleCategorySlug, 'product_cat', $post_id )){
        wp_remove_object_terms( $post_id, $saleCategorySlug, 'product_cat' );
    }
}

Upvotes: 0

@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;
}

Upvotes: 6

simple-user
simple-user

Reputation: 9

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; 

}

Upvotes: 0

helgatheviking
helgatheviking

Reputation: 26329

@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 );

}

Upvotes: 21

Giraldi
Giraldi

Reputation: 17331

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.

Upvotes: 11

mirus
mirus

Reputation: 377

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/

Upvotes: 2

Related Questions