Jump to content

[TUTORIAL]Ciudades como desplegables en PrestaShop (añadimos archivos github)


shacker

Recommended Posts

En este tutorial veremos paso a paso como añadir el campo ciudad como menú desplegable al crear o modificar una dirección.

EL primer paso es crear una tabla con las ciudades. La tabla la llamaremos city, y tendrá esta estructura

ta1.jpg.fa7ee2dd0d5574e61aca404fa694739d.jpg

Los id_state deben ser los da cada estado que tenga su tienda prestashop. La tabla se vería así:

ta2.jpg.cce791a4fe9698d920f31b2ffbce29dd.jpg

El siguiente paso es modificar la tabla ps_address añadiendo un campo id_city

ta3.thumb.jpg.9f94ea418efdd9cafc68e16e7a753b26.jpg

Por último, modificaremos la tabla address_format definiendo para que países usaremos el formato de droptown en la ciudad.

 

Solo basta con editar el campo format,  que generalmente cuando contiene estados luce asi:

ta4.jpg.232413cd0c762c14f22e94d564329f93.jpg

Y le cambiaremos el campo city asi  city:name. Name añadiría el dropdown:

ta5.jpg.3c583a2c27ae481edf102fbf1d8fbb13.jpg

A esta altura la tienda nos dará errores ya que faltaran clases, asi que ahora debemos modificar los archivos para que todo esto funcione.

Primero crearemos un archivo City.php en la carpeta clases que permitirá trabajar con las ciudades.El archivo contendrá:
 

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

                               ); 

   }



}

 

El siguiente archivo es el clases/form/CustomerAddressFormatter.php

 

Buscaremos esta línea de estados

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);

                    }

                }

 

y añadiremos al final

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);

                   

                }

 

Luego añadiremos esta función a el archivo clases/State.php


               

 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;

                }

Para estos archivos ya creados siempre podemos usar un override para evitar perder cambios al actualizar PrestaShop

EL siguiente archivo es el clases/Address.php

 

Añadiremos al inicio las variables publicas:   

 public  $id_city;

    public  $cityName;

 

En la fucion  public static $definition = array(

 

Añadiremos al final el campo creado id_city para la tabla address

'id_city' => 'isUnsignedId',

 

Y añadiremos esta nueva función

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;

    }

 

Y al final modificaremos esta función

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

 

donde este elseif debe añadir el campo city    

        } 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;

            }

 

Luego el classes/form/customeraddressform.php reemplazaremos esta función

    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;

    }

El siguiente paso es añadir el código javascript para que tome las ciudades al cambiar de estado:

Para esto modificaremos el archivo themes/nuestrotheme/templates/_partials/javascript.tpl de nuestro theme y le añadiremos  este código al final:

<!-- 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}


Por ultimo, crearemos un pseudo modulo para las funciones Ajax. Esto se puede hacer de otras formas, esta es solo una sencilla.En la carpeta modules, creamos una carpeta llamada ajax_addresses  y dentro de la misma un archivo llamado ajax.php  con este contenido:
 

<?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');



                if (is_array($states) AND !empty($states))

                {

                               $list = '';

                               if (Tools::getValue('no_empty') != 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 a los archivos

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

 

 

Edited by shacker (see edit history)
  • Like 1
  • Thanks 3
Link to comment
Share on other sites

  • 2 weeks later...
  • 1 month later...

Gracias por este gran aporte,

Lo intente pero no me funciono,

 

Revisando el codigo, (pero no soy experto en esto) encontre el que creo puede ser el problema.

en esta parte:

 

y añadiremos al final

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);

 

creo que se hace un llamado a la funcion State::getCities(315) el cual creo que es el error ya que siempre mostraria los datos pero solo para el id_state 315

 

no se como solucionarlo ya que no soy esperto en el tema, lo intente cambiando el valor 315 por una variable id_state pero no supe como hacerlo.

 

de antemano gracias por la ayuda y agradezco nuevamente este gran aporte a la comunidad.

 

saludos

 

Edited by jitoru06 (see edit history)
Link to comment
Share on other sites

  • 1 year later...
On 9/18/2020 at 6:41 PM, jitoru06 said:

Gracias por este gran aporte,

Lo intente pero no me funciono,

 

Revisando el codigo, (pero no soy experto en esto) encontre el que creo puede ser el problema.

en esta parte:

 

y añadiremos al final

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);

 

creo que se hace un llamado a la funcion State::getCities(315) el cual creo que es el error ya que siempre mostraria los datos pero solo para el id_state 315

 

no se como solucionarlo ya que no soy esperto en el tema, lo intente cambiando el valor 315 por una variable id_state pero no supe como hacerlo.

 

de antemano gracias por la ayuda y agradezco nuevamente este gran aporte a la comunidad.

 

saludos

 

Ese valor es solo para tener un valor por defecto al completar el form

 

 

Link to comment
Share on other sites

  • shacker changed the title to [TUTORIAL]Ciudades como desplegables en PrestaShop (añadimos archivos github)
  • 7 months later...

Buenas tardes,

 

Me podrían colaborar indicandome si este despliegue sirve en la versión 1.7.8.5 ya que actualmente hago los cambios y arroja el siguiente error cuando intento ingresar el nuevo campo de Ciudad:

 

image.thumb.png.83c8129a35f10fddf193b3c33918eeb7.png

Link to comment
Share on other sites

  • 1 month later...
En 2/5/2022 a las 9:05 PM, ocio87 dijo:

Buenas tardes,

 

Me podrían colaborar indicandome si este despliegue sirve en la versión 1.7.8.5 ya que actualmente hago los cambios y arroja el siguiente error cuando intento ingresar el nuevo campo de Ciudad:

 

image.thumb.png.83c8129a35f10fddf193b3c33918eeb7.png

No podrás hacerlo desde backoffice.

Tienes que editar desde phpmyadmin como explica en el manual.

Link to comment
Share on other sites

  • 1 year later...
On 29/7/2020 at 15:29, Jeoz said:

¡Hola! Es que ya hice todos los pasos pero no me aparecen las ciudades para seleccionar y tampoco me despliega el carrito de compra. 

¿Me pueden ayudar? 

Captura de pantalla 2020-07-29 a las 16.52.48.png

Captura de pantalla 2020-07-29 a las 16.52.37.png

buenas tardes me pasa igual hice todo el procedimiento pero no me deja ver las ciudades que cree tienen alguna solucion para esto?

 

Link to comment
Share on other sites

hace 10 horas, luismart6424 dijo:

buenas tardes me pasa igual hice todo el procedimiento pero no me deja ver las ciudades que cree tienen alguna solucion para esto?

 

Hola.

Hay que afinar mucho a la hora de situar el script en el .tpl:

themes/nuestrotheme/templates/_partials/javascript.tpl

Yo lo inserto directamente con {LITERAL} script {/LITERAL} al final del fichero.

Tambíen puedes intertar cargar el script donde carga el formulario.

Saludos.

Link to comment
Share on other sites

  • 3 months later...

tengo una duda a mi el desarrollo me funciona correctamente pero queria saber si hay forma de enviar el ID de la ciudad en vez de el nombre de la ciudad , osea que el cliente vea el nombre pero se guarde el ID , o que el campo ID se guarde en otro campo

Link to comment
Share on other sites

  • 2 weeks later...
  • 3 months later...

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...