Fabrizio Sabato
PHP Back-End/Front-End & Drupal Developer
Is the owner, creator and administrator of Deskema Brand and its Website. 
 
He is a PHP developer with several years of experience on web applications development.
 
He has recently changed his vision and moved to Drupal CMS to create Web Sites and Symfony for Custom Web Apps.

Codility Certified 2012

Multi-level Country State City cascading select in Wordpress

One of the most common features that you can find within an international web project is the classic multi-level cascading Country -> State/Region -> City select.
Assuming that for any kind of problem there are always "N" solutions, I don't want to present this one as the top one but rather as (imho) a good one to achieve the goal with a scalable and easy to manage approach.
Several days ago I faced a problem with WordPress CMS and a multi-level cascading (Country >> State/Region >> City) select implementation for an international B2B proj.
Unfortunately I didn't find any good plugin going into the right direction so, after groping in the dark for a couple of days, I decided to write a 'possible' solution by myself.

This solution is a combination of PHP, jQuery, Ajax and JSON (to load related sub-items without a page reload), DB MySql and, of course, WordPress. 
It's not a plug-in but a set of functions placed into function.php theme file.

First of all you need to buy and download the [ Country, State, City ] DB from here (this tutorial is designed for the $9 really basic DB version).
Since it's not for free I can't provide for the Database with this article. Sorry :-P

Ok! Assuming that you have bought it and you have already integrated within your MySql DB, let's move to the next step.
What we need now is to create a custom js file (if you want of course) otherwise you can use any other js file of your own theme.
In case, to integrate the new js file you need to use the following snippet of code:

  1. /**
  2.  * Proper way to enqueue scripts and styles
  3.  */
  4. function wpjobus_scripts() {
  5. wp_enqueue_script( 'countryField', get_template_directory_uri() . '/js/countryField.js', array(), '1.0.0', true );
  6. }
  7.  
  8. add_action( 'wp_enqueue_scripts', 'wpjobus_scripts' );
Next step is editing the files wp-content/themes/your_theme/js/countryField.js and wp-content/themes/your_theme/functions.php

countryField.js
  1. /* JS File for Country Field */
  2. (function($) {
  3.  
  4. var country = $("select[name*='countryCode']");
  5. var state = $("select[name*='stateCode']");
  6.  
  7. if (country.length) {
  8. country.change(function() {
  9.  
  10. var $this = $(this);
  11.  
  12. get_states($(this).val(), function(response) {
  13.  
  14. var obj = JSON.parse(response);
  15. var len = obj.length;
  16. var $stateValues = '';
  17.  
  18. $("select[name*='stateCode']").empty();
  19. $("select[name*='cityNameAscii']").empty();
  20. for (i = 0; i < len; i++) {
  21. var mystate = obj[i];
  22. $stateValues += '<option value="'+mystate.country_code+'-'+mystate.state_code+'">'+mystate.states+'</option>';
  23. }
  24. $("select[name*='stateCode']").append($stateValues);
  25.  
  26. });
  27. /* JSON populate Region/State Listbox */
  28. });
  29. }
  30.  
  31.  
  32. if (state.length) {
  33. state.change(function() {
  34.  
  35. var $this = $(this);
  36.  
  37. get_cities($(this).val(), function(response) {
  38.  
  39. var obj = JSON.parse(response);
  40. var len = obj.length;
  41. var $cityValues = '';
  42.  
  43. $("select[name*='cityNameAscii']").empty();
  44. for (i = 0; i < len; i++) {
  45. var mycity = obj[i];
  46. $cityValues += '<option value="'+mycity.city_name+'">'+mycity.city_name+'</option>';
  47. }
  48. $("select[name*='cityNameAscii']").append($cityValues);
  49.  
  50. });
  51.  
  52. });
  53. /* JSON populate Cities Listbox */
  54. }
  55.  
  56.  
  57.  
  58.  
  59. function get_states(countryCODE, callback) {
  60.  
  61. var data = {
  62. action: 'get_states_call',
  63. country_code: countryCODE
  64. };
  65. $.post( ajaxurl, data, function(response) {
  66. callback(response);
  67. });
  68. }
  69.  
  70. function get_cities(rowCODE, callback) {
  71.  
  72. var data = {
  73. action: 'get_cities_call',
  74. row_code: rowCODE
  75. };
  76. $.post( ajaxurl, data, function(response) {
  77. callback(response);
  78. });
  79. }
  80.  
  81. })(jQuery);

functions.php
  1. <?php
  2. /**
  3.  * Set admin-ajax.php on the front side (by default it is available only for Backend)
  4.  */
  5. add_action('wp_head','pluginname_ajaxurl');
  6. function pluginname_ajaxurl() {
  7. ?>
  8. <script type="text/javascript">
  9. var ajaxurl = "<?php echo admin_url('admin-ajax.php'); ?>";
  10. </script>
  11. <?php
  12. }
  13.  
  14.  
  15. /**
  16.  * Fill the countries select
  17.  * @return Array
  18.  */
  19.  
  20. function populate_country_select($selectedCountry = null)
  21. {
  22. global $wpdb;
  23. $db = $wpdb->get_results("SELECT * FROM ".$wpdb->prefix."citydb group by country_code order by country ASC");
  24.  
  25. $items = array();
  26.  
  27. if (null==$selectedCountry)
  28. $items[]='-';
  29.  
  30. foreach( $db as $data ) {
  31. $items[$data->country_code] = $data->country;
  32. }
  33. return $items;
  34. }
  35.  
  36.  
  37. /**
  38.  * Get states by related Country Code
  39.  * @return JSON Object
  40.  */
  41.  
  42. function get_states_call()
  43. {
  44. $country_code = $_POST['country_code'];
  45.  
  46. global $wpdb;
  47. $db = $wpdb->get_results("SELECT * FROM ".$wpdb->prefix."citydb WHERE country_code = '".$country_code."' group by state_code order by states ASC");
  48.  
  49. $items = array();
  50. $items[0]['country_code'] = "";
  51. $items[0]['state_code'] = "";
  52. $items[0]['states'] = '-';
  53.  
  54. $i = 1;
  55. foreach( $db as $data ) {
  56. $items[$i]['country_code'] = $data->country_code;
  57. $items[$i]['state_code'] = $data->state_code;
  58. if( $data->states != 'N/A' ) {
  59. $items[$i]['states'] = $data->states;
  60. } else {
  61. $items[$i]['states'] = $data->country;
  62. }
  63. $i++;
  64. }
  65. //return $items;
  66. echo json_encode($items);
  67. die();
  68. }
  69.  
  70. /**
  71.  * Get cities by related State Code or Country Code (IF State code == "00" or States == 'N/A')
  72.  * @return JSON Object
  73.  */
  74.  
  75. function get_cities_call()
  76. {
  77. if( trim($_POST['row_code']) ) {
  78. $codes = explode('-', $_POST['row_code']);
  79. $country_code = $codes[0];
  80. $state_code = $codes[1];
  81.  
  82. global $wpdb;
  83.  
  84. if( $state_code == '00' ) {
  85. $db = $wpdb->get_results("SELECT * FROM ".$wpdb->prefix."citydb WHERE country_code = '".$country_code."' order by city_name_ascii ASC");
  86. } else {
  87. $db = $wpdb->get_results("SELECT * FROM ".$wpdb->prefix."citydb WHERE state_code = '".$state_code."' AND country_code='".$country_code."' order by city_name_ascii ASC");
  88. }
  89. $items = array();
  90.  
  91. $items[0]['id'] = "";
  92. $items[0]['city_name'] = '-';
  93. $i = 1;
  94. foreach( $db as $data )
  95. {
  96. $items[$i]['id'] = $data->state_code;
  97. $items[$i]['city_name'] = $data->city_name_ascii;
  98. $i++;
  99. }
  100. //return $items;
  101. echo json_encode($items);
  102. die();
  103. }
  104. }
  105.  
  106. add_action('wp_ajax_get_states_call', 'get_states_call');
  107. add_action('wp_ajax_nopriv_get_states_call', 'get_states_call');
  108.  
  109. add_action('wp_ajax_get_cities_call', 'get_cities_call');
  110. add_action('wp_ajax_nopriv_get_cities_call', 'get_cities_call');
Now, assuming that you have a page and this page is associated with a template-your-page.php file, open it and add HTML fields:
  1. <h3><?php _e( 'Select Country:', 'themesdojo' ); ?></h3>
  2. <select name="countryCode" id="countryCode" class="countrySelect">
  3. <?php foreach(populate_country_select() as $key=>$country) { ?>
  4. <option value="<?php echo $key ?>"><?php echo $country ?></option>
  5. <?php } ?>
  6. </select>
  7.  
  8. <h3><?php _e( 'Select State/Region:', 'themesdojo' ); ?></h3>
  9. <select name="stateCode" id="stateCode" class="countrySelect"><select>
  10.  
  11. <h3><?php _e( 'Select City:', 'themesdojo' ); ?></h3>
  12. <select name="cityNameAscii" id="cityNameAscii" class="countrySelect"><select>
That's it! :-) 
Now you can run the script and check for the result.