Woocommerce update shipping methods in checkout via ajax

19,950

Solution 1

So, for those of you who may need this, it required a little finagling in woocommerce, because of the way they handle output of cart/checkout values for shipping and payment options, but in the end, I could make it happen using woocommerce's hooks, and very little plugin modification, so, a pretty clean solution.

Basically what I did was use woocommerce sessions, two hooks, and a javascript trigger that refreshes the checkout order review.

Through a combination of the woocommerce_checkout_update_order_review hook that happens in the WC_AJAX::update_order_review and use of the aformentioned $( 'body' ).trigger( 'update_checkout' ); call, I could hook in to the action that updates the checkout review block. So far so good.

What I did in this function was check if the field had a value, and if it did, save it in the WC()->session. THEN, since WC_AJAX::update_order_review calls woocommerce_order_review() to grab all the updated cart and shipping methods, I looked into the template associated with that function and found another hook, woocommerce_review_order_before_shipping, which allows me to modify the shipping methods before the cart template loops through and builds the shipping options in wc_cart_totals_shipping_html().

Now, I know, you're thinking, wait, I need checkout shipping methods, not cart ones, but in reality, they seem to be one and the same.

Hopefully my wasted hours will help someone else with a similar problem.

Cheers.

Solution 2

If anybody wants to update the cart:

foreach (WC()->cart->get_cart() as $key => $value) {
    WC()->cart->set_quantity($key, $value['quantity']+1);
    WC()->cart->set_quantity($key, $value['quantity']);
    break;
}

Change the quantity, then change back. And the shipping recalculates.

(More than 5 hours of code browsing, next time, I will code the whole webshop and use a good framework that is fully customizable OMG)

Solution 3

So Trevor's answer didn't exactly work for me (maybe WC has changed since it was made), it did lead me in the right direction though.

I used some slightly different filters. The problem I needed to solve was add/remove certain shipping options, based on a custom field.

add_action('woocommerce_checkout_update_order_review', function ($rawFields) {
            // This function checks for the value of my custom field on the checkout page.
            parse_str($rawFields, $checkoutFields);
            $this->myField = $checkoutFields['my_field'] ?? null;
});

add_filter('woocommerce_shipping_packages', 'modifyShippingPackages']);

public function modifyShippingPackages($packages)
    {
        if ($this->myField) {
            //.. modify $packages. In my case, I had to remove shipping methods.
            $rates = $packages[0]['rates'];

            $newRates = array_filter($rates, function ($shippingRate)
            {
                // figure out what should be in the rates
            });

            $packages[0]['rates'] = $newRates;

            $session = WC()->session;
            $session->set('shipping_method_counts', [count($newRates)]);
            $session->set('chosen_shipping_methods', [$newRates[0]->id]);
        }

        return $packages;
    }

The above snippet is an incomplete version of my code, but should get the point across. It can be tested easily by typing jQuery( 'body' ).trigger( 'update_checkout' ); in your javascript console.

Thanks for the hints Trevor.

Share:
19,950
Trevor Miles Wagner
Author by

Trevor Miles Wagner

I'm a relatively new web developer who is getting into application development and playing with php.

Updated on July 23, 2022

Comments

  • Trevor Miles Wagner
    Trevor Miles Wagner almost 2 years

    What i'm trying to accomplish: In woocommerce, I need to check a date delivered via a datepicker field when user selects a date, and then update shipping options accordingly via ajax, so that things like free shipping can be taken out when they are not appropriate.

    What I currently know/have figured out: I currently have the jQuery event firing and sending through a date to a custom script, which is kinda where I need to do. I have not been able to find a function within woocommerce classes that returns just the shipping data, so I don't think I can call that and return it as a fragment, as they do for the checkout already.

    However, I did find that

    WC_AJAX::update_order_review()
    

    has an action call within it, and I have successfully hooked a function onto that action, AND i've been able to fire off the

    t( 'body' ).trigger( 'update_checkout' );
    

    which fires off the action that updates the checkout review block.

    My real question: All this is great, and I'ts looking like its heading in the right direction, but I don't know woocommerce well enough to know how to get the shipping methods within my hooked action to unset them as necessary. Does anyone know if I can get them through the $woocommerce global object, and then have them be read by the rest of that WC_AJAX method??

    Any help here would be greatly appreciated.

    ** notes: yes, I know thats a 't' and not a '$' in the jQuery. Not a mistake