I can't find the way to ovveride billing state and post code.
How can I edit the other parts of existing billing fields like billing state and post code?
This is what I have in the functions.php file in my child theme (I have included the code affecting the billing part):
<?php
function my_custom_checkout_field( $checkout ) {
global $wpdb;
$check_zone = $wpdb->get_results("select area_name from brick_area where id='".$_SESSION['area']."'",ARRAY_A);
if(!empty($check_zone)){
$check_zoneid = $check_zone['0'];
}
woocommerce_form_field( 'my_field_name', array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'label' => __('Delivery Area'),
'placeholder' => __('Area'),
'readonly' =>'readonly',
'default' => $check_zoneid['area_name']
), $checkout->get_value( 'my_field_name' ));
woocommerce_form_field( 'my_expected_date', array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'required' => true,
'label' => __('Expected Delivery Date'),
'placeholder' => __('Enter expected delivery date.'),
), $checkout->get_value( 'my_expected_date' ));
/*woocommerce_form_field( 'my_expected_time', array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'required' => true,
'label' => __('Expected Delivery Time'),
'placeholder' => __('Enter expected delivery time.'),
), $checkout->get_value( 'my_expected_time' ));*/
woocommerce_form_field( 'site_contact_name', array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'required' => true,
'label' => __('Site Contact Person Name'),
'placeholder' => __('Enter site contact person name.'),
), $checkout->get_value( 'site_contact_name' ));
woocommerce_form_field( 'site_contact_phone', array(
'type' => 'tel',
'class' => array('my-field-class form-row-wide'),
'required' => true,
'label' => __('Site Contact Phone Number'),
'placeholder' => __('Enter site contact phone number.'),
), $checkout->get_value( 'site_contact_phone' ));
}
$fields['billing']['billing_city']['default'] = $_SESSION['cn'];
add_filter( 'woocommerce_default_address_fields' , 'custom_override_default_address_fields' );
function custom_override_default_address_fields( $address_fields ) {
// we are changing here billing_state field to required
$fields['billing']['billing_state']['required'] = true;
return $address_fields;
}
/*$fields['billing']['my_field_name']['default'] = $check_zoneid['area_name'];
$fields['billing']['my_field_name']['label'] = 'Area';*/
return $fields;
}
?>
Thanks
This is the complete way for billing state and billing post code override, keeping the billing selector with options.
Here is the code the fully functional and tested code:
Unsetting billing state and post code checkout fields
add_filter( 'woocommerce_checkout_fields' , 'partial_unsetting_checkout_fields' );
function partial_unsetting_checkout_fields( $fields ) {
unset($fields['billing']['billing_state']);
unset($fields['billing']['billing_postcode']);
return $fields;
}
Reinserting custom billing state and post code checkout fields
add_filter( 'woocommerce_default_address_fields' , 'art_override_default_address_fields' );
function art_override_default_address_fields( $address_fields ) {
// # for state
$address_fields['billing_state']['type'] = 'select';
$address_fields['billing_state']['class'] = array('form-row-wide');
$address_fields['billing_state']['required'] = true;
$address_fields['billing_state']['label'] = __('State', 'my_theme_slug');
$address_fields['billing_state']['placeholder'] = __('Enter state', 'my_theme_slug');
$address_fields['billing_state']['default'] ='Choice 1';
$address_fields['billing_state']['options'] = array(
'option_1' => 'Choice 1',
'option_2' => 'Choice 2',
'option_3' => 'Choice 3'
);
// # for postcode
$address_fields['billing_postcode']['type'] = 'text';
$address_fields['billing_postcode']['class'] = array('form-row-wide');
$address_fields['billing_postcode']['required'] = true;
$address_fields['billing_postcode']['label'] = __('Postcode', 'my_theme_slug');
$address_fields['billing_postcode']['placeholder'] = __('Enter your postcode', 'my_theme_slug');
return $address_fields;
}
Naturally this goes on function.php file of your active child theme or theme
Official reference: WooThemes - Customizing checkout fields using actions and filters
Note concerning the 'class' property
There is 2 ways to handle it:
The field is alone in one line (width 100%), you use: 'form-row-wide'
There is 2 fields side by side on the same line, you use:
'form-row-first' for the first field
'form-row-last' for the second field
//-------------------------- OVERRIDING BILLING STATE FIELD -------------------------------//
//Removing previous one by using unset
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 ) {
unset($fields['billing']['billing_state']);
return $fields;
}
add_filter( 'woocommerce_default_address_fields' , 'art_override_default_address_fields' );
function art_override_default_address_fields( $address_fields ) {
// # for state
$address_fields['Billing_State']['type'] = 'text';
$address_fields['Billing_State']['class'] = array('form-row-wide');
$address_fields['Billing_State']['required'] = true;
$address_fields['Billing_State']['label'] = __('State', 'my_theme_slug');
$address_fields['Billing_State']['placeholder'] = __('Enter state', 'my_theme_slug');
return $address_fields;
}
Related
I am trying to add a city dropdown in my Woocommerce store's admin order edit area. I have tried this code.
add_filter( 'woocommerce_admin_billing_fields' , 'admin_billing_city_select_field' );
function admin_billing_city_select_field( $fields ) {
global $pagenow;
// Only for new order creation
if( $pagenow != 'post-new.php' ) return $fields;
$fields['city'] = array(
'label' => __( 'City', 'woocommerce' ),
'show' => false,
'class' => 'js_field-city select short',
'type' => 'select',
'options' => array(
'' => __( 'Select a city…', 'woocommerce' ),
'Los Angeles' => __( 'Los Angeles', 'woocommerce' ),
'San Antonio' => __( 'San Antonio', 'woocommerce' ),
),
);
return $fields;
}
It's working like this
But I want to add some condition If the state matches then it will show a specific city list. And it should revel the city list after selecting the state. How can I do this?
Edit 1
I have added places as an array. And here is the full code in Github.
global $places;
$places['BD'] = array(
'dhaka' => array(
__('Aam Bagan', 'woocommerce'),
__('12 Tala', 'woocommerce'),
__('Keraniganj Upazila Sadar', 'woocommerce'),
),
'faridpur' => array(
__('Alfadanga', 'woocommerce'),
),
'gazipur' => array(
__('Gazipur Sadar', 'woocommerce'),
__('Kaliakair', 'woocommerce'),
),
'gopalganj' => array(
__('Gopalganj Sadar', 'woocommerce'),
__('Kashiani', 'woocommerce'),
),
'jamalpur' => array(
__('Bakshiganj', 'woocommerce'),
__('Dewanganj', 'woocommerce'),
),
);
function enqueue_scripts()
{
wp_enqueue_script('jquery');
wp_enqueue_script('select2', 'https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js', array('jquery'), '4.0.13', true);
wp_enqueue_script('place-select-js', plugin_dir_url(__FILE__) . 'js/place-select.js', array('jquery', 'select2'), '0.1', true);
}
add_action('admin_enqueue_scripts', 'enqueue_scripts');
add_filter('woocommerce_admin_billing_fields', 'admin_billing_city_select_field');
function admin_billing_city_select_field($fields)
{
global $pagenow, $places;
// Only for new order creation
if ($pagenow != 'post-new.php') return $fields;
$options = array('' => __('Select a city…', 'woocommerce'));
if (isset($places)) {
foreach ($places as $state => $cities) {
foreach ($cities as $city => $city_name) {
$options[$city] = $city_name[0];
}
}
}
$fields['city'] = array(
'label' => __('City', 'woocommerce'),
'show' => false,
'class' => 'js_field-city select short',
'type' => 'select',
'options' => $options,
);
return $fields;
}
The imported jquery file is from #Aurelien - AK Digital's answer. But it's returning only the first item of the array.
If I change $options[$city] = $city_name[0]; to $options[$city] = $city_name; it's returning a string text instead of cities "Array" How can I fix it?
First, for a better user experience, I recommend to move the "State field" before the "City" & "Postcode" fields.
You also may have to think about the amount of data you'll want to load. Will you propose every single US city and town or only a few ? This will conditionned the way data will be load.
But let's assume you will create you're own [short] list.
(All of this code has been tested WP 6.1.1 & WC 7.3.0)
First you need to store that list of cities, including state prefixes, in a WordPress option (ex.: '_wc_cities_by_states') like so :
$state_city_options = array(
'CA-los-angeles' => __( 'Los Angeles', 'woocommerce' ),
'CA-san-antonio' => __( 'San Antonio', 'woocommerce' ),
'WA-seattle' => __( 'Seattle', 'woocommerce' ),
'WA-olympia' => __( 'Olympia', 'woocommerce' ),
);
set_option('_wc_cities_by_states', $state_city_options);
Then, get this custom option to build your city select option list :
add_filter( 'woocommerce_admin_billing_fields' , 'admin_billing_city_select_field' );
function admin_billing_city_select_field( $fields ) {
global $pagenow;
// Get the array of cities by state from WP option
$cities_by_states = get_option('_wc_cities_by_states', )
// Only for new order creation
if( $pagenow != 'post-new.php' || !$cities_by_states ) return $fields;
// Add it to WC city fields
$fields['city'] = array(
'label' => __( 'City', 'woocommerce' ),
'show' => false,
'class' => 'js_field-city select short',
'type' => 'select',
'options' => $cities_by_states;
);
return $fields;
}
Finally, this is the Javascript (jQuery) that will do the trick (NB: Woocommerce use the select2 lib) :
jQuery(document).ready(function ($) {
$(".js_field-city option").hide();
//Required to biding after billing-edit click
$(".edit_address").one("click", function () {
filterCities();
});
//Required to avoid unbiding after Country change
$(".js_field-country").on("select2:select", function (evt) {
filterCities();
});
filterCities = () => {
$(".js_field-state").on("select2:select", function (evt) {
var state = $(this).select2("data")[0].id;
$(".js_field-city option").hide();
$(".js_field-city option[value^='" + state + "']").show();
$(".js_field-city").val(null);
});
};
});
That code works for me, I hope it will work for you as well.
Thanks to let me know !
I cant figure out how to reorder the additional fields, on the checkout page in WooCommerce.
I have added one extra field to the WooCommerce additional information section. I would like to show the time field first then the order notes below it.
This is the code that I am using:
add_filter( 'woocommerce-additional-fields', 'custom_order_fields', 20, 1 );
function custom_order_fields( $fields ) {
$fields['order_comments']['priority'] = 80;
$fields['woocommerce-delivery-time-field']['priority'] = 70;
return $fields;
}
However, this does not have the desired result. Can someone tell me what I'm doing wrong?
If you want to show your custom field first, and then the order notes.
You can either use:
// Add 'delivery time' field before 'order comments'
function filter_woocommerce_checkout_fields( $fields ) {
// Get 'order comments' field
$order_comments = $fields['order']['order_comments'];
// Unset 'order comments' field
unset( $fields['order']['order_comments'] );
// Add 'delivery time' field
$fields['order']['delivery_time'] = array(
'label' => __( 'Delivery time', 'woocommerce' ),
'required' => true,
'type' => 'text',
'class' => array( 'form-row-wide' ),
);
// Add 'order comments' field
$fields['order']['order_comments'] = $order_comments;
return $fields;
}
add_filter( 'woocommerce_checkout_fields' , 'filter_woocommerce_checkout_fields', 10, 1 );
OR use the woocommerce_before_order_notes action hook
function action_woocommerce_before_order_notes( $checkout ) {
// Add field
woocommerce_form_field( 'delivery_time', array(
'type' => 'text',
'class' => array( 'form-row form-row-wide' ),
'label' => __( 'Delivery time', 'woocommerce' ),
'required' => true,
), $checkout->get_value( 'delivery_time' ) );
}
add_action( 'woocommerce_before_order_notes', 'action_woocommerce_before_order_notes', 10, 1 );
You would need to add the field to the WooCommerce Custom Field first before you set the priority likeso.
add_action('woocommerce_checkout_fields', 'add_woocommerce_additional_fields');
// Function to add field
function add_woocommerce_additional_fields( $fields ) {
$fields['order']['delivery_time'] = array(
'type' => 'text',
'label' => __('Delivery time', 'woocommerce'),
'required' => true,
'class' => array('form-row-wide'),
'clear' => true
);
// You can set your priority here
// Just higher than it a bit
$fields['order']['order_comments']['priority'] = 80;
$fields['order']['delivery_time']['priority'] = 70;
return $fields;
}
You can check here for more information on ordering of fields in Woocommerce.
I want to change the type of some fields in my account page, where i want to change City from "textfield" to <select> (list) where I specify only some cities, I don't want users to type anything, do you have any idea on which code shall I edit?
I tried with this but I don't know what to modifie!
I want only three cities to be selected (Paris, London, Algiers)
add_filter( 'woocommerce_billing_fields', 'custom_woocommerce_billing_fields' );
function custom_woocommerce_billing_fields( $fields ) {
$fields['billing_city'] = array(
'label' => __('City', 'woothemes'),
'placeholder' => __('City', 'woothemes'),
'required' => true,
'class' => array('billing-city')
);
return $fields;
}
Thank you in advanced!
Can you please try below code.
add_filter( 'woocommerce_billing_fields', 'custom_woocommerce_billing_fields' );
function custom_woocommerce_billing_fields( $fields ) {
$fields['billing_city'] = array(
'type' => 'select',
'label' => __('City', 'woothemes'),
'placeholder' => __('City', 'woothemes'),
'required' => true,
'class' => array('billing-city'),
'options' => array(
'paris' => __('Paris', 'woothemes' ),
'london' => __('London', 'woothemes' ),
'algiers' => __('Algiers', 'woothemes' )
)
);
return $fields;
}
I am customizing the WooCommerce checkout page fields. I want to add a heading (text) in between the fields. I have reordered the fields like this
add_filter('woocommerce_checkout_fields', 'ac_checkout_reorder_fields');
function ac_checkout_reorder_fields($fields) {
$order = array(
"billing_first_name",
"billing_last_name",
"billing_company",
"billing_email",
"billing_phone",
"billing_address_1",
"billing_address_2",
"billing_postcode",
"billing_country"
);
foreach($order as $field)
{
$ordered_fields[$field] = $fields["billing"][$field];
}
$fields["billing"] = $ordered_fields;
return $fields;
}
Then I added a new heading like this
add_action( 'woocommerce_after_checkout_billing_form', 'add_custom_heading' );
function add_custom_heading( $checkout ) {
echo '<div id="add_custom_heading"><h2>' . __('Custom Heading Here') . '</h2></div>' ;
}
Now the fields are re-arranged and the custom heading is showing. But I want the heading showing just below the name & company fields. With my code, the field is being added below. How I an reposition this in my desired place.
PS: I also tried to add customize the sections of the entire field with this hook woocommerce_checkout_fields adding placeholder and removing the labels. Here are the codes but this also does not help me solve the issue.
function add_wc_custom_fields($fields) {
global $woocommerce;
$countries_obj = new WC_Countries();
$countries = $countries_obj->__get('countries');
$fields['billing']['billing_first_name'] = array(
'label' => '',
'placeholder' => _x('First Name*', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array( 'form-row-first' ),
);
$fields['billing']['billing_last_name'] = array(
'label' => '',
'placeholder' => _x('last Name*', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array( 'form-row-last' ),
);
$fields['billing']['billing_company'] = array(
'label' => '',
'placeholder' => _x('Company Name', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('checkout-billing-company')
);
$fields['billing']['billing_address_1'] = array(
'label' => '',
'placeholder' => _x('Address(Line 1)*', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('checkout-billing-addressL1')
);
$fields['billing']['billing_address_2'] = array(
'label' => '',
'placeholder' => _x('Address(Line 2)*', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('checkout-billing-addressL2')
);
$fields['billing']['billing_email'] = array(
'label' => '',
'placeholder' => _x('Email Address*', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first')
);
$fields['billing']['billing_phone'] = array(
'label' => '',
'placeholder' => _x('Phone Number', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-last')
);
$fields['billing']['billing_city'] = array(
'label' => '',
'placeholder' => _x('Town/City', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-first')
);
$fields['billing']['billing_state'] = array(
'label' => '',
'placeholder' => _x('State/County', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-first')
);
$fields['billing']['billing_postcode'] = array(
'label' => '',
'placeholder' => _x('Zip/Postcode', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-first')
);
$fields['billing']['billing_country'] = array(
'label' => '',
'type' => 'select',
'placeholder' => _x('Country', 'placeholder', 'woocommerce'),
'required' => false,
'class' => array('form-row-last'),
'options' => $countries
);
return $fields;
}
add_filter('woocommerce_checkout_fields', 'add_wc_custom_fields');
we can use the filter 'woocommerce_form_field_' . $type... where $type is the type of the input... in our case billing_company is of type text... this filter returns the html of the field, in our case billing field, billing_company.. the filter has 4 arguments being passed, these are $field, $key, $args, $value... we just need two of these...
add_action( 'woocommerce_form_field_text','reigel_custom_heading', 10, 2 );
function reigel_custom_heading( $field, $key ){
// will only execute if the field is billing_company and we are on the checkout page...
if ( is_checkout() && ( $key == 'billing_company') ) {
$field .= '<p class="form-row form-row-wide">Custom Heading</p>';
}
return $field;
}
Important: If we don’t format it as a paragraph with the class form-row it will be put to the top of all billing fields by Woocommerce (reasons unknown to me). This shows us that this as “Hack” and maybe your goals are achieved better differently.
Instead of hooking into an existing woocommerce_form_field_<field_type> filter such as woocommerce_form_field_text, I prefer to use the woocommerce_form_field_<field_type> filter to create a new field type. This allows us to add HTML output for an additional field type (and therefore we can use HTML output for a heading instead) and we can use this new heading "field type" when modifying checkout fields with the woocommerce_checkout_fields filter.
// Add field type to WooCommerce form field
add_filter( 'woocommerce_form_field_heading','sc_woocommerce_form_field_heading', 10, 4 );
function sc_woocommerce_form_field_heading($field, $key, $args, $value) {
$output = '<h3 class="form-row form-row-wide">'.__( $args['label'], 'woocommerce' ).'</h3>';
echo $output;
}
// Modify checkout fields
add_filter( 'woocommerce_checkout_fields','custom_override_checkout_fields' );
function custom_override_checkout_fields( $fields ) {
$fields['billing']['billing_heading_name'] = array(
'type' => 'heading',
'label' => 'Heading here',
);
Using the above method the solution to the original question would be:
add_filter('woocommerce_checkout_fields', 'ac_checkout_reorder_fields');
function ac_checkout_reorder_fields($fields) {
$fields['billing']['billing_heading_name'] = array(
'type' => 'heading',
'label' => 'Heading here',
);
$order = array(
"billing_first_name",
"billing_last_name",
"billing_heading_name",
"billing_company",
"billing_email",
"billing_phone",
"billing_address_1",
"billing_address_2",
"billing_postcode",
"billing_country"
);
foreach($order as $field) {
$ordered_fields[$field] = $fields["billing"][$field];
}
$fields["billing"] = $ordered_fields;
return $fields;
}
add_filter('woocommerce_form_field', 'addHeadingsInBetweenFormFields', 10, 4);
function addHeadingsInBetweenFormFields($field, $key, $args, $value = null) {
if (is_checkout() & $key === 'billing_first_name') {
$a = '<p class="form-row form-row-wide">Shipping</p>';
return $a . $field;
}
return $field;
}
I've created a custom checkout field, but I would like for it to react dynamically with the Shipping/Billing Country dropdown.
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
function custom_override_checkout_fields( $fields ) {
global $woocommerce;
$ship_country = $woocommerce->customer->get_country();
if ($ship_country !== 'US'){
$fields['billing']['international_catalogue'] = array(
'label' => __('I would like to receive a catalogue', 'woocommerce'),
'required' => false,
'type' => 'checkbox',
'class' => array('my-field-class form-row-wide'),
'default' => 0
);
}
$fields['billing']['email_opt_out'] = array(
'label' => __('Please send me the monthly newsletter <BR>(You can unsubscribe at any time)', 'woocommerce'),
'required' => false,
'type' => 'checkbox',
'class' => array('my-field-class form-row-wide'),
'default' => 1
);
return $fields;
}
This only works the way I want if you reload the checkout page after changing the Country. How do I get this checkbox to react dynamically?