Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get fields from checkout form into calculate_shipping

I'm creating a custom shipping calculator for my WooCommerce store. I need to collect some extra form data from the user to calculate the shipping properly.

I have the form fields added to woocommerce_checkout_after_customer_details. I can probably get this into the cart page without much trouble once I figure out the checkout page functionality, so let's focus on this.

I have a class extending WC_Shipping_Method with a calculate_shipping method a la this tutorial.

In that method, I want to use the extra form data (plus the destination) to calculate the shipping for that customer. Right now I just have this adding a dummy rate, which shows up.

I also created a custom cart-shipping.php file to not show any inputs for the available method in the order review cart, just the label and cost. Since the picking happens in my custom form, having options here is unnecessary.

  1. Am I doing this right, or is this approach super hacky?
  2. If this is the correct approach, how do I access the extra form fields inside the calculate_shipping method?

Have tried

Named the final options in my custom form shipping_method_* which triggers the wc-ajax=update_order_review call... which inits my custom shipping method class, but does not appear to ever call the calculate_shipping method anyhow. The only time this method seems to be called is when I'm actually adding a product to the order.

Also tried

Defined all of my possible delivery options and added them in calculate_shipping like $this->add_rate( $rate );. I think then I can somehow select one (force the user into one) when the cart updates via update_order_review ajax call? Again, the set option should be determined by these fields the user interacts with on the checkout form. But I haven't figured out how to set the shipping on that action yet.

like image 216
Randy Hall Avatar asked Mar 30 '19 18:03

Randy Hall


People also ask

How do I make a checkout field optional in WooCommerce?

Just go to any field in the Edit Section. You can find there a Required Field option. Simply mark or unmark this checkbox. This way you can set default WooCommerce fields as not required.


1 Answers

calculate_shipping() is not always called

First off, you should know that the calculate_shipping() method of your shipping method class will only be called when the shipping rates are calculated for the first time (in the current WooCommerce session), each time after a product is added to the cart, and whenever the shipping address is changed — from the cart or checkout page.

But of course, a plugin or custom code can programmatically call the method (or re-calculate the rates) at any times. However, the default behavior is as follows:

Rates are stored in the session based on the package hash to avoid re-calculation every page load.

And the calculate_shipping() method is executed by the WC_Shipping class through its calculate_shipping_for_package() method. In fact, the above quote was taken from the description of WC_Shipping::calculate_shipping_for_package() which calls WC_Shipping_Method::get_rates_for_package() and eventually the calculate_shipping() method in your own class.

How to get the submitted value of a form field in the checkout form from the calculate_shipping() method

So if you have this in the checkout form:

<input type="text" name="my_field"...>

Then you can get the submitted (POSTed) value using either the $_POST superglobal or the WC_Checkout::get_value() method:

  1. $_POST['my_field']

  2. WC()->checkout->get_value( 'my_field' )

It's as simple as that. :)


To ensure calculate_shipping is called

Run something similar to the following to clear out the session information, which will indicate that calculation still needs to be done. Be aware that if you do this on every page, it will mean that the shipping is constantly being recalculated when it doesn't need to be.

$packages = WC()->cart->get_shipping_packages();
foreach( $packages as $package_key => $package ) {
    $session_key  = 'shipping_for_package_'.$package_key;
    $stored_rates = WC()->session->__unset( $session_key );
}
like image 137
Sally CJ Avatar answered Oct 07 '22 16:10

Sally CJ