I'm building a custom shipping method for Woocommerce, and the one thing I'm totally hung up on is how to pass custom values to the calculate_shipping() function, either when it's being used on the Cart page or Checkout page.
I need to pass a handful of user-defined variables that will impact the quote -- ie "Is Residential Address", "Is Trade Show", etc etc.
calculate_shipping receives the $package array which contains a 'destination' array, but this only includes the standard add1, add2, city, state, zip, country info. I've added custom fields to the checkout page under both billing and shipping but I still can't figure out how to make these values accessible to the calculate_shipping function.
I've added a custom field like so:
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
$fields['shipping']['is_residential'] = array(
'label' => __('Residential Address?', 'woocommerce'),
'type' => 'checkbox',
'required' => false,
'class' => array('form-row-wide'),
'clear' => true
);
return $fields;
}
And I see this field show up on the Shipping section of the checkout form. However, I'm not seeing how I can access it anywhere. Even doing a print_r($_POST) on the checkout page doesn't show this field as being part of the post data, even after I know the form has been updated and re-posted.
But most importantly, I need to add the contents of the submitted field into the $package object which Woocommerce passes to a shipping method's calculate_shipping() function.
I'm just really not sure where to even start with this.
You can't expect to add checkout fields and have them available at the cart page.
The correct way is to use cart packages.
Check function get_shipping_packages() from class-wc-cart.php
public function get_shipping_packages() {
// Packages array for storing 'carts'
$packages = array();
$packages[0]['contents'] = $this->get_cart(); // Items in the package
$packages[0]['contents_cost'] = 0; // Cost of items in the package, set below
$packages[0]['applied_coupons'] = $this->applied_coupons;
$packages[0]['destination']['country'] = WC()->customer->get_shipping_country();
$packages[0]['destination']['state'] = WC()->customer->get_shipping_state();
$packages[0]['destination']['postcode'] = WC()->customer->get_shipping_postcode();
$packages[0]['destination']['city'] = WC()->customer->get_shipping_city();
$packages[0]['destination']['address'] = WC()->customer->get_shipping_address();
$packages[0]['destination']['address_2'] = WC()->customer->get_shipping_address_2();
foreach ( $this->get_cart() as $item )
if ( $item['data']->needs_shipping() )
if ( isset( $item['line_total'] ) )
$packages[0]['contents_cost'] += $item['line_total'];
return apply_filters( 'woocommerce_cart_shipping_packages', $packages );
}
You gotta hook into woocommerce_cart_shipping_packages filter and add your fields there.
Most likely you will need to add them (your fields) at the shipping calculator and checkout pages.
Hope this helps.
Related
I'm running some Facebook-Ads for a WooCommerce store and would like to add/pass the UTM parameters (utm_source, utm_medium and utm_campaign) to the WooCommerce order email (for the admin, not the customer). I did not find anything in the Woo documentation nor a plugin that is able to do that, maybe I am missing something.
I found a way how to add a custom field for the email, eg:
/**
* Add a custom field (in an order) to the emails
*/
add_filter( 'woocommerce_email_order_meta_fields', 'custom_woocommerce_email_order_meta_fields', 10, 3 );
function custom_woocommerce_email_order_meta_fields( $fields, $sent_to_admin, $order ) {
$fields['hear_about_us'] = array(
'label' => __( 'Hear About Us' ),
'value' => get_post_meta( $order->id, 'hear_about_us', true ),
);
return $fields;
}
but I don't know how I'd be able to pass the parameter contents. Any help is greatly appreciated!
I need to show shipping cost of each vendor in front of product at checkout page. enter image description here
I tried with
get_post_meta($cart_item['product_id'] , '_wcv_shipping_details', true);
But this return with blank value like:
Array ( [national] => [international] => [handling_fee] => [national_qty_override] => [national_disable] => [national_free] => [international_qty_override] => [international_disable] => [international_free] => )
I think you are trying to access cart object on order details page. Once the order is placed, the cart object will get empty. You will need to get items from the object of order and then fetch shipping cost for each item.
// You can use this piece of code on thank you page (after order is placed)
$order = new WC_Order( $order_id );
$order_item = $order->get_items();
foreach( $order_item as $product ) {
$prodct_name[] = $product['name'];
// here you will get product id and you can use that to get shipping details
get_post_meta($product['product_id'] , '_wcv_shipping_details', true);
}
I would like to add custom validation to the phone number in checkout page of woocommerce. How do I do this??
In my opinion there's no need to override default Woocommerce fields, nor use Javascript validation.
You can add a custom validation, that will be added to the Woocommerce's default billing phone field validation, hooking to an action triggered after submitting checkout.
This is the code I just implemented for a client.
// Custom validation for Billing Phone checkout field
add_action('woocommerce_checkout_process', 'custom_validate_billing_phone');
function custom_validate_billing_phone() {
$is_correct = preg_match('/^[0-9]{6,20}$/', $_POST['billing_phone']);
if ( $_POST['billing_phone'] && !$is_correct) {
wc_add_notice( __( 'The Phone field should be <strong>between 6 and 20 digits</strong>.' ), 'error' );
}
}
Of course, instead of my preg_match you can check anything else and adjust your conditional code to your needs.
You can also add custom validation for other default fields, by checking on the right $_POST variable, or your own custom checkout fields, after you correctly set them up, of course, but this is another topic :)
Hope this helps.
BY default woocommerce already have regular expression validation. The easiest way would be to validate it through jquery.
EDIT: Try not to make any changes to the woocommerce core as they will be over ridden on next update. Try this code
// Hook in
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
// Our hooked in function - $fields is passed via the filter!
function custom_override_checkout_fields( $fields ) {
$fields['shipping']['shipping_phone'] = array(
'label' => __('Phone', 'woocommerce'),
'placeholder' => _x('Phone', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-wide'),
'clear' => true
);
return $fields;
}
I have added new custom shipping field ( select option ) to woocommerce checkout page.
I am using that for shipping calculations.
that works perfect as well. but issue is while I change values in that field it do not update instantly.
It gives correct calculations on next page, after page submit.
I need it to work as change in custom field.
How to trigger WooCommerce Ajax which updates shipping calculation on change of my custom field ?
This is actually extremely simple to do if you are adding your fields in the correct way(using the woocommerce_checkout_fields filter). The only thing you need to do is to add the classes address-field and update_totals_on_change like this:
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
function custom_override_checkout_fields( $fields ) {
$fields['shipping']['custom_field'] = array(
'label' => 'Custom field',
'required' => 1,
'class' => array ('address-field', 'update_totals_on_change' )
);
return $fields;
}
I have a custom post type with just two (text)fields: an ISBN number and a youtube url by using 'supports' => array('title') when creating my custom post type.
The problem is, I don't need a title. So when I save my post, I made it so that the title becomes the ISBN number.
add_filter('wp_insert_post_data', array($this, 'change_title'), 99, 2);
function change_title($data, $postarr) {
if ($data['post_type'] == 'book_video') {
// If it is our form has not been submitted, so we dont want to do anything
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
return $data;
// Verify this came from the our screen and with proper authorization because save_post can be triggered at other times
if (!isset($_POST['wp_meta_box_nonce']))
return $data;
// Combine address with term
$title = $_POST['_bv_isbn'];
$data['post_title'] = $title;
}
return $data;
}
This works, but the problem is, when I save the post WITHOUT prefilling a title (anything at all) the post is not saved, the title change function is not called, and all of my fields are reset.
Is it possible to set a default value to the title and hide it ?
When you register your custom post type, you can set what it supports, including the title.
When you call register_post_type(), add another entry to $args called supports and set it's value to an array. You can then pass a list of elements you want that post type to support. The default is 'title' and 'editor', but there are a host of options to choose from.
For example:
<?php
register_post_type(
"myCustomPostType",
array(
'supports' : array(
'editor',
'author',
'custom-fields'
)
)
)
?>
So long as you miss out title then you won't have to define one for each post.
For more information, visit this page: http://codex.wordpress.org/Function_Reference/register_post_type#Arguments