Jump to content

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


shacker

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)
Link to comment
Share on other sites

  • 9 months later...
  • 2 months later...
  • 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

Link to comment
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.

Link to comment
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)
Link to comment
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)
Link to comment
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.

  • Like 1
Link to comment
Share on other sites

  • 10 months later...
On 10/29/2021 at 4:38 PM, apollux said:

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.

Did you manage to fix this issue? I got the same...

Link to comment
Share on other sites

  • 1 month later...
  • 1 month later...
  • 10 months later...

Test on Prestashop 8.0.0 have this error:

Php 8.1

Prestashop 8.1.1

 

SQLSTATE[42000]: Syntax error or access violation: 1064 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 1
at line 151 in file classes/db/DbPDO.php

 

image.thumb.png.cd90d0a0e223c18fa1ba6a8ad2061908.png

 

Link to comment
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
×
×
  • Create New...