shacker Posted July 14, 2020 Share Posted July 14, 2020 (edited) 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 The id_states must be given by each state that has your prestashop store. The table would look like this: The next step is to modify the ps_address table by adding an id_city field. 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: And we will change the field city to city: name. Name would add the dropdown: 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. Link to files: https://github.com/shacker2/citiesasdropdownprestashop/tree/main Edited September 26, 2021 by shacker (see edit history) Link to comment Share on other sites More sharing options...
shacker Posted July 17, 2020 Author Share Posted July 17, 2020 v1.1 updated to solve small issues Link to comment Share on other sites More sharing options...
Ehsanai Posted April 29, 2021 Share Posted April 29, 2021 On 7/17/2020 at 5:44 PM, shacker said: v1.1 updated to solve small issues Hi, Do you have solution for Back office add new order page? Link to comment Share on other sites More sharing options...
shacker Posted May 3, 2021 Author Share Posted May 3, 2021 no for the moment, sorry Link to comment Share on other sites More sharing options...
creedence Posted July 14, 2021 Share Posted July 14, 2021 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 . Link to comment Share on other sites More sharing options...
creedence Posted July 14, 2021 Share Posted July 14, 2021 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? Link to comment Share on other sites More sharing options...
Gorka Sanz Posted August 16, 2021 Share Posted August 16, 2021 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 More sharing options...
shacker Posted September 26, 2021 Author Share Posted September 26, 2021 Hi. we have uploaded all to github https://github.com/shacker2/citiesasdropdownprestashop/tree/main Link to comment Share on other sites More sharing options...
shacker Posted September 30, 2021 Author Share Posted September 30, 2021 you can test this mod working here http://tiendamovil.com.py/shop/ and here https://tiendaonline.errepar.com/ Link to comment Share on other sites More sharing options...
shahinnn Posted October 4, 2021 Share Posted October 4, 2021 Hi Shacker, thanks a lot Very good post. I using prestashop 1.6, how to used from code in prestashop 1.6? Link to comment Share on other sites More sharing options...
vivalaskip Posted October 5, 2021 Share Posted October 5, 2021 Hi there! My messages were deleted. The tutorial doesn't work. Also, the demo for the module does not work. Link to comment Share on other sites More sharing options...
shacker Posted October 5, 2021 Author Share Posted October 5, 2021 11 hours ago, shahinnn said: Hi Shacker, thanks a lot Very good post. I using prestashop 1.6, how to used from code in prestashop 1.6? i dont have for PS 1.6 for the moment Link to comment Share on other sites More sharing options...
shacker Posted October 5, 2021 Author Share Posted October 5, 2021 5 hours ago, vivalaskip said: Hi there! My messages were deleted. The tutorial doesn't work. Also, the demo for the module does not work. hi, this is not a module, is a modification Link to comment Share on other sites More sharing options...
vivalaskip Posted October 5, 2021 Share Posted October 5, 2021 53 minutes ago, shacker said: hi, this is not a module, is a modification Sorry... the modification doesn't work. And the demo for the module from @Mr. Trice doesn't work also. Link to comment Share on other sites More sharing options...
shacker Posted October 5, 2021 Author Share Posted October 5, 2021 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 Link to comment Share on other sites More sharing options...
vivalaskip Posted October 5, 2021 Share Posted October 5, 2021 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 More sharing options...
shacker Posted October 5, 2021 Author Share Posted October 5, 2021 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. Link to comment Share on other sites More sharing options...
vivalaskip Posted October 5, 2021 Share Posted October 5, 2021 3 minutes ago, shacker said: you said that is a module, and you purchase it. Yes, and I also tried your instructions and the files from Github. Both don't work. Link to comment Share on other sites More sharing options...
shacker Posted October 5, 2021 Author Share Posted October 5, 2021 you need to post here the error (javascript, enable debug and check whats is going on) Link to comment Share on other sites More sharing options...
vivalaskip Posted October 6, 2021 Share Posted October 6, 2021 (edited) 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 October 6, 2021 by vivalaskip (see edit history) Link to comment Share on other sites More sharing options...
apollux Posted October 29, 2021 Share Posted October 29, 2021 (edited) 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 October 29, 2021 by apollux (see edit history) Link to comment Share on other sites More sharing options...
apollux Posted October 29, 2021 Share Posted October 29, 2021 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. 1 Link to comment Share on other sites More sharing options...
Maciej Programmer Posted September 5, 2022 Share Posted September 5, 2022 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 More sharing options...
apollux Posted October 14, 2022 Share Posted October 14, 2022 On 9/5/2022 at 12:32 PM, Maciej Programmer said: Did you manage to fix this issue? I got the same... I did. I removed the bugged module Link to comment Share on other sites More sharing options...
shacker Posted October 15, 2022 Author Share Posted October 15, 2022 you can check the variable to avoid that error Link to comment Share on other sites More sharing options...
DNK-LUIFER Posted November 21, 2022 Share Posted November 21, 2022 (edited) Test on Prestashop 8.0.0 have this error: Php 7.4.33 php 8.0.25 Same error: Notice: Undefined variable: sql in /usr/local/lsws/www.xxxxxxx.com/ps80/ps80/classes/Address.php on line 154 How to fix ? Many thanks. Edited November 21, 2022 by freiserk (see edit history) Link to comment Share on other sites More sharing options...
ocio87 Posted October 19, 2023 Share Posted October 19, 2023 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 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now