Woocommerce wrong fragments passed back from woocommerce_add_to_cart_fragments

Viewed 1132

I'm creating a custom WooCommerce cart and updating the quantity of a cart item works correctly. The only problem is that it doesn't automatically refresh, only works after page load.

My current code, which uses the woocommerce_add_to_cart_fragments hook and uses the passed in $fragments object. According to what I've seen around this is the correct way to go, but this doesn't work and I've debugged and to me the problem seems to be that the $fragments I get in the hook doesn't have my original html but instead a html that looks like this:

<div class="widget_shopping_cart_content">

    <ul class="woocommerce-mini-cart cart_list product_list_widget ">
        <li class="woocommerce-mini-cart-item mini_cart_item">
            <a href="http://velosity-dev-wordpress.local/cart/?remove_item=92cc227532d17e56e07902b254dfad10&#038;_wpnonce=8ead5f2c51"
                class="remove remove_from_cart_button" aria-label="Remove this item" data-product_id="92"
                data-cart_item_key="92cc227532d17e56e07902b254dfad10" data-product_sku="">&times;</a> <a
                href="http://velosity-dev-wordpress.local/product/subber-one/">
                <img width="300" height="300"
                    src="http://velosity-dev-wordpress.local/wp-content/uploads/2021/09/mathilde-langevin-WxHJEMUnlIM-unsplash-300x300.jpg"
                    class="attachment-woocommerce_thumbnail size-woocommerce_thumbnail" alt="" loading="lazy"
                    srcset="http://velosity-dev-wordpress.local/wp-content/uploads/2021/09/mathilde-langevin-WxHJEMUnlIM-unsplash-300x300.jpg 300w, http://velosity-dev-wordpress.local/wp-content/uploads/2021/09/mathilde-langevin-WxHJEMUnlIM-unsplash-150x150.jpg 150w, http://velosity-dev-wordpress.local/wp-content/uploads/2021/09/mathilde-langevin-WxHJEMUnlIM-unsplash-100x100.jpg 100w"
                    sizes="(max-width: 300px) 100vw, 300px" />Subber One </a>
            <span class="quantity">2 &times; <span class="woocommerce-Price-amount amount"><bdi><span
                            class="woocommerce-Price-currencySymbol">&#36;</span>349.00</bdi></span></span>
        </li>
    </ul>

    <p class="woocommerce-mini-cart__total total">
        <strong>Subtotal:</strong> <span class="woocommerce-Price-amount amount"><bdi><span
                    class="woocommerce-Price-currencySymbol">&#36;</span>698.00</bdi></span>
    </p>


    <p class="woocommerce-mini-cart__buttons buttons"><a href="http://velosity-dev-wordpress.local/cart/"
            class="button wc-forward">View cart</a><a href="http://velosity-dev-wordpress.local/checkout/"
            class="button checkout wc-forward">Checkout</a></p>

I don't really know how to pass in the correct 'active' html in to this hook, so any pointer would be greatly appreciated! Big thanks in advance!

My custom cart html/php (inside a file called header-shopping-cart.php)

<div class="cart-items" id="cart-items">
    <?php foreach(WC()->cart->get_cart() as $cart_item_key => $cart_item) : ?>
    <div class="cart-item">
        <?php echo $cart_item['data']->get_image(); ?>
        <div>
            <h3><?php echo $cart_item['data']->get_title() ?></h3>
            <div class="quantity">
                <input type="number" id="quantity_614459a588590" class="input-text qty text" step="1" min="0" max=""
                    name="cart[<?php echo $cart_item_key ?>][qty]" value="<?php echo $cart_item['quantity']  ?>"
                    title="Qty" size="4" placeholder="" inputmode="numeric">
            </div>
            <div class="price"><?php echo $cart_item['data']->get_price_html() ?></div>
        </div>
    </div>
    <div class="divider"></div>
    <? endforeach; ?>
</div>

My JS function (inside custom.js)

jQuery(function ($) {
  $(document).on("change", "input.qty", function () {
    var $thisbutton = $(this);
    var cart_item_key = $(this)
      .attr("name")
      .replace(/cart\[([\w]+)\]\[qty\]/g, "$1");
    var item_quantity = $(this).val();
    var currentVal = parseFloat(item_quantity);

    $.ajax({
      type: "POST",
      url: cart_qty_ajax.ajax_url,
      data: {
        action: "my_cart_qty",
        cart_item_key: cart_item_key,
        quantity: currentVal,
      },
      success: function (response) {
        jQuery(document.body).trigger("added_to_cart", [response.fragments, response.cart_hash, $thisbutton]);
      },
    });
  });
});

My php-functions (inside functions.php)

<?php    
function enqueue_cart_qty_ajax() {
        wp_register_script( 'my_cart_qty-ajax-js', get_template_directory_uri() . '/js/custom.js', array( 'jquery' ), '', true );
        wp_localize_script( 'my_cart_qty-ajax-js', 'cart_qty_ajax', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
        wp_enqueue_script( 'my_cart_qty-ajax-js' );
    }
    add_action('wp_enqueue_scripts', 'enqueue_cart_qty_ajax');
    
    function ajax_my_cart_qty() {
        // Set item key as the hash found in input.qty's name
        $cart_item_key = $_POST['cart_item_key'];
        $quantity = $_POST['quantity'];
    
        // Get the array of values owned by the product we're updating
        $threeball_product_values = WC()->cart->get_cart_item( $cart_item_key );
        // Get the quantity of the item in the cart
        $threeball_product_quantity = apply_filters( 'woocommerce_stock_amount_cart_item', apply_filters( 'woocommerce_stock_amount', preg_replace( "/[^0-9\.]/", '', filter_var($_POST['quantity'], FILTER_SANITIZE_NUMBER_INT)) ), $cart_item_key );
        // Update cart validation
        $passed_validation  = apply_filters( 'woocommerce_update_cart_validation', true, $cart_item_key, $threeball_product_values, $threeball_product_quantity );
    
        // Update the quantity of the item in the cart
        if ( $passed_validation ) {
            WC()->cart->set_quantity( $cart_item_key, $threeball_product_quantity, true );
        }
    
        // Refresh the page
        echo do_shortcode( '[woocommerce_cart]' );
    
        die();
    }
    add_action('wp_ajax_my_cart_qty', 'ajax_my_cart_qty');
    add_action('wp_ajax_nopriv_my_cart_qty', 'ajax_my_cart_qty');
    
    //Responsive cart
    add_filter( 'woocommerce_add_to_cart_fragments', 'ajaxify_components', 10, 1 );
    function ajaxify_components( $fragments ) {
        ob_start();
    ?>
    <div class="cart-items" id="cart-items">
        <?php foreach(WC()->cart->get_cart() as $cart_item_key => $cart_item) : ?>
        <div class="cart-item">
            <?php echo $cart_item['data']->get_image(); ?>
            <div>
                <h3><?php echo $cart_item['data']->get_title() ?></h3>
                <div class="quantity">
                    <input type="number" id="quantity_614459a588590" class="input-text qty text" step="1" min="0" max=""
                        name="cart[<?php echo $cart_item_key ?>][qty]" value="<?php echo $cart_item['quantity']  ?>"
                        title="Qty" size="4" placeholder="" inputmode="numeric">
                </div>
                <div class="price"><?php echo $cart_item['data']->get_price_html() ?></div>
            </div>
        </div>
        <div class="divider"></div>
        <? endforeach; ?>
    </div>
    <?php
        $fragments['#cart-items'] = ob_get_clean();
    
        return $fragments;
    }

Hope someone can give me a pointer in the right direction. Thanks!

1 Answers

I just tested your code, and it seems to work:

  • On click, the ajax is made and my cart content is updated
  • the echo do_shortcode( '[woocommerce_cart]' ); is not working atm, as it thinks the cart is empty (and returns "empty cart" HTML message). But I don't see why you need the complete woocommerce cart HTML as a response here. Also, you use the response in tour JS as an object, but returning the do_shortcode will return a raw HTML string only.
  • the AJAX ends, and jQuery(document.body).trigger("added_to_cart") is called, which trigger the Woocommere cart fragment to refresh. As the AJAX call shortcode response is "wrong", the response object is not correct, but we can still trigger the event. Just make sure your custom listener handle the "no parameter" case
  • the cart fragment is refreshed, with your custom HTML

Take care about your <? endforeach; ?> that misses the <?php.

enter image description here

EDIT: After a quick test, it seems the woocommerce_cart shortcode output uses the woocommerce templates. So you could try to edit the Woocommerce Cart template files in your child theme to change the HTML output. (if you really need the cart HTML as the response to your custom ajax call).

Related