Search code examples
phpwordpresswoocommerceproducthook-woocommerce

Add sale badge to product thumbnail on single product page and shop archive pages in WooCommerce


I use the following code to add a small badge topleft of every product with -%off.

add_filter( 'woocommerce_single_product_image_thumbnail_html', 'filter_woocommerce_single_product_image_thumbnail_html', 10, 2 ); 
function filter_woocommerce_single_product_image_thumbnail_html( $thumbnail, $thumbnail_id ) {
 
    global $post, $woocommerce;
    $product = wc_get_product( $post->ID );
    $is_on_sale = $product->is_on_sale();
 
    if ( $is_on_sale ) {
 
        $rrp_price          = $product->get_regular_price();
        $sale_price         = $product->get_price();
        $discount_amount    = $rrp_price - $sale_price;
        $discount_percent   = '-' . round( ( $discount_amount / $rrp_price ) * 100, 0 ) . '%';
        $thumbnail          = '<span class="wpd-sale-thumbnail"><span class="wpd-sale-badge">' . $discount_percent . '</span>' . $thumbnail . '</span>';
 
    }
 
    return $thumbnail;
 
}

On my product page and it works. However when i try to do it on category page to all product listings with the following code:

add_filter( 'post_thumbnail_html', 'filter_woocommerce_archive_product_image_thumbnail_html', 30, 5  );
function filter_woocommerce_archive_product_image_thumbnail_html( $html, $post_id, $post_thumbnail_id, $size, $attr ) {
 
    $product = wc_get_product( $post_id );
 
    if ( ! is_a( $product, 'WC_Product' ) ) {
 
        return $html;
 
    }
 
    $is_on_sale = $product->is_on_sale();
 
    if ( $is_on_sale && is_product_category() ) {
 
        $rrp_price          = $product->get_regular_price();
        $sale_price         = $product->get_price();
        $discount_amount    = $rrp_price - $sale_price;
        $discount_percent   = '-' . round( ( $discount_amount / $rrp_price ) * 100, 0 ) . '%';
        $html               = '<span class="wpd-sale-thumbnail"><span class="wpd-sale-badge">' . $discount_percent . '</span>' . $html . '</span>';
 
    }
 
    return $html;
 
}

It does not work, there doesn't seem to be any adjustment. Can I get some guidance into it?


Solution

  • To add the badge to the product images on the archive pages, either you use

    /**
     * Get the product thumbnail for the loop.
     */
    function woocommerce_template_loop_product_thumbnail() {
        $w_g_p_t = woocommerce_get_product_thumbnail();
        
        global $product;
        
        // Is a WC product
        if ( is_a( $product, 'WC_Product' ) ) {
            
            // On sale
            $is_on_sale = $product->is_on_sale();
            
            // True
            if ( $is_on_sale ) {
                $rrp_price        = (int) $product->get_regular_price();
                $sale_price       = (int) $product->get_price();
                
                // Greater than
                if ( $rrp_price > $sale_price ) {
                    $discount_amount  = $rrp_price - $sale_price;
                    $discount_percent = '-' . round( ( $discount_amount / $rrp_price ) * 100, 0 ) . '%';
                    $w_g_p_t          = '<span class="wpd-sale-thumbnail"><span class="wpd-sale-badge">' . $discount_percent . '</span>' . $w_g_p_t . '</span>';
                }
            }
        }
        
        echo $w_g_p_t;
    }
    

    OR

    function action_woocommerce_before_shop_loop_item_title() {
        // Removes a function from a specified action hook.
        remove_action( 'woocommerce_before_shop_loop_item_title', 'woocommerce_template_loop_product_thumbnail', 10 );
        
        global $product;
        
        // Is a WC product
        if ( is_a( $product, 'WC_Product' ) ) {
            $size = 'woocommerce_thumbnail';
    
            $image_size = apply_filters( 'single_product_archive_thumbnail_size', $size );
            
            $get_image = $product->get_image( $image_size );
            
            // On sale
            $is_on_sale = $product->is_on_sale();
         
            // True
            if ( $is_on_sale ) {
                $rrp_price        = (int) $product->get_regular_price();
                $sale_price       = (int) $product->get_price();
                
                // Greater than
                if ( $rrp_price > $sale_price ) {
                    $discount_amount  = $rrp_price - $sale_price;
                    $discount_percent = '-' . round( ( $discount_amount / $rrp_price ) * 100, 0 ) . '%';
                    $get_image        = '<span class="wpd-sale-thumbnail"><span class="wpd-sale-badge">' . $discount_percent . '</span>' . $get_image . '</span>';
                }
            }
        }
    
        echo $product ? $get_image : '';
    }
    add_action( 'woocommerce_before_shop_loop_item_title', 'action_woocommerce_before_shop_loop_item_title', 9 );
    

    I have also rewritten your existing code (for the single product page)

    function filter_woocommerce_single_product_image_thumbnail_html( $html, $post_thumbnail_id ) {
        // Get the global product object
        global $product;
        
        // Is a WC product
        if ( is_a( $product, 'WC_Product' ) ) {
            
            // On sale
            $is_on_sale = $product->is_on_sale();
            
            // True
            if ( $is_on_sale ) {
                $rrp_price        = (int) $product->get_regular_price();
                $sale_price       = (int) $product->get_price();
                
                // Greater than
                if ( $rrp_price > $sale_price ) {
                    $discount_amount  = $rrp_price - $sale_price;
                    $discount_percent = '-' . round( ( $discount_amount / $rrp_price ) * 100, 0 ) . '%';
                    $html             = '<span class="wpd-sale-thumbnail"><span class="wpd-sale-badge">' . $discount_percent . '</span>' . $html . '</span>';
                }
            }
        }
     
        return $html;
    }
    add_filter( 'woocommerce_single_product_image_thumbnail_html', 'filter_woocommerce_single_product_image_thumbnail_html', 10, 2 );