Search code examples
woocommercecoupon

Adding coupon form to side cart on shop page


First, here's the website: https://aypac.de/woocommerce/shop/ (please don't mind the CSS fuck-ups or the absence of a redirection button to the cart/checkout).

So I've copied the apply coupon code from the cart page to my widget/shortcode which displays the cart as a sidebar, and it works but I'm having problems with unwanted redirections.

  1. Apply coupon reloads the whole page while it would be nicer if it would only reload the side cart (or the whole widget area).

  2. More problematic is that the remove coupon button sends me to the cart page instead of reloading the same page.

I think it's possible to do since it works with deleting items (it's somehow linked to the class used for the button). I tried tweaking stuff in the coupon code itself thanks to a filter, but it didn't do any good (I barely know any php tbh, I can basically read it and copy-paste stuff around, but not write it per say).

Here's the code for the sidecart (a shortcode in a widget area), the last ~20 lines are for the coupons:

<?php
// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
    die;
}
if ( is_admin() ) return false;


// Required to calculate deposit
WC()->cart->calculate_fees();

//$dec_p = !empty($locale['decimal_point']) ? $locale['decimal_point'] : ',';

// Calculate the total cart weight (incl. jars)

$prod_weight = WC()->cart->get_cart_contents_weight()/1000;
$jar_weight = 0;
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
    //var_dump($cart_item);
    //$_product   = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
    if (!empty($cart_item['variation']) && !empty($cart_item['variation']['attribute_pa_volume'])) {
           $v = strtolower($cart_item['variation']['attribute_pa_volume']);
           if (in_array($v, jar_to_weight)) {
                $jar_weight += jar_to_weight[$v];
           }
    }
}
$tot_weight = $jar_weight + $prod_weight;
$weightclass = ($tot_weight < 6) ? 'low_weight' : (($tot_weight < 12) ? 'med_weight' : 'hig_weight');

?>
<div class="LCKL_shop-sidecart">
    <?php if( WC()->cart->is_empty() ) { ?>
        <div class="LCKL_shop-empty">
            <h3>
                Your basket is empty
            </h3>
        </div>
    <?php
    } else {
        do_action( 'LCKL_shop-before-items' );
        ?>
        
        <ul class="LCKL_shop-items">
        
            <?php
            foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
                $_product   = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
                $product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
                $item_subtotal = $cart_item['data']->get_price() * $cart_item['quantity']; //WC()->cart->get_product_price( $_product )
                
                if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_cart_item_visible', true, $cart_item, $cart_item_key ) ) {
                    $product_permalink = apply_filters( 'woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key );
                }
                
                // Get parent product name
                $parent_product = wc_get_product($cart_item['product_id']);
                $product_name = $parent_product->get_name();
                
                $volume_html = $_product->get_attribute('pa_volume');
                $volume_html = esc_html(apply_filters( 'woocommerce_variation_option_name', $volume_html));
                $vol_wei = $volume_html;
                if ($volume_html && $_product->get_weight() > 0) {
                    $vol_wei .= '/';
                }
                if ($_product->get_weight() > 0) {
                    $vol_wei .= wc_format_weight($_product->get_weight());
                }
                ?>

<!--Start of item row-->
            
    <li class="LCKL_shop-item">
        <div class="LCKL_shop-title">
            <span>
            <?php
                if ( ! $product_permalink ) {
                    echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', $product_name, $cart_item, $cart_item_key ) . '&nbsp;' );
                } else {
                    echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', sprintf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $product_name ), $cart_item, $cart_item_key ) );
                }
                do_action( 'woocommerce_after_cart_item_name', $cart_item, $cart_item_key ); ?>
                
            </span>
            <?php
                echo apply_filters( 'woocommerce_cart_item_remove_link', sprintf(
                    '<a href="%s" class="remove_from_cart_button" aria-label="%s" data-product_id="%s" data-cart_item_key="%s" data-product_sku="%s"><i class="icon-bin"></i></a>',
                    esc_url( wc_get_cart_remove_url( $cart_item_key ) ),
                    __( 'Remove this item', 'woocommerce' ),
                    esc_attr( $product_id ),
                    esc_attr( $cart_item_key ),
                    esc_attr( $_product->get_sku() )
                ), $cart_item_key );
            ?>
        </div>
        <div>
                <?= $vol_wei ?>
            </div>
        <div class="LCKL_shop-pricing">         
            <span>
                <?= esc_html( $cart_item['quantity'] ); ?>
                &times;
                <?= apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key ); ?>  
                 <!--+ <i class="icon-deposit1"></i>-->
             </span>
            
            <span class="LCKL_shop-price">
                <?php echo apply_filters( 'woocommerce_cart_item_price', wc_price($item_subtotal) ). get_option( 'woocommerce_price_display_suffix' ); ?>               
            </span>
                        <!--
                        <div class="wc-block-components-quantity-selector">
                            <input class="wc-block-components-quantity-selector__input" type="number" step="1" min="1" max="99" aria-label="Quantity of Oat flakes in your cart." value="<?= esc_html( $cart_item['quantity'] ); ?>">
                            <button aria-label="Reduce quantity" class="wc-block-components-quantity-selector__button wc-block-components-quantity-selector__button--minus">-</button>
                            <button aria-label="Increase quantity" class="wc-block-components-quantity-selector__button wc-block-components-quantity-selector__button--plus">+</button>
                        </div> -->
                    </div>
                </li>
            <?php }  ?>

<!--End of item row-->
            
        </ul>
        <div class="LCKL_shop-subtotal">
            <table> 
                <tr>
                    <td class="LCKL_tooltip"><span>Cart weight:</span><span class="LCKL_tooltiptext">Includes the weight of the jars; make sure you can carry everything!</span></td>
                    
                    <td class="<?= $weightclass ?>">
                        <?= number_format($tot_weight, 2) . '&nbsp;kg' ?>
                    </td>
                </tr>
                <tr>
                    <td>Subtotal:</td>
                    <td><?php wc_cart_totals_subtotal_html(); ?></td>
                </tr>
                <?php foreach ( WC()->cart->get_fees() as $fee ) : ?>
                <tr>
                    <td class="LCKL_tooltip"><span><?= esc_html( $fee->name ) ?></span>:
                    <span class="LCKL_tooltiptext">Amount that you will get back when you return all the jars and/or bags</span></td>                   
                    <td><?php wc_cart_totals_fee_html( $fee ); ?></td>
                </tr>   
                <?php endforeach; ?>
                
                <?php foreach ( WC()->cart->get_coupons() as $code => $coupon ) : ?>
                <tr class="coupon-<?php echo esc_attr( sanitize_title( $code ) ); ?>">
                    <td><?php wc_cart_totals_coupon_label( $coupon ); ?></td>
                    <td><?php wc_cart_totals_coupon_html( $coupon ); ?></td>
                </tr>
                <?php endforeach; ?>    
            </table>
            
            <form method="post">
            <input type="text" name="coupon_code" class="input-text" placeholder="<?php esc_attr_e( 'Coupon code', 'woocommerce' ); ?>" id="coupon_code" value="" />
            <button type="submit" class="button" name="apply_coupon" value="<?php esc_attr_e( 'Apply', 'woocommerce' ); ?>"><?php esc_html_e( 'Apply', 'woocommerce' ); ?></button>
            
            <div><?php _e( 'Total:', 'sidebar-cart' ); ?></div>
            <div data-title="<?php esc_attr_e( 'Total', 'woocommerce' ); ?>"></div>
            <div><?php wc_cart_totals_order_total_html();?></div>
        </div>
        
    <?php } ?>
</div>

I also have this filter to replace the text for the remove button in my functions.php:

function filter_woocommerce_cart_totals_coupon_html( $coupon_html, $coupon, $discount_amount_html ) {
    // Change text
    $coupon_html = str_replace( '[Remove]', '<i class="icon-bin"></i>', $coupon_html );

    return $coupon_html;
}
add_filter( 'woocommerce_cart_totals_coupon_html', 'filter_woocommerce_cart_totals_coupon_html', 10, 3 );

Solution

  • Well I ended up completely scrapping my plug-in and modifying the cart template instead (in my child theme). That way I can use the [woocommerce_cart] shortcode instead. Still have a bit of a problem with displaying the notices in the cart and not on the main page (although I removed all the notices actions).

    remove_action( 'woocommerce_before_shop_loop', 'wc_print_notices', 10 ); /*Archive Product*/
    remove_action( 'woocommerce_before_single_product', 'wc_print_notices', 10 ); /*Single Product*/
    

    Here's the code in the cart template:

    <?php
    /**
     * Cart Page
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/cart/cart.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you
     * (the theme developer) will need to copy the new files to your theme to
     * maintain compatibility. We thy to do this as little as possible, but it does
     * happen. When this occurs the version of the template file will be bumped and
     * the readme will list any important changes.
     *
     * @see     https://docs.woocommerce.com/document/template-sthucture/
     * @package WooCommerce\Templates
     * @version 3.8.0
     */
    
    defined( 'ABSPATH' ) || exit;
    /**
     * Define jars weight
     */
    
    do_action( 'woocommerce_before_cart' ); ?>
    
    
    <form class="woocommerce-cart-form LCKL_shop_sidecart" action="<?php echo esc_url( wc_get_cart_url() ); ?>" method="post">
        <?php
        do_action( 'woocommerce_before_cart_table' );
        ?>
        <ul class="LCKL_shop-items shop_table shop_table_responsive cart woocommerce-cart-form__contents">      
            
            <?php do_action( 'woocommerce_before_cart_contents' ); ?>
    
            <?php
            foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
                $_product   = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
                $product_id = apply_filters( 'woocommerce_cart_item_product_id', $cart_item['product_id'], $cart_item, $cart_item_key );
                $item_subtotal = $cart_item['data']->get_price() * $cart_item['quantity']; //WC()->cart->get_product_price( $_product )
    
                // Get parent product name
                $parent_product = wc_get_product($cart_item['product_id']);
                $product_name = $parent_product->get_name();
                
                $volume_html = $_product->get_attribute('pa_volume');
                $volume_html = esc_html(apply_filters( 'woocommerce_variation_option_name', $volume_html));
                $vol_wei = $volume_html;
                if ($volume_html && $_product->get_weight() > 0) {
                    $vol_wei .= '/';
                }
                if ($_product->get_weight() > 0) {
                    $vol_wei .= wc_format_weight($_product->get_weight());
                }
                
                
                if ( $_product && $_product->exists() && $cart_item['quantity'] > 0 && apply_filters( 'woocommerce_cart_item_visible', true, $cart_item, $cart_item_key ) ) {
                    $product_permalink = apply_filters( 'woocommerce_cart_item_permalink', $_product->is_visible() ? $_product->get_permalink( $cart_item ) : '', $cart_item, $cart_item_key );
                    ?>
                    
                    <!--Start of item row-->            
                    <li class="LCKL_shop-item woocommerce-cart-form__cart-item <?php echo esc_attr( apply_filters( 'woocommerce_cart_item_class', 'cart_item', $cart_item, $cart_item_key ) ); ?>">
                    
                    <!--Aligns product name and remove button -->
                        <div class="LCKL_shop-title product-name" data-title="<?php esc_attr_e( 'Product', 'woocommerce' ); ?>">
                            <span>
                                <!--Product name -->
                                <?php
                                if ( ! $product_permalink ) {
                                    echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', $_product->get_name(), $cart_item, $cart_item_key ) . '&nbsp;' );
                                } else {
                                    echo wp_kses_post( apply_filters( 'woocommerce_cart_item_name', sprintf( '<a href="%s">%s</a>', esc_url( $product_permalink ), $_product->get_name() ), $cart_item, $cart_item_key ) );
                                }
    
                                do_action( 'woocommerce_after_cart_item_name', $cart_item, $cart_item_key );
    
                                // Backorder notification.
                                if ( $_product->backorders_require_notification() && $_product->is_on_backorder( $cart_item['quantity'] ) ) {
                                    echo wp_kses_post( apply_filters( 'woocommerce_cart_item_backorder_notification', '<p class="backorder_notification">' . esc_html__( 'Available on backorder', 'woocommerce' ) . '</p>', $product_id ) );
                                }
                                ?>
                            </span>
                            
                            <span class="product-remove">
                                <!--Remove button, class "remove" is mandatory!! -->
                                <?php
                                    echo apply_filters(
                                        'woocommerce_cart_item_remove_link',
                                        sprintf(
                                            '<a href="%s" class="remove" aria-label="%s" data-product_id="%s" data-product_sku="%s"><i class="icon-bin"></i></a>',
                                            esc_url( wc_get_cart_remove_url( $cart_item_key ) ),
                                            esc_html__( 'Remove this item', 'woocommerce' ),
                                            esc_attr( $product_id ),
                                            esc_attr( $_product->get_sku() )
                                        ),
                                        $cart_item_key
                                    );
                                ?>
                            </span>
                        </div>
                        
                        
                        <div>
                            <!--Volume and weight -->
                            <?= $vol_wei ?>
                        </div>
                                                
                            <!--Product quantity × price -->
                                <div class="product-quantity" data-title="<?php esc_attr_e( 'Quantity', 'woocommerce' ); ?>">
                                    <!--div class="quantity" --><?php
                                    if ( $_product->is_sold_individually() ) {
                                        $product_quantity = sprintf( '1 <input type="hidden" name="cart[%s][qty]"  dir="rtl" value="1" />', $cart_item_key );
                                    } else {
                                        $product_quantity = woocommerce_quantity_input(
                                            array(
                                                'input_name'   => "cart[{$cart_item_key}][qty]",
                                                'input_value'  => $cart_item['quantity'],
                                                'max_value'    => $_product->get_max_purchase_quantity(),
                                                'min_value'    => '0',
                                                'product_name' => $_product->get_name(),
                                                'input_dir'    => "rtl",
                                            ),
                                            $_product,
                                            false
                                        );
                                    }
    
                                    echo apply_filters( 'woocommerce_cart_item_quantity', $product_quantity, $cart_item_key, $cart_item ); // PHPCS: XSS ok.
                                    ?>  
                                    <!--/div class="quantity" -->                                   
                                    &times;&nbsp;       
                                    <!--span class="woocommerce-Price-amount amount" -->
                                    <?php
                                        echo apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key ); // PHPCS: XSS ok.
                                    ?>
                                    <!--/span class="woocommerce-Price-amount amount" -->
                                    
                                                                <!--Subtotal per item-->
                                    <span class="LCKL_shop-price product-subtotal" data-title="<?php esc_attr_e( 'Subtotal', 'woocommerce' ); ?>">
                                        <?php
                                            echo apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key ); // PHPCS: XSS ok.
                                        ?>
                                    </span>
                                </div>                  
    
                    </li>
                    <?php
                }
            }
            ?>
    
            <?php do_action( 'woocommerce_cart_contents' ); ?>
    
        </ul>
                
        <div class="actions">
            <?php if ( wc_coupons_enabled() ) { ?>
                <div class="coupon">
                    <input type="text" name="coupon_code" class="input-text" id="coupon_code" value="" placeholder="<?php esc_attr_e( 'Coupon code', 'woocommerce' ); ?>" /> <button type="submit" class="button" name="apply_coupon" value="<?php esc_attr_e( 'Apply', 'woocommerce' ); ?>"><?php esc_attr_e( 'Apply', 'woocommerce' ); ?></button>
                    <?php do_action( 'woocommerce_cart_coupon' ); ?>
                </div>
            <?php } ?>  
            <!--Coupon and update cart -->
            <!-- <button type="submit" class="recart" name="action" value="refresh"><i class="icon-recart" ></i></button> -->
            <button type="submit" class="button" name="update_cart" value="<?php esc_attr_e( 'Update cart', 'woocommerce' ); ?>"><?php esc_html_e( 'Update cart', 'woocommerce' ); ?></button>
            
            <?php do_action( 'woocommerce_cart_actions' ); ?>
    
            <?php wp_nonce_field( 'woocommerce-cart', 'woocommerce-cart-nonce' ); ?>
            
        </div>
    
        <?php do_action( 'woocommerce_after_cart_contents' ); ?>
        <?php do_action( 'woocommerce_after_cart_table' ); ?>
    
        <?php
        do_action( 'woocommerce_before_cart_collaterals' );
        /**
         * Cart collaterals hook.
         *
         * @hooked woocommerce_cross_sell_display
         * @hooked woocommerce_cart_totals - 10
         */
        do_action( 'woocommerce_cart_collaterals' );
        do_action( 'woocommerce_after_cart_collaterals' );
        ?>
    
    
        <?php do_action( 'woocommerce_after_cart' ); ?>
    
    </form>
    

    The code in the cart-totals:

    <?php
    /**
     * Cart totals
     *
     * This template can be overridden by copying it to yourtheme/woocommerce/cart/cart-totals.php.
     *
     * HOWEVER, on occasion WooCommerce will need to update template files and you
     * (the theme developer) will need to copy the new files to your theme to
     * maintain compatibility. We try to do this as little as possible, but it does
     * happen. When this occurs the version of the template file will be bumped and
     * the readme will list any important changes.
     *
     * @see     https://docs.woocommerce.com/document/template-structure/
     * @package WooCommerce\Templates
     * @version 2.3.6
     */
    
    WC()->cart->calculate_fees();
    
    $prod_weight = WC()->cart->get_cart_contents_weight()/1000;
    $jar_weight = 0;
    foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
        //var_dump($cart_item);
        //$_product   = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
        if (!empty($cart_item['variation']) && !empty($cart_item['variation']['attribute_pa_volume'])) {
            $v = trim(strtolower($cart_item['variation']['attribute_pa_volume']));
            if (!empty(jar_to_weight[$v])) {
                $jar_weight += jar_to_weight[$v] * $cart_item['quantity'];
            } else {
                print("Warning: weight for container $v not found.");
            }
        } else {
            var_dump($cart_item);
        }
    }
    $tot_weight = $jar_weight + $prod_weight;
    $weightclass = ($tot_weight < 6) ? 'low_weight' : (($tot_weight < 12) ? 'med_weight' : 'hig_weight');
    
    
    ?>
    <div class="cart_totals LCKL_shop-subtotal">
    
        <?php do_action( 'woocommerce_before_cart_totals' ); ?>
    
        <table cellspacing="0" class="shop_table shop_table_responsive">
        
                        
            <tr>
                <td class="LCKL_tooltip"><span>Cart weight:</span><span class="LCKL_tooltiptext">Includes the weight of the jars; make sure you can carry everything!</span></td>
                
                <td class="<?= $weightclass ?>">
                    <?= number_format($tot_weight, 2) . '&nbsp;kg' ?>
                </td>
            </tr>
            <tr>
                <td>Subtotal:</td>
                <td><?php wc_cart_totals_subtotal_html(); ?></td>
            </tr>
            <?php 
            // Add rows for the additional fees (such as deposit)
            foreach ( WC()->cart->get_fees() as $fee ) : ?>
                <tr>
                    <td class="LCKL_tooltip"><span><?= esc_html( $fee->name ) ?></span>:
                    <span class="LCKL_tooltiptext">Amount that you will get back when you return all the jars and/or bags</span></td>                   
                    <td><?php wc_cart_totals_fee_html( $fee ); ?></td>
                </tr>   
            <?php endforeach; ?>
                    
                
            <?php foreach ( WC()->cart->get_coupons() as $code => $coupon ) : ?>
                <tr class="cart-discount coupon-<?php echo esc_attr( sanitize_title( $code ) ); ?>">
                    <td><?php wc_cart_totals_coupon_label( $coupon ); ?></td>
                    <td data-title="<?php echo esc_attr( wc_cart_totals_coupon_label( $coupon, false ) ); ?>"><?php wc_cart_totals_coupon_html( $coupon ); ?></td>
                </tr>
            <?php endforeach; ?>
    
            
    
            <?php do_action( 'woocommerce_cart_totals_before_order_total' ); ?>
    
            <tr class="order-total">
                <th><?php esc_html_e( 'Total', 'woocommerce' ); ?></th>
                <td data-title="<?php esc_attr_e( 'Total', 'woocommerce' ); ?>"><?php wc_cart_totals_order_total_html(); ?></td>
            </tr>
    
            <?php do_action( 'woocommerce_cart_totals_after_order_total' ); ?>
    
        </table>
    
        <div class="wc-proceed-to-checkout">
            <?php do_action( 'woocommerce_proceed_to_checkout' ); ?>
        </div>
    
        <?php do_action( 'woocommerce_after_cart_totals' ); ?>
    
    </div>