County in respect of Country list

Permalink 1 user found helpful
Hi All
I'm integrating a payment system where I need users to put its address details. I've done everything instead the County/State list in respect of Country. Well its like if I choose US, it automatically gets the list of states of US. If any other country, it will show a textbox. Pretty similar like core-commerce billing or shipping page.

I need the same to implemented. Is there any code or how-to available in forum. Or is there anybody who can help me to solve this.

Thanks in advance!

Rony

ronyDdeveloper
 
bbeng89 replied on at Permalink Best Answer Reply
bbeng89
Hey Rony,
Hopefully this bit of code will help you out. It has a dropdown list of the US states and also a textbox. When the country dropdown is changed the javascript checks if the selected value is 'US'. If it is then it makes the state dropdown visible. If it is anything other than 'US' it hides the state dropdown and shows the state textbox.

<?php
   $stateHelper = Loader::helper('lists/states_provinces');
   $countryHelper = Loader::helper('lists/countries');
   $states = $stateHelper->getStateProvinceArray('US');
   $countries = $countryHelper->getCountries();
?>
Country: 
<select id="country">
    <?php foreach($countries as $abbr=>$fullName):?>
        <option value="<?php echo $abbr ?>" <?php echo $abbr == "US" ? 'selected="selected"' : "" ?>>
            <?php echo $fullName; ?>
        </option>
    <?php endforeach; ?>
</select>
State:
ronyDdeveloper replied on at Permalink Reply
ronyDdeveloper
Thanks so much for this. Really helpful.

Will do some tricks to fetch all available states list in respect of all individual country by Ajax call. Anyway you can write it down as how-to so that people can easily get help from this.

Rony
bbeng89 replied on at Permalink Reply
bbeng89
Glad I could help! I think I will go ahead and write a how-to for this later on this morning. I think I will also include the AJAX necessary to pull the states for the selected country. Anyway, when I get it posted I'll send you the link.
bbeng89 replied on at Permalink Reply
bbeng89
Hey Rony,
Just an FYI - I published a how-to on this topic here:http://www.concrete5.org/documentation/how-tos/developers/filtering...

The how-to is a bit cleaner because I used concrete5's form helpers to generate the dropdown lists. Also the how-to shows how you can use AJAX to filter the states based on the selected country. Hopefully it will be helpful to someone.

Have a good one!

Blake
rc255 replied on at Permalink Reply
rc255
hi bbeng89, I know that this is an old link, but I'm hoping you can help me as this is driving me crazy...

I have followed you tutorial on the above link and I have created a little form to GET the submitted info:
<form method="get">
<?php $fh = Loader::helper('form'); ?>
Country: 
<?php echo $fh->select('country', $countries, 'GB'); ?>
<br/><br/>
State: 
<?php echo $fh->select('stateDropdown', $states, 'IL'); ?>
<input type="text" id="stateTextbox" name="stateTextbox" style="display:none;" />
<script>
    $(document).ready(function(){
        $('#country').change(function(){
            $.ajax({
                url: '<?php echo $this->action('get_states'); ?>',
                type: 'GET',
                data: { country: $(this).val() },


and my controller has:

<?php
class StateFilterController extends Controller{
    public function on_start(){
        $this->stateHelper = Loader::helper('lists/states_provinces');
        $this->countryHelper = Loader::helper('lists/countries');
    }
    public function get_states(){
        $country = $this->get('country');
        if(!empty($country)){
            $states = $this->stateHelper->getStateProvinceArray($country);
            echo json_encode($states);
        }
        exit;
    }
    public function view(){


This all works fine and if, for example, the user selects 'United States' it will change the state dropdown accordingly. On SUBMIT, USA is automtically selected and the states are also successfully loaded. Brilliant!

However, if the user selects a country which does NOT have any pre-entered 'states', for example Ukraine, the 'stateTextbox' appears and they type in the state.

My problem is that on SUBMIT, it comes up with an error:

Warning: Invalid argument supplied for foreach() in /concrete/core/helpers/form.php on line 341


I understand why the error is happening - the controller is loading in 'Ukraine', but form.php isn't able to find any associated 'states', instead of the 'stateTextbox' loading in...

Is there any way 'round this? I'm so close, yet I feel so far away. It would be brilliant to get this to work properly.

I you can help me out, I'd be really grateful as I've been puzzling over this for weeks now :(

Thanks

Rob
bbeng89 replied on at Permalink Reply
bbeng89
Hi Rob,
Can you show me the code that handles the form submission? Wherever in your code that the error is being thrown.

Thanks
rc255 replied on at Permalink Reply
rc255
Hi there, thanks for getting back to me.

I'm not using a 'bespoke' form for my little country/state selector - the entire code on the single page is that above. I do not know how to make a 'bespoke' form to handle just this page :(

The form.php is the default /concrete/core/helpers/form.php and the code referenced in the error message is (line 341).This is the chunk that form.php is referring to:
public function select($key, $optionValues, $valueOrArray = false, $miscFields = array()) {
      $val = $this->getRequestValue($key);
      if (is_array($val)) {
         $valueOrArray = $val[0];
      }
      if ((strpos($key, '[]') + 2) == strlen($key)) {
         $_key = substr($key, 0, strpos($key, '[]'));
         $id = $_key . $this->selectIndex;
      } else {
         $_key = $key;
         $id = $key;
      }
      if (is_array($valueOrArray)) {
         $miscFields = $valueOrArray;
      } else {

I don't know if this is any help. I think I have to produce a different version, but I have no idea how to go about it :(

If you can point me in the right direction, I'd be really happy.

Cheers
bbeng89 replied on at Permalink Reply
bbeng89
So I unfortunately don't have an environment up to test this yet, so hopefully I didn't mess anything up here... but give this a shot. Replace the view() function in your controller with this:

public function view(){
  $search_country_posted = $_GET["country"];
  if (!$search_country_posted){$search_country_posted ='GB';}
    $this->set('countries', $this->countryHelper->getCountries());
    $statesForCountry = $this->stateHelper->getStateProvinceArray($search_country_posted);
    $this->set('states', empty($statesForCountry) ? array() : $statesForCountry);
}


All this does is make sure that $states is initialized to an empty array if no states are found for the selected country. Because right now i think $states is null and it's trying to iterate over a null.

You're also probably going to want to add some logic to the view to show the textbox instead of the dropdown if the $states array is empty. Let me know if you need help with that.

Let me know if that works.

Thanks!

Blake
rc255 replied on at Permalink Reply
rc255
That's brilliant Blake - the error message is no longer cropping up!

The countries dropdown is behaving on 'submit', but as you pointed out, the states dropdown is reverting to an empty 'select' instead of the textBox. I changed the code on the single page for the stateTextbox to:
<? $stateTextboxValue = $_GET["stateTextbox"] ?>
<input type="text" id="stateTextbox" name="stateTextbox" <? if ($stateTextboxValue)
{echo 'value="'.$stateTextboxValue.'"';}; ?> 
style="display:none;" />

so that it populates it with the submitted value,, but it just needs swapping over. Do I need to do something else in the controller to make that happen?

I am so happy that that error message has gone! You are a star mate!
bbeng89 replied on at Permalink Reply
bbeng89
Hey Rob,
Glad that fix worked! Now hiding/showing the dropdown vs. the textbox is just handled through the style="display:none" property.

So we know that if the $states array is empty, it means that there are no states for the currently selected country and instead we want to show the textbox. So in that case, the dropdown should have style="display:none" set, and the textbox should not.

However, if the $states array has values in it, then we want to leave style="display:none" on the textbox, and not on the dropdown.

So here is some code that would do that. Again, this is untested so please forgive me if I made a mistake somewhere. I've simplified things a bit by using the concrete5 html helper to generate the textbox rather than using actual html tags. I just find it a little cleaner/easier.

<?php 
   $stateTextboxValue = $_GET["stateTextbox"];
   echo $fh->select('stateDropdown', $states, 'IL', array('style' => empty($states) ? 'display:none' : '' )); 
   echo $fh->text('stateTextbox', $stateTextboxValue, array('style' => empty($states) ? '' : 'display:none'));
?>


Hopefully this does the trick. Let me know how it goes.

Blake
rc255 replied on at Permalink Reply
rc255
Brilliant Blake, it works a treat! Thank you very much for your help - this has been a headache for me for ages!

Just 2 quick questions...

1. Is it possible to re-order the Countries list so that the most popular at at the top ie. United Sates, United Kingdom then the rest ?

2. I'm trying to implement the above code on my homepage. I have made a pagetype with a handle 'homepage.php' and the controller in /controllers/page_types/homepage.php. This works on any page which I apply the page_type design to APART from the homepage. Does the homepage have some sort of issue with another controller being applied to it. I know that the controller works, but it simply does nothing...

Once again, thanks very much for your time and helping me out :)

Rob
bbeng89 replied on at Permalink Reply
bbeng89
Glad that worked for you!

1. There isn't going to be a super easy way of doing this. In my code, I had the countries dropdown set with 'US' as the default, in this line:

<?php echo $fh->select('country', $countries, 'US'); ?>


It looks like you changed it to have 'GB' (United Kingdom) as the default.

If that isn't what you want and you want it to be actually reordered you're going to have to do some work. I'm sure there is a much more clever way of doing this, but I've written a basic method that will let you pass in an array of country codes you want to appear at the top of the list, and then all the rest will follow in alphabetical order. So you can add this to your controller and then update your view() method. Here is the method and updated view().

protected function getCustomSortedCountries(array $firstCountryCodes)
{
   // array containing all countries
    $allCountries = $this->countryHelper->getCountries();
    $firstCountries = array();
    foreach($firstCountryCodes as $countryCode)
    {
        $firstCountries[$countryCode] = $allCountries[$countryCode];
    }
    // remove the countries in the first array so they aren't duplicated
    $allCountries = array_diff_key($allCountries, $firstCountries);
    // append $allCountries to $firstCountries and return
    return array_merge($firstCountries, $allCountries);
}
public function view()



Hopefully that does the trick.


2. I'm honestly not sure what could be going on here. Have you tried clearing the cache? The homepage shouldn't be any different from any other page. If I think of anything else on this I'll let you know.
YuC replied on at Permalink Reply
thank you, it solves my problem