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