Woocommerce check-out page fields customization

Woocommerce_fields_example
Customized billing field example

Woocommerce check-out page fields customization

I am working on a Woocommerce website. Each site owner would obviously like his website to be different than others and as programmers we should be able to fulfil our client’s request to maximum possible logical extent.
Coming down to the problem, my client being from India wanted the check-out fields to be customized in a certain way. If we go and look up the Woocommerce documentation we will find their recommended way is to use filters and hooks in your ‘functions.php’ file and remove some undesired fields or else put some new fields. They also give you steps to add some new fields but It goes to so much only. You may have a different order in terms of fields’ positioning (which was in my client’s case), there is no help to my utter surprise on the web on how to achieve this. Me being a programmer who has been customizing different CMS and ecommerce systems could go through the class files and achieve my desired results. For all those people who have been looking for a solution for the same (I know there are quite a few, as I was querying through Google many people asking the same question and not getting help).
The solution:
The fields in the checkout page comes from the /classes/class-wc-countries.php file.
Recommended approach:
First take a back up of the file and then within your template create a Woocommerce folder and put your own over-ridden wc-countries.php file.
If you have little php knowledge you would understand what I have done just by looking at the files code:

function get_allowed_countries() {

asort( $this->countries );

if ( get_option('woocommerce_allowed_countries') !== 'specific' )
return $this->countries;

$allowed_countries = array();

$allowed_countries_raw = get_option( 'woocommerce_specific_allowed_countries' );

foreach ($allowed_countries_raw as $country)
$allowed_countries[$country] = $this->countries[$country];

return $allowed_countries;
}

/**
* get_allowed_country_states function.
*
* @access public
* @return array
*/
function get_allowed_country_states() {

if ( get_option('woocommerce_allowed_countries') !== 'specific' )
return $this->states;

$allowed_states = array();

$allowed_countries_raw = get_option( 'woocommerce_specific_allowed_countries' );

foreach ( $allowed_countries_raw as $country )
if ( ! empty( $this->states[ $country ] ) )
$allowed_states[ $country ] = $this->states[ $country ];

return $allowed_states;
}

/**
* Gets an array of countries in the EU.
*
* @access public
* @return array
*/
function get_european_union_countries() {
return array('AT', 'BE', 'BG', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HU', 'IE', 'IT', 'LT', 'LU', 'LV', 'MT', 'NL', 'PL', 'PT', 'RO', 'SE', 'SI', 'SK');
}

/**
* Gets the correct string for shipping - ether 'to the' or 'to'
*
* @access public
* @return string
*/
function shipping_to_prefix() {
global $woocommerce;
$return = '';
if (in_array($woocommerce->customer->get_shipping_country(), array( 'GB', 'US', 'AE', 'CZ', 'DO', 'NL', 'PH', 'USAF' ))) $return = __('to the', 'woocommerce');
else $return = __('to', 'woocommerce');
return apply_filters('woocommerce_countries_shipping_to_prefix', $return, $woocommerce->customer->get_shipping_country());
}

/**
* Prefix certain countries with 'the'
*
* @access public
* @return string
*/
function estimated_for_prefix() {
$return = '';
if (in_array($this->get_base_country(), array( 'GB', 'US', 'AE', 'CZ', 'DO', 'NL', 'PH', 'USAF' ))) $return = __('the', 'woocommerce') . ' ';
return apply_filters('woocommerce_countries_estimated_for_prefix', $return, $this->get_base_country());
}

/**
* Correctly name tax in some countries VAT on the frontend
*
* @access public
* @return string
*/
function tax_or_vat() {
$return = ( in_array($this->get_base_country(), $this->get_european_union_countries()) ) ? __('VAT', 'woocommerce') : __('Tax', 'woocommerce');

return apply_filters( 'woocommerce_countries_tax_or_vat', $return );
}

/**
* Include the Inc Tax label.
*
* @access public
* @return string
*/
function inc_tax_or_vat() {
$return = ( in_array($this->get_base_country(), $this->get_european_union_countries()) ) ? __('(incl. VAT)', 'woocommerce') : __('(incl. tax)', 'woocommerce');

return apply_filters( 'woocommerce_countries_inc_tax_or_vat', $return );
}

/**
* Include the Ex Tax label.
*
* @access public
* @return string
*/
function ex_tax_or_vat() {
$return = ( in_array($this->get_base_country(), $this->get_european_union_countries()) ) ? __('(ex. VAT)', 'woocommerce') : __('(ex. tax)', 'woocommerce');

return apply_filters( 'woocommerce_countries_ex_tax_or_vat', $return );
}

/**
* Get the states for a country.
*
* @access public
* @param mixed $cc country code
* @return array of states
*/
function get_states( $cc ) {
if (isset( $this->states[$cc] )) return $this->states[$cc];
}

/**
* Outputs the list of countries and states for use in dropdown boxes.
*
* @access public
* @param string $selected_country (default: '')
* @param string $selected_state (default: '')
* @param bool $escape (default: false)
* @return void
*/
function country_dropdown_options( $selected_country = '', $selected_state = '', $escape = false ) {

asort($this->countries);

if ( $this->countries ) foreach ( $this->countries as $key=>$value) :
if ( $states = $this->get_states($key) ) :
echo '';
foreach ($states as $state_key=>$state_value) :
echo ''.$value.' — '. ($escape ? esc_js($state_value) : $state_value) .'';
endforeach;
echo '';
else :
echo ''. ($escape ? esc_js( $value ) : $value) .'';
endif;
endforeach;
}

/**
* Outputs the list of countries and states for use in multiselect boxes.
*
* @access public
* @param string $selected_countries (default: '')
* @param bool $escape (default: false)
* @return void
*/
function country_multiselect_options( $selected_countries = '', $escape = false ) {

$countries = $this->get_allowed_countries();

foreach ( $countries as $key => $val ) {

echo '' . ( $escape ? esc_js( $val ) : $val ) . '';

if ( $states = $this->get_states( $key ) ) {
foreach ($states as $state_key => $state_value ) {

echo '' . ( $escape ? esc_js( $val . ' > ' . $state_value ) : $val . ' > ' . $state_value ) . '';

}
}

}
}

/**
* Get country address formats
*
* @access public
* @return array
*/
function get_address_formats() {

if (!$this->address_formats) :

// Common formats
$postcode_before_city = "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}";

// Define address formats
$this->address_formats = apply_filters('woocommerce_localisation_address_formats', array(
'default' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}\n{postcode}\n{country}",
'AU' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {state} {postcode}\n{country}",
'AT' => $postcode_before_city,
'BE' => $postcode_before_city,
'CH' => $postcode_before_city,
'CN' => "{country} {postcode}\n{state}, {city}, {address_2}, {address_1}\n{company}\n{name}",
'CZ' => $postcode_before_city,
'DE' => $postcode_before_city,
'FI' => $postcode_before_city,
'DK' => $postcode_before_city,
'FR' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city_upper}\n{country}",
'HK' => "{company}\n{first_name} {last_name_upper}\n{address_1}\n{address_2}\n{city_upper}\n{state_upper}\n{country}",
'HU' => "{name}\n{company}\n{city}\n{address_1}\n{address_2}\n{postcode}\n{country}",
'IS' => $postcode_before_city,
'IS' => $postcode_before_city,
'LI' => $postcode_before_city,
'NL' => $postcode_before_city,
'NZ' => "{name}\n{company}\n{address_1}\n{address_2}\n{city} {postcode}\n{country}",
'NO' => $postcode_before_city,
'PL' => $postcode_before_city,
'SK' => $postcode_before_city,
'SI' => $postcode_before_city,
'ES' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city}\n{state}\n{country}",
'SE' => $postcode_before_city,
'TR' => "{name}\n{company}\n{address_1}\n{address_2}\n{postcode} {city} {state}\n{country}",
'US' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}, {state} {postcode}\n{country}",
'VN' => "{name}\n{company}\n{address_1}\n{city}\n{country}",
));
endif;

return $this->address_formats;
}

/**
* Get country address format
*
* @access public
* @param array $args (default: array())
* @return string address
*/
function get_formatted_address( $args = array() ) {

$args = array_map('trim', $args);

extract( $args );

// Get all formats
$formats = $this->get_address_formats();

// Get format for the address' country
$format = ($country && isset($formats[$country])) ? $formats[$country] : $formats['default'];

// Handle full country name
$full_country = (isset($this->countries[$country])) ? $this->countries[$country] : $country;

// Country is not needed if the same as base
if ( $country == $this->get_base_country() ) $format = str_replace('{country}', '', $format);

// Handle full state name
$full_state = ($country && $state && isset($this->states[$country][$state])) ? $this->states[$country][$state] : $state;

// Substitute address parts into the string
$replace = apply_filters('woocommerce_formatted_address_replacements', array(
'{first_name}' => $first_name,
'{last_name}' => $last_name,
'{name}' => $first_name . ' ' . $last_name,
'{company}' => $company,
'{address_1}' => $address_1,
'{address_2}' => $address_2,
'{city}' => $city,
'{state}' => $full_state,
'{postcode}' => $postcode,
'{country}' => $full_country,
'{first_name_upper}' => strtoupper($first_name),
'{last_name_upper}' => strtoupper($last_name),
'{name_upper}' => strtoupper($first_name . ' ' . $last_name),
'{company_upper}' => strtoupper($company),
'{address_1_upper}' => strtoupper($address_1),
'{address_2_upper}' => strtoupper($address_2),
'{city_upper}' => strtoupper($city),
'{state_upper}' => strtoupper($full_state),
'{postcode_upper}' => strtoupper($postcode),
'{country_upper}' => strtoupper($full_country),
));

$formatted_address = str_replace( array_keys($replace), $replace, $format );

// Clean up white space
$formatted_address = preg_replace('/ +/', ' ', trim($formatted_address));
$formatted_address = preg_replace('/\n\n+/', "\n", $formatted_address);

// Add html breaks
$formatted_address = nl2br($formatted_address);

// We're done!
return $formatted_address;
}

/**
* Get country locale settings
*
* @access public
* @return array
*/
function get_country_locale() {
if ( ! $this->locale ) {

// Locale information used by the checkout
$this->locale = apply_filters('woocommerce_get_country_locale', array(
'AF' => array(
'state' => array(
'required' => false,
),
),
'AT' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'BE' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false,
),
),
'BI' => array(
'state' => array(
'required' => false,
),
),
'CA' => array(
'state' => array(
'label' => __('Province', 'woocommerce'),
'placeholder' => __('Province', 'woocommerce')
)
),
'CH' => array(
'postcode_before_city' => true,
'state' => array(
'label' => __('Canton', 'woocommerce'),
'placeholder' => __('Canton', 'woocommerce'),
'required' => false
)
),
'CL' => array(
'city' => array(
'required' => false,
),
'state' => array(
'label' => __('Municipality', 'woocommerce'),
'placeholder' => __('Municipality', 'woocommerce')
)
),
'CN' => array(
'state' => array(
'label' => __('Province', 'woocommerce'),
'placeholder' => __('Province', 'woocommerce')
)
),
'CO' => array(
'postcode' => array(
'required' => false
)
),
'CZ' => array(
'state' => array(
'required' => false
)
),
'DE' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'DK' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'FI' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'FR' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'HK' => array(
'postcode' => array(
'required' => false
),
'city' => array(
'label' => __('Town/District', 'woocommerce'),
'placeholder' => __('Town/District', 'woocommerce')
),
'state' => array(
'label' => __('Region', 'woocommerce'),
'placeholder' => __('Region', 'woocommerce')
)
),
'HU' => array(
'state' => array(
'required' => false
)
),
'IS' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'IL' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'NL' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'NZ' => array(
'state' => array(
'required' => false
)
),
'NO' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'PL' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'RO' => array(
'state' => array(
'required' => false
)
),
'SG' => array(
'state' => array(
'required' => false
)
),
'SK' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'SI' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'ES' => array(
'postcode_before_city' => true,
'state' => array(
'label' => __('Province', 'woocommerce'),
'placeholder' => __('Province', 'woocommerce')
)
),
'LI' => array(
'postcode_before_city' => true,
'state' => array(
'label' => __('Municipality', 'woocommerce'),
'placeholder' => __('Municipality', 'woocommerce'),
'required' => false
)
),
'LK' => array(
'state' => array(
'required' => false
)
),
'SE' => array(
'postcode_before_city' => true,
'state' => array(
'required' => false
)
),
'TR' => array(
'postcode_before_city' => true,
'state' => array(
'label' => __('Province', 'woocommerce'),
'placeholder' => __('Province', 'woocommerce')
)
),
'US' => array(
'postcode' => array(
'label' => __('Zip', 'woocommerce'),
'placeholder' => __('Zip', 'woocommerce')
),
'state' => array(
'label' => __('State', 'woocommerce'),
'placeholder' => __('State', 'woocommerce')
)
),
'GB' => array(
'postcode' => array(
'label' => __('Postcode', 'woocommerce'),
'placeholder' => __('Postcode', 'woocommerce')
),
'state' => array(
'label' => __('County', 'woocommerce'),
'placeholder' => __('County', 'woocommerce'),
'required' => false
)
),
'VN' => array(
'state' => array(
'required' => false
),
'postcode' => array(
'required' => false,
'hidden' => true
),
'address_2' => array(
'required' => false,
'hidden' => true
)
),
));

$this->locale = array_intersect_key( $this->locale, $this->get_allowed_countries() );

$this->locale['default'] = apply_filters('woocommerce_get_country_locale_default', array(
'address_2' => array(
'required' => false
),
'postcode' => array(
'label' => __('Postcode/Zip', 'woocommerce'),
'placeholder' => __('Postcode/Zip', 'woocommerce'),
'required' => true
),
'city' => array(
'label' => __('Town/City', 'woocommerce'),
'placeholder' => __('Town/City', 'woocommerce'),
'required' => true
),
'state' => array(
'label' => __('State', 'woocommerce'),
'placeholder' => __('State', 'woocommerce'),
'required' => true
)
));

// Filter default AND shop base locales to allow overides via a single function. These will be used when changing countries on the checkout
if ( ! isset( $this->locale[ $this->get_base_country() ] ) ) $this->locale[ $this->get_base_country() ] = $this->locale['default'];

$this->locale['default'] = apply_filters( 'woocommerce_get_country_locale_base', $this->locale['default'] );
$this->locale[ $this->get_base_country() ] = apply_filters( 'woocommerce_get_country_locale_base', $this->locale[ $this->get_base_country() ] );

}

return $this->locale;

}
function get_address_fields( $country, $type = 'billing_' ) {
$locale = $this->get_country_locale();

$fields = array(
'first_name' => array(
'label' => __('First Name', 'woocommerce'),
'placeholder' => _x('First Name', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first'),
),
'last_name' => array(
'label' => __('Last Name', 'woocommerce'),
'placeholder' => _x('Last Name', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first'),
),
'email' => array(
'label' => __('Email Address', 'woocommerce'),
'placeholder' => _x('Email Address', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first'),
),
'phone' => array(
'label' => __('Phone', 'woocommerce'),
'placeholder' => _x('Phone', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first'),
),

'address_1' => array(
'type' => 'textarea',
'label' => __('Address', 'woocommerce'),
'placeholder' => _x('Address', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first'),
),

'city' => array(
'label' => __('Town/City', 'woocommerce'),
'placeholder' => _x('Town/City', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first'),
),
'postcode' => array(
'label' => __('Postcode/Zip', 'woocommerce'),
'placeholder' => _x('Postcode/Zip', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first', 'update_totals_on_change'),
),
'state' => array(
'type' => 'state',
'label' => __('State', 'woocommerce'),
'placeholder' => _x('State', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first', 'update_totals_on_change'),
),
'country' => array(
'type' => 'country',
'label' => __('Country', 'woocommerce'),
'placeholder' => _x('Country', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first', 'update_totals_on_change', 'country_select'),
)

);

// Prepend field keys
$address_fields = array();

foreach ( $fields as $key => $value ) {
$address_fields[$type . $key] = $value;
}

/*// Billing/Shipping Specific
if ( $type == 'billing_' ) {

$address_fields['billing_email'] = array(
'label' => __('Email Address', 'woocommerce'),
'placeholder' => _x('Email Address', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-first')
);
$address_fields['billing_phone'] = array(
'label' => __('Phone', 'woocommerce'),
'placeholder' => _x('Phone', 'placeholder', 'woocommerce'),
'required' => true,
'class' => array('form-row-last'),
'clear' => true
);<?php */

// Return
return $address_fields;
}
}

/**
* woocommerce_countries class.
*
* @extends WC_Countries
* @deprecated 1.4
* @package WooCommerce/Classes
*/
class woocommerce_countries extends WC_Countries {
public function __construct() {
_deprecated_function( 'woocommerce_countries', '1.4', 'WC_Countries()' );
parent::__construct();
}
}

The real work is done at the Apply fields and get the array section where I have over-ridden the default template files and created my desired files. Like Asian countries customers would prefer the Address field to be a text area rather than a textbox which I have changed accordingly. Similarly the client may require single column address fields rather than double column; to achieve the same I have appended the { ‘class’ => array(‘form-row-first’), } instead of the default form – row – second class.
If you need any further help in styling the elements and CSS selector classes you can write to me at shakeel.osmani@gmail.com . Apart from this if you want a complete custom solution then I provide professional services and can get your site up and running along with payment (Next Blog will be on the same) gateway running on SSL.

Thanks & Regards
Shakeel Osmani
Contact: shakeel.osmani@gmail.com

7 thoughts on “Woocommerce check-out page fields customization

  1. greetings. i hope you will reply. this is very important. i am currently making a shipping plugin for woocommerce. was having issue on how to get the value of the field city/town which is default field in woocommerce checkout page. can you help me?

  2. Hey Man, I’m trying to change the appearance of “select a state” on the cart.php and checkout.php pages. I have made other customizations with Filters & Hooks, but I can’t figure out how to modify this text with filters or hooks, and am starting to believe it’s not possible.

    I have been lead to believe that it’s possible to do this with “translations”, and I have installed POEdit in an attempt to modify and replace the required file: woocommerce.pot located in -> /woocommerce/i18n/languages

    But after overwriting the file, and refreshing the page ( also clearing the browser cache ) it still shows “select a state” instead of “province”.

    I’m really at wits end with this one, and I need this done by the end of day tomorrow, hopefully you can help me figure this out, because apparently nobody else out there can.

    Thanks in advance,

    Sincerely,

    Chris Coffin

  3. Hi, this is very interesting and worked for me if overriding the core .php file. However, when I try to put the wc-class-countries.php file in my child theme, woocommerce keeps reading its own core file, ignoring my override. I tried every directory (mytheme/woocommerce/includes etc.) but it doesn’t work! Where should I put the class-wc-countries.php file in my child theme? Thanks!

    • Hi, Sorry for the delayed response. I was away busy with some assignments. Actually you need to create inside your child theme in the following manner

      /wp-content/themes/your-child-theme/woocommerce/includes/class-wc-countries.php

      for sure this will work.

      Thanks

  4. Hi, I am trying to add the counties dropdown to my registration fields. I am really struggling to understand how to use the country_dropdown_options to populate a select box. Do you have an example of how this can be done?

    Many thanks

    Helen

    • Hi Helen,

      What I understand from your query is, You want to get list of All the countries and populate a select box. Well if that is the case then it is really simple, the class-wc-countries.php of woocommerce has a method called get_countries which return a sorted list of countries as an array. So you simply can do it this way.

      $countries = array();
      $countries = get_countries();
      <select>
        <?php foreach($countries as $key => $value) { ?>
          <option value="<?php echo $key ?>"><?php echo $value ?></option>
        <?php }?>
      </select>
      

      I hope that was your query and you would be able to resolve it.

  5. Hi, ranatigrina2002
    I use this method to add extra fields in registration page…

    add_action( ‘woocommerce_register_form’, ‘wooc_extra_register_fields’ );
    function wooc_extra_register_fields() {
    ?>

    *
    <input type="text" class="input-text" name="billing_company" id="reg_billing_company" value="” />

    *
    <input type="text" class="input-text" name="billing_pi" id="reg_billing_pi" value="” />

    *
    <input type="text" class="input-text" name="billing_first_name" id="reg_billing_first_name" value="” />

    *
    <input type="text" class="input-text" name="billing_last_name" id="reg_billing_last_name" value="” />

    *
    <input type="text" class="input-text" name="billing_email" id="reg_billing_email" value="” />

    *
    <input type="text" class="input-text" name="billing_phone" id="reg_billing_phone" value="” />

    *
    <input type="text" class="input-text" name="billing_address_1" id="reg_billing_address_1" value="” />

    *
    <input type="text" class="input-text" name="billing_postcode" id="reg_billing_postcode" value="” />

    *
    <input type="text" class="input-text" name="billing_city" id="reg_billing_city" value="” />

    <?php
    }

    ——————————————————————–

    and to add countries and states (synchronized)?

    something like this does nor work…

    *

    $value) { ?>
    <option value="”>

    Can you help me please?

    Leo

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.