Show sub Areas dropdown based on chosen city in WooCommerce checkout - php

I am creating a WooCommerce plugin and I want to display dynamically Sub Areas according to chosen customer city in checkout page.
Here is my code attempt:
add_filter( 'woocommerce_checkout_fields', 'dvs_city_list' );
function dvs_city_list( $fields ) {
$fields["billing"]["billing_city"]["type"] = 'select';
$fields["billing"]["billing_city"]["input_class"] = array(
'state_select' => 'state_select'
);
$fields["billing"]["billing_city"]["options"] = array(
'Lahore' => 'Lahore',
'Karachi' => 'Karachi'
),
return $fields;
}
add_filter( 'woocommerce_checkout_fields', 'dvs_area_list' );
function dvs_area_list( $fields ) {
$fields['billing']['billing_area']['label'] = 'Area';
$fields['billing']['billing_area']['required'] = 'True';
$fields["billing"]["billing_area"]["type"] = 'select';
$fields["billing"]["billing_area"]["class"][0] = 'form-row-last';
$fields['billing']['billing_area']['priority'] = 50;
$fields["billing"]["billing_area"]["input_class"] = array(
'state_select' => 'state_select'
);
$city = $_REQUEST['billing_city'];
if ($city == 'Lahore') {
$fields["billing"]["billing_area"]["options"] = array(
'Naval Town' => 'Naval Town',
'Bahria Town' => 'Bahria Town',
'Faisal Town' => 'Faisal Town'
);
}
else ($city == 'Karachi') {
$fields["billing"]["billing_area"]["options"] = array(
'Walton Road' => 'Walton Road',
'Zest Road' => 'Zest Road'
);
}
return $fields;
}
Here is the screenshot
But I am getting this error
Notice:
Undefined index: billing_city in …wp-content/plugins/custom-plugin/index.php on line 35
How to fixed this error? What I am doing wrong?

To synch a custom checkout select field from another select field, it requires to use jQuery.
Also you can merge both functions as they use the same hook.
Below in the first function, we keep your cities / areas settings that we can call everywhere. The last function enable dynamic options changes on the "Billing areas" dropdown depending on the chosen city:
function cities_areas_settings() {
$text_domain = 'woocommerce';
return array(
__('Lahore', $text_domain) => array(
__('Naval Town', $text_domain),
__('Bahria Town', $text_domain),
__('Faisal Town', $text_domain),
),
__('Karachi', $text_domain) => array(
__('Walton Road', $text_domain),
__('Zest Road', $text_domain),
)
);
}
add_filter( 'woocommerce_checkout_fields', 'custom_checkout_fields' );
function custom_checkout_fields( $fields ) {
// Initializing
$text_domain = 'woocommerce';
$option_cities = array();
$lahore_areas = array( '' => __('Choose your area', $text_domain) );
// Load settings and prepare options arrays
foreach( cities_areas_settings() as $city => $areas ) {
$option_cities[$city] = $city;
if( $city === 'Lahore' ) {
foreach( $areas as $area ) {
$lahore_areas[$area] = $area;
}
}
}
// 1. Billing City field
$fields['billing']['billing_city']['type'] = 'select';
$fields['billing']['billing_city']['class'] = array('form-row-first');
$fields['billing']['billing_city']['input_class'] = array('state_select');
$fields['billing']['billing_city']['options'] = $option_cities;
// 2. Billing Area Field
$fields['billing']['billing_area'] = array(
'type' => 'select',
'label' => __('Area', $text_domain),
'class' => array('form-row-last'),
'input_class' => array('state_select'),
'options' => $lahore_areas,
'required' => true,
'default' => '',
'priority' => 50,
);
return $fields;
}
add_action('wp_footer', 'custom_checkout_js_script');
function custom_checkout_js_script() {
if( is_checkout() && ! is_wc_endpoint_url() ) :
// Initializing
$text_domain = 'woocommerce';
$karachi_areas = array( '' => __('Choose your area', $text_domain) );
$settings = cities_areas_settings(); // Load settings
// Prepare 'Karachi' options dropdown
foreach( cities_areas_settings()['Karachi'] as $area ) {
$karachi_areas[$area] = $area;
}
?>
<script language="javascript">
jQuery( function($){
var a = 'select[name="billing_city"]',
b = 'select[name="billing_area"]',
o = <?php echo json_encode($karachi_areas); ?>,
s = $(b).html();
// Utility function to fill dynamically the select field options
function dynamicSelectOptions( opt ){
var options = '';
$.each( opt, function( key, value ){
options += '<option value="'+key+'">'+value+'</option>';
});
$(b).html(options);
}
// On Start (once DOM is loaded)
if ( $(a).val() === 'Karachi' ) {
dynamicSelectOptions( o );
}
console.log($(a).val());
// On billing city change live event
$('form.woocommerce-checkout').on('change', a, function() {
console.log($(this).val());
if ( $(this).val() === 'Karachi' ) {
dynamicSelectOptions( o );
} else {
$(b).html(s);
}
});
});
</script>
<?php
endif;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
Related: Dynamic synched custom checkout select fields in WooCommerce

Related

Want to add a drop down to select local pickup location at checkout [duplicate]

Inspired from Shipping carrier custom fields validation in Woocommerce checkout page answer code, I use following code which displays a select field with shipping companies (this field is displayed only when I choose a specific 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 the checkout page
$customer_carrier_method = 'flat_rate:14';
if( $method->id != $customer_carrier_method ) return; // Mostrar solo para "flat_rate:14"
$chosen_method_id = WC()->session->chosen_shipping_methods[ $index ];
// If the chosen shipping method is 'flat_rate: 14', we will show
if($chosen_method_id == $customer_carrier_method ):
echo '<div class="custom-carrier2">';
woocommerce_form_field( 'carrier_name1', array(
'type' => 'select',
'class' => array('carrier_name2-class form-row-wide'),
'label' => __('<strong>Shipping Company</strong>'),
'required' => 'true',
'options' => array(
'1' => '', // no data means that the field is not selected
'Shipping Company 1' => 'Shipping Company 1',
'Shipping Company 2' => 'Shipping Company 2',
'Shipping Company 3' => 'Shipping Company 3',
'Shipping Company 4' => 'Shipping Company 4'
)
), WC()->checkout->get_value( 'carrier_name1' ) );
echo '</div>';
endif;
}
// Validate the custom selection field
add_action('woocommerce_checkout_process', 'carrier_checkout_process');
function carrier_checkout_process() {
if( isset( $_POST['carrier_name1'] ) && empty( $_POST['carrier_name1'] ) )
wc_add_notice( ( "<strong>Shipping Company</strong> it is a required field." ), "error" );
}
// Save custom fields to sort metadata
add_action( 'woocommerce_checkout_update_order_meta', 'carrier_update_order_meta', 30, 1 );
function carrier_update_order_meta( $order_id ) {
if( isset( $_POST['carrier_name1'] ))
update_post_meta( $order_id, 'carrier_name1', sanitize_text_field( $_POST['carrier_name1'] ) );
}
The problem is that it's only displayed on the checkout page and I would like it to show it in cart page, keeping the selected vale on cart page to checkout page.
I think I have found something that says that this transfer of the selected data between the cart and the payment page is done through Ajax, but I am not skilled with Ajax and I don't know how to make that work.
To make that work on cart and checkout pages, you will need some additional code using jQuery, Ajax and WC Session variable:
Final update - To make the code more dynamic, we start with a custom function that will handle all required settings:
// Custom function that handle your settings
function carrier_settings(){
return array(
'targeted_methods' => array('flat_rate:14'), // Your targeted shipping method(s) in this array
'field_id' => 'carrier_name', // Field Id
'field_type' => 'select', // Field type
'field_label' => '', // Leave empty value if the first option has a text (see below).
'label_name' => __("Carrier company","woocommerce"), // for validation and as meta key for orders
'field_options' => array(
// The option displayed at first ( or keep an empty value '',)
__("Choose a carrier company", "woocommerce"),
// The carrier companies below (one by line)
'Company name 1',
'Company name 2',
'Company name 3',
'Company name 4',
),
);
}
Then we can load that settings on any function where it's needed.
Now the Select field with carrier companies displayed for a specific shipping method on cart and checkout pages:
// Display the custom checkout field
add_action( 'woocommerce_after_shipping_rate', 'carrier_company_custom_select_field', 20, 2 );
function carrier_company_custom_select_field( $method, $index ) {
extract( carrier_settings() ); // Load settings and convert them in variables
$chosen = WC()->session->get('chosen_shipping_methods'); // The chosen methods
$value = WC()->session->get($field_id);
$value = WC()->session->__isset($field_id) ? $value : WC()->checkout->get_value('_'.$field_id);
$options = array(); // Initializing
if( ! empty($chosen) && $method->id === $chosen[$index] && in_array($method->id, $targeted_methods) ) {
echo '<div class="custom-carrier">';
// Loop through field otions to add the correct keys
foreach( $field_options as $key => $option_value ) {
$option_key = $key == 0 ? '' : $key;
$options[$option_key] = $option_value;
}
woocommerce_form_field( $field_id, array(
'type' => $field_type,
'label' => '', // Not required if the first option has a text.
'class' => array('form-row-wide ' . $field_id . '-' . $field_type ),
'required' => true,
'options' => $options,
), $value );
echo '</div>';
}
}
The Ajax part: The jQuery sender + PHP WordPress admin Ajax receiver code for the selected carrier company:
// jQuery code (client side) - Ajax sender
add_action( 'wp_footer', 'carrier_company_script_js' );
function carrier_company_script_js() {
// Only cart & checkout pages
if( is_cart() || ( is_checkout() && ! is_wc_endpoint_url() ) ):
// Load settings and convert them in variables
extract( carrier_settings() );
$js_variable = is_cart() ? 'wc_cart_params' : 'wc_checkout_params';
// jQuery Ajax code
?>
<script type="text/javascript">
jQuery( function($){
if (typeof <?php echo $js_variable; ?> === 'undefined')
return false;
$(document.body).on( 'change', 'select#<?php echo $field_id; ?>', function(){
var value = $(this).val();
$.ajax({
type: 'POST',
url: <?php echo $js_variable; ?>.ajax_url,
data: {
'action': 'carrier_name',
'value': value
},
success: function (result) {
console.log(result); // Only for testing (to be removed)
}
});
});
});
</script>
<?php
endif;
}
// The Wordpress Ajax PHP receiver
add_action( 'wp_ajax_carrier_name', 'set_carrier_company_name' );
add_action( 'wp_ajax_nopriv_carrier_name', 'set_carrier_company_name' );
function set_carrier_company_name() {
if ( isset($_POST['value']) ){
// Load settings and convert them in variables
extract( carrier_settings() );
if( empty($_POST['value']) ) {
$value = 0;
$label = 'Empty';
} else {
$value = $label = esc_attr( $_POST['value'] );
}
// Update session variable
WC()->session->set( $field_id, $value );
// Send back the data to javascript (json encoded)
echo $label . ' | ' . $field_options[$value];
die();
}
}
Then on checkout page, the field validation and saving the chosen carrier company to the order:
// Conditional function for validation
function has_carrier_field(){
$settings = carrier_settings();
return array_intersect(WC()->session->get( 'chosen_shipping_methods' ), $settings['targeted_methods']);
}
// Validate the custom selection field
add_action('woocommerce_checkout_process', 'carrier_company_checkout_validation');
function carrier_company_checkout_validation() {
// Load settings and convert them in variables
extract( carrier_settings() );
if( has_carrier_field() && isset( $_POST[$field_id] ) && empty( $_POST[$field_id] ) )
wc_add_notice(
sprintf( __("Please select a %s as it is a required field.","woocommerce"),
'<strong>' . $label_name . '</strong>'
), "error" );
}
// Save custom field as order meta data
add_action( 'woocommerce_checkout_create_order', 'save_carrier_company_as_order_meta', 30, 1 );
function save_carrier_company_as_order_meta( $order ) {
// Load settings and convert them in variables
extract( carrier_settings() );
if( has_carrier_field() && isset( $_POST[$field_id] ) && ! empty( $_POST[$field_id] ) ) {
$order->update_meta_data( '_'.$field_id, $field_options[esc_attr($_POST[$field_id])] );
WC()->session->__unset( $field_id ); // remove session variable
}
}
Display the selected carrier on admin order pages, on customer orders and email notifications:
// Display custom field in admin order pages
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'admin_order_display_carrier_company', 30, 1 );
function admin_order_display_carrier_company( $order ) {
// Load settings and convert them in variables
extract( carrier_settings() );
$carrier = $order->get_meta( '_'.$field_id ); // Get carrier company
if( ! empty($carrier) ) {
// Display
echo '<p><strong>' . $label_name . '</strong>: ' . $carrier . '</p>';
}
}
// Display carrier company after shipping line everywhere (orders and emails)
add_filter( 'woocommerce_get_order_item_totals', 'display_carrier_company_on_order_item_totals', 1000, 3 );
function display_carrier_company_on_order_item_totals( $total_rows, $order, $tax_display ){
// Load settings and convert them in variables
extract( carrier_settings() );
$carrier = $order->get_meta( '_'.$field_id ); // Get carrier company
if( ! empty($carrier) ) {
$new_total_rows = [];
// Loop through order total rows
foreach( $total_rows as $key => $values ) {
$new_total_rows[$key] = $values;
// Inserting the carrier company under shipping method
if( $key === 'shipping' ) {
$new_total_rows[$field_id] = array(
'label' => $label_name,
'value' => $carrier,
);
}
}
return $new_total_rows;
}
return $total_rows;
}
All code goes on functions.php file of your active child theme (or theme). Tested and works.
Other related threads:
Enable delivery time options for a specific state in Woocommerce checkout
Update cart shipping data with AJAX in WooCommerce
On cart page (for chosen specific shipping method):
On checkout page page (for chosen specific shipping method):
On customer orders (email notifications and admin order pages too):

Save Woocommerce cart item custom data as order item meta data displaying it on orders and emails

Regarding Woocommerce. I have custom data that I am adding to the cart. In the functions.php file, I have the following function.
// Display cart item custom data in cart and checkout pages
add_filter( 'woocommerce_get_item_data', 'display_cart_item_custom_on_cart_and_checkout', 10, 2 );
function display_cart_item_custom_on_cart_and_checkout( $cart_item_data, $cart_item ){
if( isset($cart_item['custom_data']['label0']) && isset($cart_item['custom_data']['value0']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label0'],
'value' => $cart_item['custom_data']['value0'],
);
}
if( isset($cart_item['custom_data']['label']) && isset($cart_item['custom_data']['value']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label'],
'value' => $cart_item['custom_data']['value'],
);
}
if( isset($cart_item['custom_data']['label2']) && isset($cart_item['custom_data']['value2']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label2'],
'value' => $cart_item['custom_data']['value2'],
);
}
if( isset($cart_item['custom_data']['label3']) && isset($cart_item['custom_data']['value3']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label3'],
'value' => $cart_item['custom_data']['value3'],
);
}
if( isset($cart_item['custom_data']['label4']) && isset($cart_item['custom_data']['value4']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label4'],
'value' => $cart_item['custom_data']['value4'],
);
}
if( isset($cart_item['custom_data']['label5']) && isset($cart_item['custom_data']['value5']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label5'],
'value' => $cart_item['custom_data']['value5'],
);
}
if( isset($cart_item['custom_data']['label6']) && isset($cart_item['custom_data']['value6']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label6'],
'value' => $cart_item['custom_data']['value6'],
);
}
if( isset($cart_item['custom_data']['label7']) && isset($cart_item['custom_data']['value7']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label7'],
'value' => $cart_item['custom_data']['value7'],
);
}
if( isset($cart_item['custom_data']['label8']) && isset($cart_item['custom_data']['value8']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label8'],
'value' => $cart_item['custom_data']['value8'],
);
}
if( isset($cart_item['custom_data']['label9']) && isset($cart_item['custom_data']['value9']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label9'],
'value' => $cart_item['custom_data']['value9'],
);
}
if( isset($cart_item['custom_data']['label10']) && isset($cart_item['custom_data']['value10']) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label10'],
'value' => $cart_item['custom_data']['value10'],
);
}
return $cart_item_data;
}
This works and the custom data is shown in the cart. However, the custom data does not show on the order and order email. I have seen, on Stackoverflow several answers that provide solutions to this problem but I cannot make them work for my situation. The solutions that I reference are.
Save and display order item custom meta data in Woocommerce
Display and save added custom cart item data on Woocommerce Cart, Checkout and Orders
Can anybody kindly show me what "my" function should be?
Thank you.
First you can optimize and compact your function this way:
// Display cart item custom data in cart and checkout pages
add_filter( 'woocommerce_get_item_data', 'display_cart_item_custom_on_cart_and_checkout', 10, 2 );
function display_cart_item_custom_on_cart_and_checkout( $cart_item_data, $cart_item ){
$keys = array('0','','2','3','4','5','6','7','8','9','10'); // Fields numbers part keys array
// Loop through Fields numbers part keys array
foreach( $keys as $key ) {
if( isset($cart_item['custom_data']['label'.$key]) && isset($cart_item['custom_data']['value'.$key]) ) {
$cart_item_data[] = array(
'name' => $cart_item['custom_data']['label'.$key],
'value' => $cart_item['custom_data']['value'.$key],
);
}
}
return $cart_item_data;
}
Then to save all your custom cart item data as custom order item meta data and display it everywhere on orders and emails, use the following:
// Save cart item custom data as order item meta data and display it everywhere in Orders and email notifications
add_action('woocommerce_checkout_create_order_line_item', 'save_as_custom_order_item_meta_data', 10, 4 );
function save_as_custom_order_item_meta_data( $item, $cart_item_key, $values, $order ) {
$keys = array('0','','2','3','4','5','6','7','8','9','10'); // Fields numbers part keys array()
// Loop through Fields numbers part keys array
foreach( $keys as $key ) {
if( isset( $values['custom_data']['label'.$key] ) && isset( $values['custom_data']['value'.$key] ) ) {
$item->update_meta_data( $values['custom_data']['label'.$key], $values['custom_data']['value'.$key] );
}
}
}
Code goes in functions.php file of the active child theme (or active theme). It should works.

Replace the city text field by a dropdown of cities for a specific country in Woocommerce

The existing list of countries is excellent, but we need to select Saudi Arabia in particular and show it another list with the names of the main cities such as Riyadh, Jeddah, etc. The last option is another, and if he selects another, a text box will appear to write the name of the city or region in which it is located.
I try this, Its work with me when select Saudi Arabia, i can show list of cities, but I could not do if he selects another, a text box will appear to write the name of the city or region in which it is located.
add_filter( 'woocommerce_default_address_fields' , 'customize_checkout_city_field' );
function customize_checkout_city_field( $address_fields ) {
global $woocommerce;
if ($woocommerce->customer->get_country() == 'SA') {
$towns_cities_arr = array(
'0' => __('Select City', 'my_theme_slug'),
'Abhā' => 'Abhā',
'Abqaiq' => 'Abqaiq',
'Al-Baḥah' => 'Al-Baḥah',
'Al-Dammām' => 'Al-Dammām',
'Al-Hufūf' => 'Al-Hufūf',
'Al-Jawf' => 'Al-Jawf',
'Al-Kharj' => 'Al-Kharj',
'Al-Khubar' => 'Al-Khubar',
'Al-Qaṭīf' => 'Al-Qaṭīf',
'Al-Ṭaʾif' => 'Al-Ṭaʾif',
'ʿArʿar' => 'ʿArʿar',
'Buraydah' => 'Buraydah',
'Dhahran' => 'Dhahran',
'Ḥāʾil' => 'Ḥāʾil',
'Jiddah' => 'Jiddah',
'Jīzān' => 'Jīzān',
'Khamīs Mushayt' => 'Khamīs Mushayt',
'King Khalīd Military City' => 'King Khalīd Military City',
'Mecca' => 'Mecca',
'Medina' => 'Medina',
'Najrān' => 'Najrān',
'Ras Tanura' => 'Ras Tanura',
'Riyadh' => 'Riyadh',
'Sakākā' => 'Sakākā',
'Tabūk' => 'Tabūk',
'Yanbuʿ' => 'Yanbuʿ',
'Other' => 'Other',
);
$address_fields['city']['type'] = 'select';
$address_fields['city']['class'] = array('update_totals_on_change');
$address_fields['city']['label'] = __('City', 'my_theme_slug');
$address_fields['city']['options'] = $towns_cities_arr;
} else {
$address_fields['city']['type'] = 'text';
}
return $address_fields;
}
Updated (Additional city text field when "Others" is the selected value from the dropdown)
The following code (jQuery powered) will replace the city text field by a custom dropdown of cities for a specific country only and for this specific country, if the city selected value is "Others", an additional text field will appear under the cities dropdown, where customer can enter manually a different city.
The code works for shipping and billing fields independently.
When "Others" is selected for the defined country, the two last functions will:
Validate that the city additional field is filled,
Save the city value as billing or shipping city value.
The code:
// HERE are is the array of cities for Saudi Arabia (SA)
function get_cities_options(){
$domain = 'woocommerce'; // The domain text slug
return array(
'' => __('Select a city', $domain),
'Abhā' => 'Abhā', 'Abqaiq' => 'Abqaiq',
'Al-Baḥah' => 'Al-Baḥah', 'Al-Dammām' => 'Al-Dammām',
'Al-Hufūf' => 'Al-Hufūf', 'Al-Jawf' => 'Al-Jawf',
'Al-Kharj' => 'Al-Kharj', 'Al-Khubar' => 'Al-Khubar',
'Al-Qaṭīf' => 'Al-Qaṭīf', 'Al-Ṭaʾif' => 'Al-Ṭaʾif',
'ʿArʿar' => 'ʿArʿar', 'Buraydah' => 'Buraydah',
'Dhahran' => 'Dhahran', 'Ḥāʾil' => 'Ḥāʾil',
'Jiddah' => 'Jiddah','Jīzān' => 'Jīzān',
'Khamīs Mushayt' => 'Khamīs Mushayt',
'King Khalīd Military City' => 'King Khalīd Military City',
'Mecca' => 'Mecca', 'Medina' => 'Medina',
'Najrān' => 'Najrān', 'Ras Tanura'=> 'Ras Tanura',
'Riyadh' => 'Riyadh', 'Sakākā' => 'Sakākā',
'Tabūk' => 'Tabūk', 'Yanbuʿ' => 'Yanbuʿ',
'Other' => __('Other cities (not listed)', $domain),
);
}
// add an additional field
add_filter( 'woocommerce_checkout_fields' , 'additional_checkout_city_field' );
function additional_checkout_city_field( $fields ) {
// Inline CSS To hide the fields on start
?><style> #billing_city2_field.hidden, #shipping_city2_field.hidden {display:none;}</style><?php
$fields['billing']['billing_city2'] = array(
'placeholder' => _x('Other city', 'placeholder', 'woocommerce'),
'required' => false,
'priority' => 75,
'class' => array('form-row-wide hidden'),
'clear' => true
);
$fields['shipping']['shipping_city2'] = array(
'placeholder' => _x('Other city', 'placeholder', 'woocommerce'),
'required' => false,
'priority' => 75,
'class' => array('form-row-wide hidden'),
'clear' => true
);
return $fields;
}
// Add checkout custom select fields
add_action( 'wp_footer', 'custom_checkout_city_field', 20, 1 );
function custom_checkout_city_field() {
// Only checkout page
if( is_checkout() && ! is_wc_endpoint_url() ):
$country = 'SA'; // <=== <=== The country code
$b_city = 'billing_city';
$s_city = 'shipping_city';
$billing_city_compo = 'name="'.$b_city.'" id="'.$b_city.'"';
$shipping_city_compo = 'name="'.$s_city.'" id="'.$s_city.'"';
$end_of_field = ' autocomplete="address-level2" value="">';
$billing_text_field = '<input type="text" class="input-text" ' . $billing_city_compo . $end_of_field;
$shipping_text_field = '<input type="text" class="input-text" ' . $shipping_city_compo . $end_of_field;
$billing_select_field = '<select ' . $billing_city_compo . $end_of_field;
$shipping_select_field = '<select ' . $shipping_city_compo . $end_of_field;
?>
<script type="text/javascript">
jQuery(function($){
var a = <?php echo json_encode( get_cities_options() ); ?>, fc = 'form.checkout',
b = 'billing', s = 'shipping', ci = '_city2',
bc = '<?php echo $b_city; ?>', sc = '<?php echo $s_city; ?>', co = '_country',
bci = '#'+bc, sci = '#'+sc, fi = '_field',
btf = '<?php echo $billing_text_field; ?>', stf = '<?php echo $shipping_text_field; ?>',
bsf = '<?php echo $billing_select_field; ?>', ssf = '<?php echo $shipping_select_field; ?>',
cc = '<?php echo $country; ?>';
// Utility function that fill dynamically the select field options
function dynamicSelectOptions( type ){
var select = (type == b) ? bsf : ssf,
fvalue = (type == b) ? $(bci).val() : $(sci).val();
$.each( a, function( key, value ){
selected = ( fvalue == key ) ? ' selected' : '';
selected = ( ( fvalue == '' || fvalue == undefined ) && key == '' ) ? ' selected' : selected;
select += '<option value="'+key+'"'+selected+'>'+value+'</option>';
});
select += '</select>';
if ( type == b )
$(bci).replaceWith(select);
else
$(sci).replaceWith(select);
}
// Utility function that will show / hide the "country2" additional text field
function showHideCity2( type, city ){
var field = (type == b) ? bci : sci,
country = $('#'+type+co).val();
if( country == cc && city == 'Other' && $('#'+type+ci+fi).hasClass('hidden') ){
$('#'+type+ci+fi).removeClass('hidden');
} else if( country != cc || ( city != 'Other' && ! $('#'+type+ci+fi).hasClass('hidden') ) ) {
$('#'+type+ci+fi).addClass('hidden');
if( country != cc && city == 'Other' ){
$(field).val('');
}
}
}
// On billing country change
$(fc).on('change', '#'+b+co, function(){
var bcv = $(bci).val();
if($(this).val() == cc){
if( $(bci).attr('type') == 'text' ){
dynamicSelectOptions(b);
showHideCity2( b, $(bci).val() );
}
} else {
if( $(bci).attr('type') != 'text' ){
$(bci).replaceWith(btf);
$(bci).val(bcv);
showHideCity2( b, $(bci).val() );
}
}
});
// On shipping country change
$(fc).on('change', '#'+s+co, function(){
var scv = $(sc).val();
if($(this).val() == cc){
if( $(sci).attr('type') == 'text' ){
dynamicSelectOptions(s);
showHideCity2( s, $(sci).val() );
}
} else {
if( $(sci).attr('type') != 'text' ){
$(sci).replaceWith(stf);
$(sci).val(scv);
showHideCity2( s, $(sci).val() );
}
}
});
// On billing city change
$(fc).on('change', bci, function(){
showHideCity2( b, $(this).val() );
});
// On shipping city change
$(fc).on('change', sci, function(){
showHideCity2( s, $(this).val() );
});
});
</script>
<?php
endif;
}
// Check for city 2 fields if billing or/and shipping city fields is "Other"
add_action('woocommerce_checkout_process', 'cbi_cf_process');
function cbi_cf_process() {
// Check billing city 2 field
if( isset($_POST['billing_city2']) && empty($_POST['billing_city2']) && $_POST['billing_city'] == 'Other' ){
wc_add_notice( __( "Please fill in billing city field" ), "error" );
}
// Updating shipping city 2 field
if( isset($_POST['shipping_city2']) && empty($_POST['shipping_city2']) && $_POST['shipping_city'] == 'Other' ){
wc_add_notice( __( "Please fill in shipping city field" ), "error" );
}
}
// Updating billing and shipping city fields when using "Other"
add_action( 'woocommerce_checkout_create_order', 'update_order_city_field', 30, 2 );
function update_order_city_field( $order, $posted_data ) {
// Updating billing city from 'billing_city2'
if( isset($_POST['billing_city2']) && ! empty($_POST['billing_city2']) && $_POST['billing_city'] == 'Other' ){
$order->set_billing_city(sanitize_text_field( $_POST['billing_city2'] ) );
}
// Updating shipping city
if( isset($_POST['shipping_city2']) && ! empty($_POST['shipping_city2']) && $_POST['shipping_city'] == 'Other' ){
$order->set_shipping_city(sanitize_text_field( $_POST['shipping_city'] ) );
}
}
Code goes in function.php file of your active child theme (active theme). Tested and works.
Please Remember that a customer can be in a foreign country outside Soudi Arabia (billing country) and buy something that will be shipped in Soudi Arabia (shipping country).

Hide shipping method based on the radio button in WooCommerce checkout page

I put a radio button in the checkout page with the code below.
add_action( 'woocommerce_before_checkout_shipping_form', 'custom_shipping_radio_button', 10, 1 );
function custom_shipping_radio_button( $checkout ) {
woocommerce_form_field( 'shipping_type', array(
'type' => 'radio',
'class' => array( 'form-row-wide' ),
'label' => __('收件方式 *'),
'options' => array(
'shipping_1' => __('全家店到店'),
'shipping_2' => __('指定地址'),
'shipping_3' => __('自行取貨'),
),
), $checkout->get_value( 'shipping_type' ) );
}
I want to hide the option based on shipping method. For example, if the customers choose local pickup, options, shipping_1 and shipping_2 will disappear. I searched some information and tried to make the codes as below.
add_action( 'woocommerce_after_checkout_form', 'hide_shipping_type' );
function hide_shipping_type( $available_gateways ) {
global $woocommerce;
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
$chosen_shipping_no_ajax = $chosen_methods[0];
if ( 0 === strpos( $chosen_shipping_no_ajax, 'local_pickup' ) ) {
?>
<script type="text/javascript">
jQuery('#shipping_type_shipping_1,#shipping_type_shipping_2').fadeOut();
</script>
<?php
}
?>
<script type="text/javascript">
jQuery('form.checkout').on('change','input[name^="shipping_method"]',function() {
var val = jQuery( this ).val();
if (val.match("^local_pickup")) {
jQuery('#shipping_type_shipping_1,#shipping_type_shipping_2').fadeOut();
} else {
jQuery('#shipping_type_shipping_1,#shipping_type_shipping_2').fadeIn();
}
});
</script>
<?php
}
I found that the labels for the options cannot be hidden. I thought the problem may be caused by jQuery script. However, I cannot make it better.
Does anybody have idea about this problem?
UPDATE
I got a new idea about unsetting shipping method in cart page and hiding shipping method in checkout page based on shipping type radio button. As a result, I tried to write the code as below. These codes can work and the label of shipping method can disappear. However, after selecting the one of shipping methods, the other hide shipping methods will fade in. Is there any solution?
//Unset shipping method in cart page
add_filter( 'woocommerce_cart_ready_to_calc_shipping', 'disable_shipping_calc_on_cart', 99 );
function disable_shipping_calc_on_cart( $show_shipping ) {
if( is_cart() ) {
return false;
}
return $show_shipping;
}
//Hide shipping method in checkout page based on the selection of radio button.
add_action( 'woocommerce_before_checkout_shipping_form', 'custom_shipping_radio_button', 10, 1 );
function custom_shipping_radio_button( $checkout ) {
woocommerce_form_field( 'shipping_type', array(
'type' => 'radio',
'class' => array( 'form-row-wide' ),
'label' => __('收件方式 *'),
'options' => array(
'shipping_1' => __('全家店到店'),
'shipping_2' => __('指定地址'),
'shipping_3' => __('自行取貨'),
),
), $checkout->get_value( 'shipping_type' ) );
?>
<script type="text/javascript">
jQuery(function($){
$("input[name=shipping_type]").on("change",function(){
if($("#shipping_type_shipping_1").is(":checked")) {
$("#add_familimart,#shipping_first_name_field,#shipping_last_name_field,#shipping_city_field,#shipping_company_field,#shipping_method_0_flat_rate9,label[for='shipping_method_0_flat_rate9']").fadeIn();
} else {
$("#add_familimart,#shipping_first_name_field,#shipping_last_name_field,#shipping_city_field,#shipping_company_field,#shipping_method_0_flat_rate9,label[for='shipping_method_0_flat_rate9']").fadeOut();
}
if($("#shipping_type_shipping_2").is(":checked")) {
$("#shipping_postcode_field,#shipping_address_1_field,#shipping_method_0_flat_rate10,#shipping_method_0_flat_rate11,#shipping_method_0_flat_rate12,label[for='shipping_method_0_flat_rate12'],label[for='shipping_method_0_flat_rate11'],label[for='shipping_method_0_flat_rate10']").fadeIn();
} else {
$("#shipping_postcode_field,#shipping_address_1_field,#shipping_method_0_flat_rate10,#shipping_method_0_flat_rate11,#shipping_method_0_flat_rate12,label[for='shipping_method_0_flat_rate12'],label[for='shipping_method_0_flat_rate11'],label[for='shipping_method_0_flat_rate10']").fadeOut();
}
if($("#shipping_type_shipping_3").is(":checked")) { $("#shipping_address_2_field,#shipping_method_0_local_pickup8,label[for='shipping_method_0_local_pickup8']").fadeIn();
} else {
$("#shipping_address_2_field,#shipping_method_0_local_pickup8,label[for='shipping_method_0_local_pickup8']").fadeOut();
}
});
});
</script>
<?php
}
You can merge all your code in the first function and it work as well. Now you should need to add jQuery ready() function at start.
It's quiet simple and easy to target <label> tags with a "for" attribute using in your case label[for="shipping_type_shipping_1"] and label[for="shipping_type_shipping_2"]…
I have revisited and compacted your code in one unique hooked function:
add_action( 'woocommerce_before_checkout_shipping_form', 'custom_shipping_radio_buttons', 10, 1 );
function custom_shipping_radio_buttons( $checkout ) {
woocommerce_form_field( 'shipping_type', array(
'type' => 'radio',
'class' => array( 'form-row-wide' ),
'label' => __('收件方式 *'),
'options' => array(
'shipping_1' => __('全家店到店'),
'shipping_2' => __('指定地址'),
'shipping_3' => __('自行取貨'),
),
), $checkout->get_value( 'shipping_type' ) );
$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' )[0];
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
var a = 'shipping_type_shipping_',
b = 'label[for="'+a+'1"],label[for="'+a+'2"],#'+a+'1,#'+a+'2';
<?php if ( 0 === strpos( $chosen_shipping_methods, 'local_pickup' ) ): ?>
$(b).fadeOut(); // Once DOM is loaded
<?php endif; ?>
// On live "change event
$('form.checkout').on('change','input[name^="shipping_method"]',function() {
var c = $(this).val();
if ( c.match('^local_pickup') )
$(b).fadeOut();
else
$(b).fadeIn();
});
});
</script>
<?php
}
Code goes in function.php file of the active child theme (or active theme).
Tested and works. It shows / hide the 2 radio buttons + their labels, depending if "local_pickup" is the chosen Shipping Method…
Update (related to your comment)
May be you should try something like this:
add_action( 'woocommerce_before_checkout_shipping_form', 'custom_shipping_radio_buttons', 10, 1 );
function custom_shipping_radio_buttons( $checkout ) {
woocommerce_form_field( 'shipping_type', array(
'type' => 'radio',
'class' => array( 'form-row-wide' ),
'label' => __('收件方式 *'),
'options' => array(
'shipping_1' => __('全家店到店'),
'shipping_2' => __('指定地址'),
'shipping_3' => __('自行取貨'),
),
), $checkout->get_value( 'shipping_type' ) );
// HERE below define your shipping "flat rates" method IDs in the array
$other_method_ids = array( 'flat_rate:09', 'flat_rate:10', 'flat_rate:11', 'flat_rate:12' );
$local_pickup = 'local_pickup';
// Get the chosen shipping method
$chosen_shipping = WC()->session->get( 'chosen_shipping_methods' )[0];
// Get the chosen shipping method ID
$chosen_shipping_expl = explode( ':', $chosen_shipping );
$chosen_method_id = $chosen_shipping_expl[0];
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
var a = 'shipping_type_shipping_',
b = 'label[for="'+a+'1"],label[for="'+a+'2"],#'+a+'1,#'+a+'2',
c = <?php echo json_encode( $other_method_ids ); ?>; // array of shipping methods ids
// Once DOM is loaded
<?php if ( $chosen_method_id === $local_pickup || in_array( $chosen_shipping, $other_method_ids) ): ?>
$(b).fadeOut();
<?php endif; ?>
// On live "change event
$('form.checkout').on('change','input[name^="shipping_method"]',function() {
var d = $(this).val();
console.log(e);
if ( e.match('^local_pickup') || $.inArray(d, c) !== -1 )
$(b).fadeOut();
else
$(b).fadeIn();
});
});
</script>
<?php
}
Tested and works.
I tried to add the codes for the other options with your codes. However, it cannot works well.
$chosen_shipping_methods_2 = WC()->session->get( 'chosen_shipping_methods' )[0];
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
var d = 'shipping_type_shipping_',
e ='label[for="'+d+'2"],label[for="'+d+'3"],#'+d+'2,#'+d+'3';
<?php if ( 0 === strpos( $chosen_shipping_methods_2, 'flat_rate:9' ) ): ?>
$(e).fadeOut(); // Once DOM is loaded
<?php endif; ?>
// On live "change event
$('form.checkout').on('change','input[name^="shipping_method"]',function() {
var f = $(this).val();
if ( f.match('^flat_rate:9') )
$(e).fadeOut();
else
$(e).fadeIn();
});
});
</script>
<?php
$chosen_shipping_methods_3 = WC()->session->get( 'chosen_shipping_methods' )[0];
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
var g = 'shipping_type_shipping_',
h = 'label[for="'+g+'1"],label[for="'+g+'3"],#'+g+'1,#'+g+'3';
<?php if ( 0 === strpos( $chosen_shipping_methods_3, 'flat_rate:10', 'flat_rate:11', 'flat_rate:12' ) ): ?>
$(h).fadeOut(); // Once DOM is loaded
<?php endif; ?>
// On live "change event
$('form.checkout').on('change','input[name^="shipping_method"]',function() {
var i = $(this).val();
if ( i.match('^flat_rate:10', '^flat_rate:11', '^flat_rate:12') )
$(h).fadeOut();
else
$(h).fadeIn();
});
});
</script>
<?php

WooCommerce Shipping Methods for Plugin Development

I've been endlessly trying to add a shipping method to the following code but with no luck. Could a good samaritan please offer some assistance.
I just need to add a shipping method.
Basically what the code does is add the fields to the shipping zones section in woo commerce setting | Shipping Zones.
I'd like to add a [flat rate to the zone that is created]
I've searched everywhere but with no luck. :-(
<?php
//Creating the Zone.
function DW_shipping_zone_init() {
// Getting the Shipping object
$available_zones = WC_Shipping_Zones::get_zones();
// Get all WC Countries
$all_countries = WC()->countries->get_countries();
//Array to store available names
$available_zones_names = array();
// Add each existing zone name into our array
foreach ($available_zones as $zone ) {
if( !in_array( $zone['zone_name'], $available_zones_names ) ) {
$available_zones_names[] = $zone['zone_name'];
}
}
// Check if our zone 'C' is already there
if( ! in_array( 'C', $available_zones_names ) ){
// Create an empty object
$zone_za = new stdClass();
//Add null attributes
$zone_za->zone_id = null;
$zone_za->zone_order =null;
// Add shipping zone name
$zone_za->zone_name = 'C';
// Instantiate a new shipping zone with our object
$new_zone_za = new WC_Shipping_Zone( $zone_za );
// Add South Africa as location
$new_zone_za->add_location( 'ZA', 'country' );
// Save the zone, if non existent it will create a new zone
$new_zone_za->save();
// Add our shipping method to that zone
$new_zone_za->add_shipping_method( 'some_shipping_method' );
}
add_action( 'init', 'DW_shipping_zone_init' );
You can create plugin for this:
Create one folder inside wp-content/pluigins/ with name "woocommerce-fast-delivery-system"
Inside "woocommerce-fast-delivery-system" folder, create two files as displayed below:
1. fast-delivery-shipping-method.php
<?php
/*
Plugin Name: WooCommerce Fast Delivery Shipping Method
*/
/**
* Check if WooCommerce is active
*/
$active_plugins = apply_filters( 'active_plugins', get_option( 'active_plugins' ) );
if ( in_array( 'woocommerce/woocommerce.php', $active_plugins) ) {
add_filter( 'woocommerce_shipping_methods', 'add_fast_delivery_shipping_method' );
function add_fast_delivery_shipping_method( $methods ) {
$methods[] = 'WC_Fast_Delivery_Shipping_Method';
return $methods;
}
add_action( 'woocommerce_shipping_init', 'fast_delivery_shipping_method_init' );
function fast_delivery_shipping_method_init(){
require_once 'class-fast-delivery-shipping-method.php';
}
}
2. class-fast-delivery-shipping-method.php
<?php
class WC_Fast_Delivery_Shipping_Method extends WC_Shipping_Method{
public function __construct(){
$this->id = 'fast_delivery_shipping_method';
$this->method_title = __( 'Fast Delivery Shipping Method', 'woocommerce' );
// Load the settings.
$this->init_form_fields();
$this->init_settings();
// Define user set variables
$this->enabled = $this->get_option( 'enabled' );
$this->title = $this->get_option( 'title' );
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
public function init_form_fields(){
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable Fast Delivery Shipping', 'woocommerce' ),
'default' => 'yes'
),
'title' => array(
'title' => __( 'Method Tittle', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'Fast Delivery Shipping', 'woocommerce' ),
)
);
}
public function is_available( $package ){
foreach ( $package['contents'] as $item_id => $values ) {
$_product = $values['data'];
$weight = $_product->get_weight();
if($weight > 10){
return false;
}
}
return true;
}
public function calculate_shipping($package){
//get the total weight and dimensions
$weight = 0;
$dimensions = 0;
foreach ( $package['contents'] as $item_id => $values ) {
$_product = $values['data'];
$weight = $weight + $_product->get_weight() * $values['quantity'];
$dimensions = $dimensions + (($_product->length * $values['quantity']) * $_product->width * $_product->height);
}
//calculate the cost according to the table
switch ($weight) {
case ($weight < 1):
switch ($dimensions) {
case ($dimensions <= 1000):
$cost = 3;
break;
case ($dimensions > 1000):
$cost = 4;
break;
}
break;
case ($weight >= 1 && $weight < 3 ):
switch ($dimensions) {
case ($dimensions <= 3000):
$cost = 10;
break;
}
break;
case ($weight >= 3 && $weight < 10):
switch ($dimensions) {
case ($dimensions <= 5000):
$cost = 25;
break;
case ($dimensions > 5000):
$cost = 50;
break;
}
break;
}
// send the final rate to the user.
$c= $this->add_rate( array(
'id' => $this->id,
'label' => $this->title,
'cost' => $cost
));
}
}

Categories