I have added two custom input fields in the shipping method section by changing the template /genesis-sample/woocommerce/checkout/review-order.php
I have also managed to get them conditionally required. Only when the specific radio button is checked, the input fields appear and become required. I am using jQuery code to make the fields appear and disappear. All of this is working fine. The code is here:
if ( isset($_POST['shipping_method_0_legacy_local_pickup']) && $_POST['shipping_method_0_legacy_local_pickup'] == 1 ) {
$cheq = 'true';
}
else {
$cheq = 'false';
}
woocommerce_form_field( 'fieldtester1' , array(
'type' => 'text',
'class' => array('wccs-field-class wccs-form-row-wide blahblah1'),
'label' => 'Enter Your Carrier Name',
'required' => $cheq,
'placeholder' => 'Carrier Name',
), $checkout->get_value( 'fieldtester1' ));
woocommerce_form_field( 'fieldtester2' , array(
'type' => 'text',
'class' => array('wccs-field-class wccs-form-row-wide blahblah2'),
'label' => 'Enter Your Carrier Account #',
'required' => $cheq,
'placeholder' => 'Carrier Number',
), $checkout->get_value( 'fieldtester2' ));
But here's the problem, even with the required attribute set as true for the two fields, the validation doesn't happen if the field is empty when the Place Order button is pressed. Ideally, the order should not go through and an error message should be generated. I have already added following code in functions.php to force validation of the input field but it doesn't do a thing. The code is here:
add_action('woocommerce_checkout_process', 'carrier_checkout_process');
function carrier_checkout_process() {
if ( isset($_POST['shipping_method_0_legacy_local_pickup']) && $_POST['shipping_method_0_legacy_local_pickup'] == 1 ) {
if( empty( $_POST['fieldtester1'] ) ) {
wc_add_notice( ( "Please don't forget to enter your shipping carrier details." ), "error" );
}
}
}
So, here's what I am looking for:
can anyone help me in forcing the validation for two input fields I
have added?
After the validation, I would also like to add these two fields in the new order email, and order details in the Wordpress dashboard.
If you are wondering why I am not using a woocommerce checkout field hook to add the input fields in the first place...I am not adding the fields to the checkout form so the hooks aren't of any help. The fields are added in the shipping method section. Another reason was, I wanted to update the required attribute every time a user would switch the shipping method. Using Jquery to change the required attribute wasn't working for whatever reason, believe me I tried for two days. Anyways, that part is already working. The only issues are getting the fields to validate and add them to the order emails and order details. I have looked around everywhere and the closest help I got was this post where LoicTheAztec gave a detailed solution
This can be done without jQuery using a special hook that will display your two "Carrier" custom fields below legacy_local_pickup shipping method when it's selected. If customer change to a different shipping method, those fields will be removed.
So you should need to remove your customized template and all related code before.
Now the validation works perfectly and when "Carrier" custom fields are filled, they are saved in order meta data.
// Add custom fields to a specific selected shipping method
add_action( 'woocommerce_after_shipping_rate', 'carrier_custom_fields', 20, 2 );
function carrier_custom_fields( $method, $index ) {
if( ! is_checkout()) return; // Only on checkout page
$customer_carrier_method = 'legacy_local_pickup';
if( $method->id != $customer_carrier_method ) return; // Only display for "local_pickup"
$chosen_method_id = WC()->session->chosen_shipping_methods[ $index ];
// If the chosen shipping method is 'legacy_local_pickup' we display
if($chosen_method_id == $customer_carrier_method ):
echo '<div class="custom-carrier">';
woocommerce_form_field( 'carrier_name' , array(
'type' => 'text',
'class' => array('form-row-wide carrier-name'),
'label' => 'Carrier Information:',
'required' => true,
'placeholder' => 'Carrier Name',
), WC()->checkout->get_value( 'carrier_name' ));
woocommerce_form_field( 'carrier_number' , array(
'type' => 'text',
'class' => array('form-row-wide carrier-number'),
'required' => true,
'placeholder' => 'Carrier Number',
), WC()->checkout->get_value( 'carrier_number' ));
echo '</div>';
endif;
}
// Check custom fields validation
add_action('woocommerce_checkout_process', 'carrier_checkout_process');
function carrier_checkout_process() {
if( isset( $_POST['carrier_name'] ) && empty( $_POST['carrier_name'] ) )
wc_add_notice( ( "Please don't forget to enter the shipping carrier name." ), "error" );
if( isset( $_POST['carrier_number'] ) && empty( $_POST['carrier_number'] ) )
wc_add_notice( ( "Please don't forget to enter the shipping carrier account number." ), "error" );
}
// Save custom fields to order meta data
add_action( 'woocommerce_checkout_update_order_meta', 'carrier_update_order_meta', 30, 1 );
function carrier_update_order_meta( $order_id ) {
if( isset( $_POST['carrier_name'] ))
update_post_meta( $order_id, '_carrier_name', sanitize_text_field( $_POST['carrier_name'] ) );
if( isset( $_POST['carrier_number'] ))
update_post_meta( $order_id, '_carrier_number', sanitize_text_field( $_POST['carrier_number'] ) );
}
This code goes on functions.php file of your active child theme (or theme). Tested and works.
Not displayed:
Displayed when "Local Pickup" is selected:
Related
In WooCommerce checkout page, I have added a custom field using the code below:
add_action( 'woocommerce_before_order_notes', 'bbloomer_add_custom_checkout_field' );
function bbloomer_add_custom_checkout_field( $checkout ) {
$current_user = wp_get_current_user();
$saved_gst_no = $current_user->gst_no;
woocommerce_form_field( 'gst_no', array(
'type' => 'text',
'class' => array( 'form-row-wide' ),
'label' => 'GST Number',
'placeholder' => 'GST Number',
'required' => true
//'default' => $saved_gst_no,
), $checkout->get_value( 'gst_no' ) );
error_log( $checkout->get_value( 'gst_no' ) );
}
On entering any value in GST Number field (custom checkout field), then going to payment screen by clicking "Place order" button and returning to checkout page without completing the transaction, all default woocommerce fields like billing phone, email etc are auto filled from the session.
However, the custom field added via above code is always blank. I tried logging the value of $checkout->get_value( 'gst_no' ) and it is always null.
How to make the $checkout object store custom field values?
Update 2
Because you need to save 'gst_no' custom field value as user data too. The following will save that custom field as user meta data:
add_action( 'woocommerce_checkout_update_customer', 'action_checkout_update_customer', 10, 2 );
function action_checkout_update_customer( $customer, $data ) {
if( isset($_POST['gst_no']) ) {
$customer->update_meta_data( 'gst_no', sanitize_text_field($_POST['gst_no']) );
}
}
Code goes in functions.php file of the active child theme (or active theme). It should work.
Note: For info, the WC_Checkout method get_value() use the user meta data.
I have added a custom field using the below code:
add_action( 'woocommerce_before_order_notes', 'bbloomer_add_custom_checkout_field' );
function bbloomer_add_custom_checkout_field( $checkout ) {
$current_user = wp_get_current_user();
$saved_gst_no = $current_user->gst_no;
woocommerce_form_field( 'gst_no', array(
'type' => 'text',
'class' => array( 'form-row-wide' ),
'label' => 'GST Number',
'placeholder' => 'GST Number',
'required' => true
//'default' => $saved_gst_no,
), $checkout->get_value( 'gst_no' ) );
}
On entering any value in GST Number field (custom checkout field), then going to payment screen by clicking "Place order" button and returning to checkout page without completing the transaction, all default woocommerce fields like billing phone, email etc are auto filled from the session.
However, the custom field added via above code is always blank. How to get the previously entered value auto-filled in the custom field for guest users, similar to the way default woocommerce fields are auto-filled?
Updated (replaced wrong WC_Session method set() with get() on the first function)
This will work for guest users too. Replace your code with:
// Display checkout custom field
add_action( 'woocommerce_before_order_notes', 'add_custom_checkout_field' );
function add_custom_checkout_field( $checkout ) {
$key_field = 'gst_no';
woocommerce_form_field( $key_field, array(
'type' => 'text',
'class' => array( 'form-row-wide' ),
'label' => __('GST Number'),
'placeholder' => __('GST Number'),
'required' => true
//'default' => $saved_gst_no,
), $checkout->get_value($key_field) ? $checkout->get_value($key_field) : WC()->session->get($key_field) );
}
// Save checkout custom field value in a WC_Session variable
add_action( 'woocommerce_checkout_create_order', 'action_checkout_create_order', 10, 2 );
function action_checkout_create_order( $order, $data ) {
$key_field = 'gst_no';
if( isset($_POST[$key_field]) ) {
WC()->session->set($key_field, sanitize_text_field($_POST[$key_field]));
}
}
// Save checkout custom field value as user meta data
add_action( 'woocommerce_checkout_update_customer', 'action_checkout_update_customer', 10, 2 );
function action_checkout_update_customer( $customer, $data ) {
$key_field = $key_field;
if( isset($_POST['gst_no']) ) {
$customer->update_meta_data($key_field, sanitize_text_field($_POST[$key_field]));
}
}
Note: Uses a WC_Session variable to store the submitted value for guests, allowing to display it on checkout when returning without completing the transaction.
In my experience, WooCommerce was saving the form data in session variables via AJAX when fields were updated via the update_order_review path. In order to hook into this and stored my custom vars, I used the following:
add_action('woocommerce_checkout_update_order_review', 'custom_woocommerce_checkout_update_order_review');
function custom_woocommerce_checkout_update_order_review($post_data){
// Convert $post_data string to array and clean it
$post_arr = array();
parse_str($post_data, $post_arr);
wc_clean($post_arr);
if(isset($post_arr['gst_no'])) {
WC()->session->set('gst_no', $post_arr['gst_no']);
}
}
Also a deeper way to add the custom checkout fields so that it's available in the $data passed to woocommerce_checkout_update_order_meta:
add_filter('woocommerce_checkout_fields', 'custom_checkout_fields');
function custom_checkout_fields($fields){
$fields['gst_no'] = array(
'type' => 'text',
'default' => WC()->session->get('gst_no')
);
return $fields;
}
How do I set the value of a custom field on an order, upon checkout submit, to a selected value in an HTML component?
I've implemented a custom HTML component, inside the checkout form, whose selected value I want to insert in a custom field on my orders. But I can't seem to figure out how to hook these things up.
I've arrived at somthing like:
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
function custom_override_checkout_fields( $fields ) {
$fields['order']['special_delivery'] = 'The value from HTML';
return $fields;
}
But I'm not even sure that's the right way to go.
Any ideas?
Using woocommerce_after_checkout_billing_form allows you to add custom fields to your checkout form. You also need to save the field in the order meta using woocommerce_checkout_update_order_meta.
In the following code there is a custom text field used for saving VAT ID. Feel free to get rid of the "mrank" prefixes. Also rename the field as you need it.
It will show you how to add a field and to save in order meta.
/**
* VAT Number in WooCommerce Checkout
*/
function mrank_vat_field( $checkout ) {
echo '<div id="mrank_vat_field">';
woocommerce_form_field( 'vat_number', array(
'type' => 'text',
'class' => array( 'vat-number-field form-row-wide') ,
'label' => __( 'VAT-ID' ),
'placeholder' => __( 'Enter number' ),
'description' => __( 'Please enter your VAT-ID' ),
'required' => true,
), $checkout->get_value( 'vat_number' ));
echo '</div>';
}
add_action( 'woocommerce_after_checkout_billing_form', 'mrank_vat_field' );
/**
* Save VAT Number in the order meta
*/
function mrank_checkout_vat_number_update_order_meta( $order_id ) {
if ( ! empty( $_POST['vat_number'] ) ) {
update_post_meta( $order_id, '_vat_number', sanitize_text_field( $_POST['vat_number'] ) );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'mrank_checkout_vat_number_update_order_meta' );
In the Checkout form I created a select field. My question is how in Wordpress or Woocmmerce this camp can be left as required.
<p class="form-row form-row-wide validate-required validate-region" id="shipping_region_field" data-priority="6">
<select name="shipping_region" id="shipping_region" class="state_select select2-selection--single" autocomplete="address-level1" data-placeholder="" tabindex="-1" aria-hidden="true">
<option>Opción 01</option>
<option>Opción 02</option>
</select>
</p>
1) For normal or custom billing and shipping fields you can use woocommerce_billing_fields or woocommerce_shipping_fields action hooks on checkout page as follow.
It will make a custom checkout field required without any need to add a validation script and to save it in the order. The field will also appear in My account edit adresses field section.
Some argument explanations:
The class 'update_totals_on_change' allow to trigger "update checkout" on change.
The 'required' attribute make the field required or not
The 'priority' attribute allow you to change the location of the field.
The code:
add_filter( 'woocommerce_shipping_fields', 'display_shipping_region_checkout_field', 20, 1 );
function display_shipping_region_checkout_field( $fields ) {
$fields['shipping_region'] = array(
'type' => 'select',
'label' => __("Region", "woocommerce") ,
'class' => array('form-row-wide', 'update_totals_on_change'),
'required' => true,
'options' => array(
'' => __("Choose a region please"),
'option-1' => __("Option 01"),
'option-2' => __("Option 02"),
'option-3' => __("Option 03"),
),
'priority' => 100,
'clear' => true,
);
return $fields;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
2) In specific cases you need to use the woocommerce_default_address_fields filter. This filter is applied to all billing and shipping default fields *(see the documentation). It's used only some default checkout fields.
3) You can also use woocommerce_checkout_fieldsthat has $fields as function argument (see documentation).
4) For other custom checkout fields you can use one of the following hooks with the woocommerce_form_field() function:
woocommerce_before_checkout_billing_form has $checkout as function argument
woocommerce_before_checkout_billing_form has $checkout as function argument
woocommerce_before_checkout_shipping_form has $checkout as function argument
woocommerce_before_checkout_shipping_form has $checkout as function argument
woocommerce_before_order_notes has $checkout as function argument
woocommerce_after_order_notes has $checkout as function argument
The code that display the field, validate the field and save the field in the order:
// Display field
add_action( 'woocommerce_after_checkout_shipping_form', 'display_shipping_region_after_checkout_shipping_form', 10, 1 );
function display_shipping_region_after_checkout_shipping_form ( $checkout ) {
woocommerce_form_field( 'shipping_region', array(
'type' => 'select',
'label' => __("Region", "woocommerce") ,
'class' => array('form-row-wide','update_totals_on_change'),
'required' => true,
'options' => array(
'' => __("Choose a region please"),
'option-1' => __("Option 01"),
'option-2' => __("Option 02"),
'option-3' => __("Option 03"),
),
'priority' => 100,
'clear' => true,
), $checkout->get_value( 'shipping_region' ) );
}
// Field Validation
add_action('woocommerce_checkout_process', 'shipping_region_custom_checkout_field_validation');
function shipping_region_custom_checkout_field_validation() {
if ( isset($_POST['shipping_region']) && empty($_POST['shipping_region']) )
wc_add_notice( __( 'Please select something into Region field.' ), 'error' );
}
// Save Field value
add_action( 'woocommerce_checkout_create_order', 'action_checkout_create_order_callback', 10, 2 );
function action_checkout_create_order_callback( $order, $data ) {
if ( isset($_POST['shipping_region']) && empty($_POST['shipping_region']) ) {
$order->update_meta_data( '_shipping_region', esc_attr($_POST['shipping_region']) );
if( $order->get_user_id() > 0 )
update_user_met( $order->get_user_id(), 'shipping_region', esc_attr($_POST['shipping_region']) );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
WooCommerce Documentation: Customizing checkout fields using actions and filters
you can add an attribute to the select tag
<select required name="shipping_region" id="shipping_region" class="state_select select2-selection--single" autocomplete="address-level1" data-placeholder="" tabindex="-1" aria-hidden="true">
<option>Opción 01</option>
<option>Opción 02</option>
</select>
**If You Have Created Your Custom Field at the checkout page**
add_action( 'woocommerce_after_checkout_validation', 'shipping_time_optionss', 9999, 2);
function shipping_time_optionss( $fields, $errors ){
// if any validation errors
if ( empty( $_POST['woo_shipping_time'] ) ) {
$errors->add( 'woocommerce_password_error', __( 'Please Select Shipping Time Option.' ) );
}
}
**
If You Have Created Your Custom Field at the checkout page
**
add_action( 'woocommerce_after_checkout_validation', 'shipping_time_optionss', 9999, 2);
function shipping_time_optionss( $fields, $errors ){
// if any validation errors
if ( empty( $_POST['woo_shipping_time'] ) ) {
$errors->add( 'woocommerce_password_error', __( 'Please Select Shipping Time Option.' ) );
}
}
I added custom email field on WooCommerce Checkout by following code
woocommerce_form_field('giftcard-friend-email', array(
'type' => 'email',
'class' => array( 'form-row-wide' ),
'required' => true,
'label' => __('To: Friend Email') ,
'placeholder' => __('Friend Email') ,
),
$checkout->get_value('giftcard-friend-email'));
It's working, required validation also working but it doesn't give error when input is an invalid email address.
I wonder if there's any built-in WooCommerce method to achieve this same as Billing Email?
Thanks!
You should always provide in your question the full function code and all related code too.
I have changed the giftcard-friend-email to _giftcard_friend_email a more normal slug as it will be saved as order meta data, so underscores are better option.
The following code will:
Display a custom email field in checkout page (so remove your code).
Will validate this email field checking that is not empty and a valid email.
Save this custom email value as order meta data when order is placed
The code:
// Display the custom checkout field
add_action('woocommerce_before_order_notes', 'add_custom_checkout_field', 20, 1 );
function add_custom_checkout_field( $checkout ) {
echo '<div id="friend_email_checkout_field">';
woocommerce_form_field( '_giftcard_friend_email', array(
'type' => 'email',
'label' => __('To: Friend Email') ,
'placeholder' => __('Friend Email') ,
'class' => array( 'form-row-wide' ),
'required' => true,
), $checkout->get_value('_friend_email') );
echo '</div>';
}
// Field custom email Validation
add_action( 'woocommerce_checkout_process', 'friend_email_checkout_field_validation' );
function friend_email_checkout_field_validation() {
if( isset($_POST['_giftcard_friend_email']) && empty($_POST['_giftcard_friend_email']) )
wc_add_notice( __( 'Please fill in the "Friend Email" field.', 'woocommerce' ), 'error' );
elseif( !preg_match("^[_a-z0-9-]+(\.[_a-z0-9-]+)*#[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$^", $_POST['_giftcard_friend_email'] ) ){
wc_add_notice( __( 'Please enter a valid "Friend Email".', 'woocommerce' ), 'error' );
}
}
// Save the checkout field value to order meta data
add_action('woocommerce_checkout_create_order', 'save_friend_email_checkout_field_value', 20, 2 );
function save_friend_email_checkout_field_value( $order, $data ) {
if ( isset( $_POST['_giftcard_friend_email'] ) ) {
$order->update_meta_data( '_giftcard_friend_email', sanitize_email( $_POST['_giftcard_friend_email'] ) );
}
}
Code goes in function.php file of the active child theme (or active theme). Tested and works.