Jump to content

[TUTORIAL]Add city as Dropdown in PrestaShop (updated with github files)


shacker
 Share

Recommended Posts

 

In this tutorial we will see step by step how to add the city field as a drop-down menu when creating or modifying an address.

The first step is to create a table with the cities. We will call the table city, and it will have this structure

ta1.jpg.647f9d6d4ba1e60e4ef4c44457d02dac.jpg

The id_states must be given by each state that has your prestashop store. The table would look like this:

ta2.jpg.2156faee70522bad1a8fba9291eb3c10.jpg

The next step is to modify the ps_address table by adding an id_city field.

ta3.thumb.jpg.c7b808f1601c55e6b813479c33731879.jpg

Finally, we will modify the address_format table defining for which countries we will use the droptown format in the city.

 

Just edit the format field, which generally when it contains states looks like this:

ta4.jpg.eac9a8a73023df6822cf98edf19ff310.jpg

And we will change the field city to city: name. Name would add the dropdown:

ta5.jpg.095f9408af2899655b329bc4ba8e1612.jpg

At this point the store will give us errors since classes are missing, so now we must modify the files so that all this works.

First we will create a City.php file in the classes folder that will allow us to work with cities. The file will contain:


class CityCore extends ObjectModel

{

 

   public $id_city;

   public $id_state;

   public $name;

 

  

                protected           $fieldsRequired = array('id_city', 'name');

                protected           $fieldsSize = array('name' => 128, 'id_state' => 10);

                protected           $fieldsValidate = array('name' => 'isGenericName', 'id_state' => 'isUnsignedId', );

 

                protected           $table = 'city';

                protected           $identifier = 'id_city';

  

                private static $_cache_get_cities = array();

  

  

                public function getFields()

                {

                               parent::validateFields();

                               $fields['id_city'] = (int)($this->id_city);

                               $fields['name'] = pSQL($this->name);

                               $fields['id_state'] = pSQL($this->id_state);     

                               return $fields;

                }

  

 

                public function delete()

                {

                               $id = $this->id;

                               parent::delete();

                }  

  

  

                public static function getCities($id_state)

                {

 

                                               $id_city = Db::getInstance()->ExecuteS('

                                               SELECT * FROM `'._DB_PREFIX_.'city`

                                               WHERE `id_state` = '.(int)$id_state.'

         ORDER BY `name`;'

                                               );

                              

 

                               return $id_city;

                }

  

  

   public static function getCityName($id_city)

   {

                               return Db::getInstance()->getValue('

                               SELECT `name` FROM `'._DB_PREFIX_.'city`

                               WHERE `id_city` = '.(int)$id_city

                               ); 

   }

 

}

Then we replace this function in  classes/form/customeraddressform.php :

    public function submit()

    {

        if (!$this->validate()) {

            return false;

        }

 

        $address = new Address(

            $this->getValue('id_address'),

            $this->language->id

        );

 

        

        

        foreach ($this->formFields as $formField) {

        if ($formField->getName() == 'id_city'){

                        $id_city = Db::getInstance()->getRow('

                                   SELECT * FROM `'._DB_PREFIX_.'city`

                                   WHERE `id_city` = '.$formField->getValue().';');

            $address->city = $id_city['name'];

            $address->{$formField->getName()} = $formField->getValue();

        } else {

            $address->{$formField->getName()} = $formField->getValue();

 

        }

      

        }

 

        if (!isset($this->formFields['id_state'])) {

            $address->id_state = 0;

        }

 

        if (empty($address->alias)) {

            $address->alias = $this->translator->trans('My Address', [], 'Shop.Theme.Checkout');

        }

 

 

                                 

        Hook::exec('actionSubmitCustomerAddressForm', array('address' => &$address));

        $this->setAddress($address);

        $this->getPersister()->save(

            $address,

            $this->getValue('token'));

        Db::getInstance()->Execute('UPDATE `'._DB_PREFIX_.'address` SET `city` = \''.$address->city.'\' WHERE id_address = '.$address->id);

        return true;

    }

The following file is the classes / form / CustomerAddressFormatter.php

We will look for this line of states

elseif ($entity === 'State') {

                    if ($this->country->contains_states) {

                        $states = State::getStatesByIdCountry($this->country->id, true);

                        foreach ($states as $state) {

                            $formField->addAvailableValue(

                                $state['id_state'],

                                $state[$entityField]

                            );

                        }

                        $formField->setRequired(true);

                    }

                }

 

and we add this to the end of these lines

 

elseif ($entity === 'city') {

                    $formField->setType('select');

                    $formField->setName('id_' . strtolower($entity));

                        $cities =  State::getCities(315);

                        foreach ($cities as $city) {

                            $formField->addAvailableValue(

                                $city['id_city'],

                                $city[$entityField]

                            );

                        }

                        $formField->setRequired(false);

                   

                }

 

And we add this function to the clases/State.php file

 

                public static function getCities($id_state)

                {

 

                                               $id_city = Db::getInstance()->ExecuteS('

                                               SELECT * FROM `'._DB_PREFIX_.'city`

                                               WHERE `id_state` = '.(int)$id_state.'

         ORDER BY `name`;'

                                               );

                              

 

                               return $id_city;

                }

For these already created files we can always use an override to avoid losing changes when updating PrestaShop

The next file is the classes / Address.php

We will add public variables at the beginning:

 

    public  $id_city;

    public  $cityName;

 

in the function  public static $definition = array(

 

We will add at the end the created field id_city for the address table

'id_city' => 'isUnsignedId',

 

And we add this new function

public function getFields()

    {

        if (isset($this->id))

        $sql = 'select * from '. _DB_PREFIX_ . 'city

        WHERE  id_city= ' . $this->id_city;

        $sql2 = Db::getInstance()->getRow($sql);

        $fields['id_address'] = (int)($this->id);

        $fields['id_customer']     = is_null($this->id_customer) ? 0 : (int)($this->id_customer);

        $fields['id_manufacturer'] = is_null($this->id_manufacturer) ? 0 : (int)($this->id_manufacturer);

        $fields['id_supplier']     = is_null($this->id_supplier) ? 0 : (int)($this->id_supplier);

        $fields['id_country']      = (int)($this->id_country);

        $fields['id_state']        = (int)($this->id_state);

        $fields['alias']           = pSQL($this->alias);

        $fields['company']         = pSQL($this->company);

        $fields['lastname']        = pSQL($this->lastname);

        $fields['firstname']       = pSQL($this->firstname);

        $fields['address1']        = pSQL($this->address1);

        $fields['address2']        = pSQL($this->address2);

        $fields['postcode']        = pSQL($this->postcode);

        $fields['city']            = pSQL($sql2['name']);

        $fields['other']           = pSQL($this->other);

        $fields['phone']           = pSQL($this->phone);

        $fields['phone_mobile']    = pSQL($this->phone_mobile);

        $fields['vat_number']      = pSQL($this->vat_number);

        $fields['dni']             = pSQL($this->dni);

        $fields['deleted']         = (int)($this->deleted);

        $fields['date_add']        = pSQL($this->date_add);

        $fields['date_upd']        = pSQL($this->date_upd);

       

        $fields['id_city']   = is_null($this->id_city) ? 0 : (int)($this->id_city);

        return $fields;

    }

 

And change this function at the end

public static function initialize($id_address = null, $with_geoloc = false) 

 

where this elseif we add the city field

 

            } elseif ($with_geoloc && isset($context->customer->geoloc_id_country)) {

                $address = new Address();

                $address->id_country = (int) $context->customer->geoloc_id_country;

                $address->id_state = (int) $context->customer->id_state;

                $address->id_city = (int) $context->customer->id_city;

                $address->postcode = $context->customer->postcode;

            }

 

 

The next step is to add the javascript code so that it takes the cities when changing state:

For this we will modify the file themes / ourtheme / templates / _partials / javascript.tpl of our theme and we will add this code at the end:

<!-- direcciones -->

 

 

{literal}

<script>

(function(){"use strict";var c=[],f={},a,e,d,b;if(!window.jQuery){a=function(g){c.push(g)};f.ready=function(g){a(g)};e=window.jQuery=window.$=function(g){if(typeof g=="function"){a(g)}return f};window.checkJQ=function(){if(!d()){b=setTimeout(checkJQ,100)}};b=setTimeout(checkJQ,100);d=function(){if(window.jQuery!==e){clearTimeout(b);var g=c.shift();while(g){jQuery(g);g=c.shift()}b=f=a=e=d=window.checkJQ=null;return true}return false}}})();

</script>

{/literal}

 

 

 

{if $page.page_name == "address" or $page.page_name == "order" or  $page.page_name == "checkout"}

                <script type="text/javascript">

$(document).ready(function(){

$(".form-control-select .js-city").last().val();

 

 

 

 

                                                               var mi_ajaxurl                  = '{$urls.base_url}modules/';

                                                               var aux_id_state             = {if isset($smarty.post.id_state) and $smarty.post.id_state <> null}{$smarty.post.id_state}{else}{if isset($customer.addresses[$smarty.get.id_address].id_state)}{$customer.addresses[$smarty.get.id_address].id_state}{else}0{/if}{/if};

                                                               var aux_id_city = {if isset($smarty.post.id_city) and $smarty.post.id_city <> null}{$smarty.post.id_city}{else}{if isset($customer.addresses[$smarty.get.id_address].id_city)}{$customer.addresses[$smarty.get.id_address].id_city}{else}0{/if}{/if};

                                                               var aux_city       = '{if isset($smarty.post.city) and $smarty.post.city <> null}{$smarty.post.city}{else}{if isset($customer.addresses[$smarty.get.id_address].city)}{$customer.addresses[$smarty.get.id_address].city}{else}0{/if}{/if}';

 

 

                                                               {literal}

                                                                                              $(document).ready(function(){

 

              $.urlParam = function(name){

    var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);

    if (results==null) {

       return null;

    }

    return decodeURI(results[1]) || 0;

}

 

                                                                                                              ajaxCity();

 

                                                                                                              $("[name='id_state']").change(function() {

                                                                                                              ajaxCity();

                                                                                                              });

                                                                                                             

                                                                                                              $("[name='id_city']").change(function() {

 

                                                                                                              });

                                                                                                             

 

                                                                                                              function ajaxCity(valueaaa){

                                                                                                                             $.ajax({

                                                                                                                                             type: "GET",

                                                                                                                                             url: mi_ajaxurl+"ajax_addresses/ajax.php?ajaxCity=1&id_state="+$("[name='id_state']").val()+"&aux_id_state="+aux_id_state+"&aux_city="+aux_city,

                                                                                                                                             success: function(r){

                                                                                                                                                             if( r == 'false' ){

                                                                                                                                                                             $("[name='id_city']").fadeOut();

                                                                                                                                                                            $("[name='id_city'] option[value=0]").attr("selected", "selected");

                                                                                                                                                             }else{

                                                                                                                                                                            $("[name='id_city']").html(r);

                                                                                                                                                                            $("[name='id_city']").fadeIn();

                                                                                                                                                                            //$('#id_city option[value=0]').attr("selected", "selected");                                                                                                                                            

                                                                                                                                                                            $("[name='id_city'] option[value='+aux_id_city+']").attr("selected", "selected");

                                                                                                                                                             }

                                                                                                                                                             $("[name='id_city']").trigger('click');

                                                                                                                                                             //$("#id_street").trigger('click');

                                                                                                                                                             /*/***ajaxStreet();*/

                                                                                                                                             }

                                                                                                                             });

                                                                                                              };

                                                                                                                                                                                                                                          

 

                                                                                                             

                                                                                              });

 

                                                               {/literal}

                                                               </script>

{/if}

 

Finally, we will create a pseudo module for Ajax functions. This can be done in other ways, this is just a simple one: In the modules folder, we create a folder called ajax_addresses and within it a file called ajax.php with this content:
 

<?php
    include(dirname(__FILE__). '/../../config/config.inc.php');
    include(dirname(__FILE__). '/../../init.php');
    
    


// obtengo  city 
if (isset($_GET['ajaxCity']) AND isset($_GET['id_state']))
{
   
$idTemp = ( (int)(Tools::getValue('id_state')) == 0 ? (int)(Tools::getValue('aux_id_state')) : (int)(Tools::getValue('id_state')) );
$idcity =  Tools::getValue('aux_city');
    $states = Db::getInstance()->ExecuteS('
        SELECT C.id_city, C.name
        FROM '._DB_PREFIX_.'city C
        WHERE C.id_state = '.$idTemp.'
        ORDER BY C.`name` ASC');
    $states2 = Db::getInstance()->getRow('
        SELECT C.id_city, C.name
        FROM '._DB_PREFIX_.'city C
        WHERE C.name = \''.$idcity.'\'
        ORDER BY C.`name` ASC');

//var_dump($states);
    if (is_array($states) AND !empty($states))
    {
        $list = '';
        if (Tools::getValue('aux_id_state') != true)
        if($idcity != null){
            $list = '<option value="'.$states2['name'].'" class="showme" >'.($idcity == 0 ? "---" : $idcity) .'</option>'."\n";

        }

        foreach ($states AS $state)
        if($idcity == 0){
            $list .= '<option value="'.(int)($state['id_city']).'"'.((isset($_GET['id_city']) AND $_GET['id_city'] == $state['id_city']) ? ' selected="selected"' : '').'>'.$state['name'].'</option>'."\n";
        } else{
            $list .= '<option value="'.(int)($state['id_city']).'"'.((isset($idcity) AND $idcity == $state['name']) ? ' selected="selected"' : '').'>'.$state['name'].'</option>'."\n";

        }
    }
    else
        $list = 'false';

    die($list);
}

 

 

Luego solo debemos limpiar cache y ya deberíamos ver las ciudades como dropdown.

ta6.jpg.48e48500d93444a5a66e2b8e06730b56.jpg

 

Link to files:

https://github.com/shacker2/citiesasdropdownprestashop/tree/main

 

Edited by shacker (see edit history)

Share this post


Link to post
Share on other sites

  • 9 months later...
  • 2 months later...
On 7/17/2020 at 4:14 PM, shacker said:

v1.1 updated to solve small issues

hi - do you have any update on the city selector ?  or like an git ? seems something goes wrong , if i copy paste from the site .

Share this post


Link to post
Share on other sites

55 minutes ago, Mr. Trice said:

I have a module that solves the city and zip code issue.  See one of my clients that make use of it here safetynigeria.com

where is the module to check it out?

Share this post


Link to post
Share on other sites

  • 1 month later...

Hi Shacker. Very good post.

 

But I have a problem, that dropdown never fills with values. Perhaps I got a problem with the copy, but Iĺl try to do for 3 or more times. Do you have a git or somethin similar that yo can share me (and the resto of the comunity, of course)?

 

Thanks

Share this post


Link to post
Share on other sites

  • 1 month later...
  • shacker changed the title to [TUTORIAL]Add city as Dropdown in PrestaShop (updated with github files)
Just now, shacker said:

hi, sorry,but this post is not for the module that you purchase. In this post we didnt offer any module, only a modification to show the cities as dropdown

Hi again. Like I wrote earlier. The modification does not work. Your instructions and files from github did not work for our website.

Share this post


Link to post
Share on other sites

9 minutes ago, vivalaskip said:

Hi again. Like I wrote earlier. The modification does not work. Your instructions and files from github did not work for our website.

you said that is a module, and you purchase it.

Share this post


Link to post
Share on other sites

8 hours ago, shacker said:

you need to post here the error (javascript, enable debug and check whats is going on)

Totally agree! I did that on Sunday and the messages were deleted. Now I have to wait for the team that developed the module to fix the issue, after that I will reproduce the probem and post the errors and screenshots. 

Edited by vivalaskip (see edit history)

Share this post


Link to post
Share on other sites

  • 4 weeks later...
On 10/6/2021 at 12:05 AM, shacker said:

you need to post here the error (javascript, enable debug and check whats is going on)

[PrestaShopDatabaseException]

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 1' at line 3<br /><br /><pre>select * from ps_city WHERE id_city= LIMIT 1</pre>
at line 769 in file classes/db/Db.php

plus some unclosed accolades in javascript file.

Edited by apollux (see edit history)

Share this post


Link to post
Share on other sites

20 minutes ago, apollux said:

[PrestaShopDatabaseException]

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 1' at line 3<br /><br /><pre>select * from ps_city WHERE id_city= LIMIT 1</pre>
at line 769 in file classes/db/Db.php

plus some unclosed accolades in javascript file.

Also:

(1/1) ContextErrorException 
Notice: Undefined index: id_address

which is from

$(".form-control-select .js-city").last().val();
                                                               var mi_ajaxurl                  = '{$urls.base_url}modules/';

                                                               var aux_id_state             = '{if isset($smarty.post.id_state) and $smarty.post.id_state <> null}{$smarty.post.id_state}{else}{if isset($customer.addresses[$smarty.get.id_address].id_state)}{$customer.addresses[$smarty.get.id_address].id_state}{else}0{/if}{/if}';

                                                               var aux_id_city = '{if isset($smarty.post.id_city) and $smarty.post.id_city <> null}{$smarty.post.id_city}{else}{if isset($customer.addresses[$smarty.get.id_address].id_city)}{$customer.addresses[$smarty.get.id_address].id_city}{else}0{/if}{/if}';

                                                               var aux_city       = '{if isset($smarty.post.city) and $smarty.post.city <> null}{$smarty.post.city}{else}{if isset($customer.addresses[$smarty.get.id_address].city)}{$customer.addresses[$smarty.get.id_address].city}{else}0{/if}{/if}';

Bottom line, does not work for 1.7.6.9.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

Cookies ensure the smooth running of our services. Using these, you accept the use of cookies. Learn More