Combos dependientes symfony 2

1,796 views
Skip to first unread message

Pablo Natario

unread,
Dec 19, 2012, 11:21:06 AM12/19/12
to symfo...@googlegroups.com
Hola gente, estoy buscando pero no encuentro la manera de hacer que al seleccionar un combo, me actualice otro, el ejemplo es sencillo, elijo una provincia y una ciudad en dos combos. No encuentro la forma para symfony 2, alguien sabe como?
Saludos.

--
------------------------------------------------------------------------
"Si me canse de esperar, fue porque el tiempo no curó ni una herida.
Si me cansé de olvidar, fue porque el olvido es la ''pastilla suicida''.
Si me cansé de perdonar,fue porque cuando duele nunca, nunca, se olvida.
Si me cansé de mentir, fue porque la verdad lastima solo al principio.
Si me cansé de dormir, fue porque al ''sueño'' no lo sueño dormido.
Si me cansé de ceder, fue porque cediendo te vas muriendo en vida.
Si me cansé de llorar, fue porque en las lágrimas no encontré salida.
Si me cansé de correr, fue porque a muchas cosas las perdí por correr noche y día.
Si me cansé de mirar, fue porque mirando ví una vez a la muerte.
Si me cansé de perder, fue porque una vez me desangre por perderte.
Si me cansé del culo cerrar fue por el hambre, el miedo a la guerra y a la fría soledad".
Tema: Si me cansé. Autor: Callejeros.

Sergio Sánchez

unread,
Dec 19, 2012, 11:24:44 AM12/19/12
to symfo...@googlegroups.com
Yo lo he solucionado usando ajax (mediante jquery), no sé si existe algo en symfony2 que haga esto de forma automática.

--
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Google Groups.
Para publicar en este grupo, envía un email a symfo...@googlegroups.com
Para darte de baja, envía un email a symfony-es+...@googlegroups.com
El resto de opciones puedes encontrarlas en http://groups.google.com/group/symfony-es?hl=es

Pablo Natario

unread,
Dec 19, 2012, 11:28:18 AM12/19/12
to symfo...@googlegroups.com
ok, si yo lo armo asi en ajax con jquery, la consulta para que me muestre las ciudades donde la pondrías? en un action común se podrá? 


2012/12/19 Sergio Sánchez <tiradoen...@gmail.com>

Sergio Sánchez

unread,
Dec 19, 2012, 11:34:06 AM12/19/12
to symfo...@googlegroups.com
Si, yo generé una ruta (con su correspondiente action) que devolvía un json con las ciudades según la provincia que se le mandase por post, luego con jquery maquetaba el json y generaba el select.

Martín Bertinat

unread,
Dec 19, 2012, 11:37:07 AM12/19/12
to symfo...@googlegroups.com
Te dejo algo que te puede llegar a servir..

// Cuando te cambian el departamento (provincia en tu caso)

$('#inmo_sistemabundle_propiedadtype_departamento').change(function(){
// borrás el primer item..
         $('#inmo_sistemabundle_propiedadtype_localidad option:gt(0)').remove();
// Buscas el id del depto seleccionado
         id_depto = $('#inmo_sistemabundle_propiedadtype_departamento').val();
con esta funcion pasandole el id del departamento y el 'select' se encarga de cargar los datos ahí.
         loadLocalidadesByDepartamento(id_depto, '#inmo_sistemabundle_propiedadtype_localidad');
 });

/**
* Permite cargar las localidades de determinado departamento en un select
* idDepartamento: Será el id (numérico) de un departamento para buscar sus localidades
* selectorLocalidades: Será un string que hará referencia al selector identificador
* del select que se rellenárá con las localidades
*/
function loadLocalidadesByDepartamento(idDepartamento, selectorLocalidades){
    $(selectorLocalidades+' option:gt(0)').remove();
    if(idDepartamento){
        $.ajax({
            dataType: 'json',
            async: false,
            url: Routing.generate('ajax_localidades', {'departamentoId' : idDepartamento}),
            success: function(data){
                $.each(data, function(i){
                    $(selectorLocalidades).append($("<option></option>").attr("value",this.value).text(this.text));
                });
            }
        });
    }
}


Fijate que estoy usando FOSJSrouting (para generar correctamente la ruta)

Y este sería el action:

/**
    * $departamentoId : Id del departamento para filtrar las localidades que sean de ese departamento
    */
    public function localidadesAction($departamentoId){
        $em = $this->getDoctrine()->getEntityManager();
        $entities = $em->getRepository('SistemaBundle:Localidad')->findByDepartamento($departamentoId);
        $localidades = array();
        foreach($entities as $entity){
            $localidad['value'] = $entity->getId();
            $localidad['text'] = $entity->getNombre();
            $localidades[] = $localidad;
        }
        $response = new Response();
        $response->setContent(json_encode($localidades));
        return $response;
    }

Cualquier consulta a las ordenes, espero que te sirva.

Juan Martín Díaz

unread,
Dec 19, 2012, 5:56:00 PM12/19/12
to symfo...@googlegroups.com
Hola Pablo, la dificultad no está en la parte AJAX ni en armar una ruta con un action en Symfony.
El tema es que cuando terminás de completar el formulario y hacés el submit, Symfony te va a decir que el elemento del selectbox dependiente que está seleccionado no se encuentra en la colección.
Esto es porque cuando seleccionas un elemento del select principal estás cargando en el select dependiente una colección de datos diferente a la que carga el form por defecto.
La dificultad entonces en este caso está en el form.
El form debe tener la capacidad de aplicar un filtro de datos al select secundario según el valor del select primario.
Para eso debe recibir como parámetro el valor del select primario.
Sinceramente he investigado mucho pero no he llegado a una solución elegante aunque sí funcional.
Lo que hago es pasarle desde el controlador al form como parámetro dentro de options el valor por el cual deseo filtrar.
Como el form todavía no ha sido procesado no puedo acceder al dato que necesito de otra manera que no sea el objeto request, en mi caso:

$request = $this->getRequest();
$kindId = $request->get('mibundle_producttype[kind]', null, true); //kindId es el valor del primer select

//Y se lo paso al form así:
$form    = $this->createForm(new ProductType(), $entity, array('kindId' => $kindId));
$form->bindRequest($request);

El Form quedaría así:

class ProductType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $kindId = $options['kindId'];

        $builder
            ->add('kind', 'kind', array('empty_value' => 'Seleccione el tipo', 'label' => 'Tipo', 'property_path' => false, 'data' => $kindId, )) //property_path está en falso porque kind no pertenece a product, lo uso solo para filtrar
            ->add('category', 'entity', array('class' => 'MiBundle:Category', 'property' => 'name', 'label' => 'Categoría', 'empty_value' => 'Seleccione la categoría',
                'query_builder' => function($repository) use ($kindId) {
                    $qb = $repository->createQueryBuilder('c');
                    
                    return $qb->where('c.kind = :kind')
                            ->setParameter('kind', $kindId);
                }))
            ->add('name', null, array('label' => 'Nombre',))
            ->add('description', 'textarea', array('max_length' => 1000, 'label' => 'Descripción',))
            ->add('code', null, array('label' => 'Código',))
            ->add('tradeMark', 'entity', array('class' => 'KunturCencerroBundle:TradeMark', 'required' => false, 'property' => 'name', 'label' => 'Marca', 'empty_value' => 'Seleccione la marca',))
        ;
    }

    public function getName()
    {
        return 'mibundle_producttype';
    }
    
    public function getDefaultOptions(array $options)
    {
        //return array('data_class' => 'MiBundle\Entity\Product');
        $options['kindId'] = '';
        return $options; 
    }
}


Como dije, no es la solución más elegante para hacer esto ya que seguramente para hacerlo bien haya que usar eventos y acceder a los valores enviados por el navegador desde ahí, pero es una solución y funciona.
Espero que te sirva y si encontrás una solución mejor por favor enviala como respuesta por acá.
Saludos.

Fran Moreno

unread,
Dec 20, 2012, 7:18:00 AM12/20/12
to symfo...@googlegroups.com
Buenas,

El otro día escribí un artículo sobre esto:


Se puede ver un ejemplo aquí:


Y el código del proyecto está disponible ahí. Lo suyo creo que sería tener un Type que le indiques de cuál dependes, a ver si puedo un día de estos intentarlo.

Saludos,
Fran Moreno
Para darte de baja, envía un email a symfony-es+unsubscribe@googlegroups.com

nitram

unread,
Dec 20, 2012, 7:45:33 AM12/20/12
to symfo...@googlegroups.com
Excelente el artículo Fran!!! Lo voy a tratar de poner en práctica hoy.

Gracias

Pablo Natario

unread,
Dec 21, 2012, 10:10:13 AM12/21/12
to symfo...@googlegroups.com
Hola gente gracias por sus respuestas! las vi todas hoy y decidí ver lo que dice Fran.

Ahora tengo una consulta porque me mareé un poco.
En el ajax en donde dice :"url: '{{ path("select_provinces") }}',"
que pongo en lugar de "select_provinces"?

Saludos.


2012/12/20 nitram <martinb...@gmail.com>

Fran Moreno

unread,
Dec 21, 2012, 11:07:11 AM12/21/12
to symfo...@googlegroups.com
Esa es el path a esta acción:


Lo único que hace es obtener el country_id que se le ha pasado como parámetro y devuelve las provincias de ese country en formato:

<option value="1">Provincia 1</option>
<option value="2">Provincia 2</option>

Y eso será lo que rellene el select de provincias. 

Es lo que comentaba Juan, en el post sólo está explicado la parte de formularios y como quedaba muy largo no expliqué la parte de ajax y tal, igual este finde lo actualizo y añado algo.

Saludos,
Fran Moreno

nitram

unread,
Dec 21, 2012, 12:42:14 PM12/21/12
to symfo...@googlegroups.com
Fran, si vas a actualizar el artículo con los javascripts y demás, estaría bueno que la petición ajax devolviera un json en vez del código html! Lo digo como sugerencia, para que quede mas prolijo.

mauricio_lo...@hotmail.com

unread,
Dec 13, 2013, 7:03:33 PM12/13/13
to symfo...@googlegroups.com

Hola Frank:

He seguido todos los pasos que has indicado pero no logro hacer la implementación correctamente.

En mi caso tengo 02 combos “País” y “Ciudad”, el combo ciudad depende del país que se seleccione en el 1er combo.

Te comento los puntos en lo que he podido comprobar que se presentan las fallas:

1.- he creado la ruta :

select_ciudades:

    pattern:  /ciudades

    defaults: { _controller: "BackendBundle:Distrito:ciudades" }

 

2.- Controller

/**

    * @Route("/ciudades", name="select_ciudades")

    * @Template()

    */

    public function ciudadesAction()

    {

        $pais_id = $this->getRequest()->request->get('pais_id');

 

        $em = $this->getDoctrine()->getManager();

 

        $ciudades = $em->getRepository('BackendBundle:Ciudad')->findByPaisId($pais_id);

 

        return array('ciudades' => $ciudades);

    }

3.-El código ajax, no muestra resultado cuando selecciono un país.

<script>

        $(function(){           

            $("#distrito_pais").change(function(){

                var data = {pais_id: $(this).val()};           

                $.ajax({

                    type: 'post',

                    url: '{{ path("select_ciudades") }}',

                    data: data,

                    success: function(data) {

                        $('#distrito_ciudad').html(data);                        

                    }

                });

            });

        });

    </script>

¿CUAL ES EL PROBLEMA?

Francisco Barahona Sarmiento

unread,
Dec 16, 2013, 5:02:57 PM12/16/13
to symfo...@googlegroups.com
Que mas Mauricio, ¿si le da un alert a "pais_id", le muestra algo?. mande los datos de una, sin crear esa variable, sino algo como :  ...data:{pais_id: $(this).val()},... a ver si no le esta llevando esa variable bien, aparte despues comprobar si en el controlador le muestra con un "var_dump" ese valor. Las primeras veces que hice unas listas desplegables dependientes esos eran mis principales problemas, es un poco enredado la primera vez que uno lo hace, lo sé. Me avisa cualquier cosa. Saludos

--
--
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Google Groups.
Para publicar en este grupo, envía un email a symfo...@googlegroups.com
Para darte de baja, envía un email a symfony-es+...@googlegroups.com
El resto de opciones puedes encontrarlas en http://groups.google.com/group/symfony-es?hl=es
 
---
Has recibido este mensaje porque estás suscrito al grupo "symfony-es" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus correos electrónicos, envía un correo electrónico a symfony-es+...@googlegroups.com.
Para obtener más opciones, visita https://groups.google.com/groups/opt_out.

mauricio_lo...@hotmail.com

unread,
Dec 19, 2013, 12:58:15 PM12/19/13
to symfo...@googlegroups.com
Gracias a todos ya lo pude implementar, me falto lo siguiente:

1.- En el Controller

<?php
namespace XXXX\BackendBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;

class DefaultController extends Controller

{
    /**    
     * @Route("/ciudades", name="select_ciudades")
     * @Template()
     */
    public function ciudadesAction()
    {
        $pais_id = $this->getRequest()->request->get('pais_id');
        $em = $this->getDoctrine()->getManager();
        $ciudades = $em->getRepository('BackendBundle:Ciudad')->findByPaisId($pais_id);
        return $this->render('BackendBundle:Default:ciudades.html.twig', array('ciudades' => $ciudades));
    }
   
}

2.- Crear la vista ciudades.html.twig:
<option value="">Ciudad</option>
{% for ciudad in ciudades %}
    <option value="{{ ciudad.id }}">{{ ciudad.nombre }}</option>
{% endfor %}

3.- Agregar en el routing.yml :
select_ciudades:
    pattern:  /ciudades
    defaults: { _controller: "BackendBundle:Default:ciudades" }

4.- En el formulario el javascript:
{% block javascripts %}
    {{ parent() }}

    <script>
        $(function(){           
            $("#distrito_pais").change(function(){
                var data = {
                    pais_id: $(this).val()
                };

                $.ajax({
                    type: 'post',
                    url: '{{ path("select_ciudades") }}',
                    data: data,
                    success: function(data) {
                        $('#distrito_ciudad').html(data);                       
                    }
                });
            });
        });
    </script>
{% endblock %}


El miércoles, 19 de diciembre de 2012 11:21:06 UTC-5, Pablo Natario escribió:

Jesús López

unread,
Jan 15, 2014, 6:06:23 AM1/15/14
to symfo...@googlegroups.com
Hola a todos, llevo unos dias investigando esto de los selects dependientes ya que estoy en un desarrollo donde los debo implementar. 
En mi caso yo tengo una entidad Provincia y una entidad Municipio que se relaciona con provincia tipo provincia_id.
He creado un modelo Location con adress y municipio y he creado AddMunicipioFieldSubscriber y un AddProvinciaFieldSubscriber. 
En mi caso trabajo con rutas yml así que he creado unas rutas para el ejemplo:

examples_dependent_selects_location_new:
  pattern: /selects-dependientes/location/new
  defaults: { _controller: MiBundle:Municipio:location }
 
select_provincias:
  pattern: /provincias
  defaults: { _controller: MiBundle:Municipio:provincias }
  
select_municipios:
  pattern: /municipios
  defaults: { _controller: MiBundle:Municipio:municipios }
  requirements:
      _method: POST

y unas acciones para probar la demo:

public function locationAction(Request $request)
    {
        $location = new Location();
        $form = $this->createForm(new LocationType(), $location);

        if ($request->isMethod('POST')) {
            $form->bind($request);

            if ($form->isValid()) {

                // do amazing things

                $flashBag = $this->get('session')->getFlashBag();
                $flashBag->add('smtc_success',array('title'=>'Se ha creado una localización:'));
                $flashBag->add('smtc_success', sprintf('Dirección: %s', $location->address));
                $flashBag->add('smtc_success', sprintf('Ciudad: %s', $location->municipio->getNombre()));

                return $this->render('MiBundle:Municipio:newLocation.html.twig',array('form'=>$form->createView()));
            }
        }

        return $this->render('MiBundle:Municipio:newLocation.html.twig',array(
            'form' => $form->createView()
        ));
    }
    
    public function provinciasAction()
    {
     $em = $this->getDoctrine()->getManager();

     $provincias = $em->getRepository('TalleresFrontendBundle:Provincia')->findAll();

     return $this->render('MiBundle:Municipios:newLocation.html..twig',array('provincias' => $provincias));
    }

   public function municipiosAction()
   {    
    $request = $this->getRequest()->get('request');
    $provincia_id = $request->get('provincia');
    $id = $_REQUEST['provincia'];
   
    $em = $this->getDoctrine()->getManager();

    $municipios = $em->getRepositoryMiBundle:Municipio')->findByProvincia($provincia_id);

    return $this->render('MiBundle:Municipios:newLocation.html.twig',array('municipios'=>$municipios));//$this->render(MiBundle:Municipios:newLocation.html.twig',array('municipios' => $municipios));
}

Con todo esto más un LocationType y una vista que queda de la siguiente forma:


{% block javascripts %}
    {{ parent() }}
    <script>
        $(function(){
      
            $("#location_provincia").change(function(){
                var data = {
                    provincia: $(this).val()
                };
                alert (data['provincia']);
                 $("body").css("cursor", "progress");
                $.ajax({
                    type: 'post',
                    url: '{{ path("select_municipios") }}',
                    data: data,
                    success: function(data) {
                        //$('#location_municipio').html(data);
                        $('#location_municipio').html('<option>'+data+'</option>');
                    }
                });
             });
        });
    </script>
{% endblock %}

{% block body %}
    
    <div class="row">
        <div class="span12">
            <form action="{{ path('examples_dependent_selects_location_new') }}" method="POST" novalidate>
                {{ form_row(form.address) }}
                {{ form_row(form.provincia) }}
                {{ form_row(form.municipio) }}
                {{ form_rest(form) }}
                <button type="submit" class="btn btn-success">
                    Guardar
                </button>
            </form>
        </div>
    </div>


    
    
 {% endblock %}   

El combo dependiente no Funciona. Si hago un envio de datos incorrecto si recoge los datos por post y cargará en el combo municipios los municipios, pero al realizar la llamada ajax siempre obtengo un error 500. 

Así pues deduzco que existe algun problema con la llamada ajax, la ruta o el controlador, pero ya no sé por donde tirar. 
Pido un poco de ayuda si se os ocurre algo. 

Gracias por aguantar el churro.



Estoy intentando implementar el ejemplo de Fran con Eventos y he de decir que casi me funciona. Digo casi, porque si

Fran Moreno

unread,
Jan 16, 2014, 11:08:49 AM1/16/14
to symfo...@googlegroups.com
Buenas,

En el método municipiosAction, tienes varias cosas:

public function municipiosAction()
   {    
    $request = $this->getRequest()->get('request');
    $provincia_id = $request->get('provincia');
    $id = $_REQUEST['provincia'];
    ...

Para obtener la petición sólo hace falta $request = $this->getRequest(); o directamente como parámetro en método:

public function municipiosAction(Request $request)

Y bueno el $_REQUEST supongo que serán pruebas. Sólo te haría falta una vez tienes el $request:

$provincia_id = $request->get('provincia');

No he mirado más código, no sé si será eso sólo o hay más cosas.

Fran Moreno,

Monserrat Foster

unread,
Feb 5, 2014, 4:20:52 PM2/5/14
to symfo...@googlegroups.com
Yo implemente lo que Fran posteo en el blog de showmethecode (tengo tiempo en esto, pero hoy me decidi a hacerlo funcionar) y aunque funciona (tecnicamente) 
envia la peticion pero el controller me anda dando  The controller must return a response seguido del array con los items para popular el segundo combo y eso si no he podido arreglarlo. Si de algo funciona, aqui esta el codigo (sin el de los formularios) pq no creo que ese sea el problema.


si alguien sabe como debe ser, se agradece la ayuda. 

Monserrat Foster

unread,
Feb 5, 2014, 4:31:47 PM2/5/14
to symfo...@googlegroups.com
Se que deberia crear un response, lo que me parece curioso es que en el ejemplo de fran no lo veo

Manuel Aguirre

unread,
Feb 5, 2014, 4:45:55 PM2/5/14
to symfo...@googlegroups.com

Monserrat Foster

unread,
Feb 5, 2014, 4:51:51 PM2/5/14
to symfo...@googlegroups.com
Lo tengo con anotaciones... 

Jeronimo SALAZAR RAMIREZ

unread,
Feb 5, 2014, 5:39:41 PM2/5/14
to symfo...@googlegroups.com
No puedo creeer que a estas alturas y con el codigo de showmeetcode sigan con los mismos problemas.
--
-------------------------------------------------------
Si programas  en PHP, usa Symfony
-------------------------------------------------------
Navega mas seguro con FireFox
------------------------------------------------------

Monserrat Foster

unread,
Feb 5, 2014, 5:41:38 PM2/5/14
to symfo...@googlegroups.com
Se supone que se esta siguiendo el codigo, y el problema que al menos yo tengo, es en base al codigo que esta en el blog... A veces es mejor no opinar si no se tiene nada bonito que decir -.-" 

Absalón Valdés Ormeño

unread,
Feb 5, 2014, 8:53:42 PM2/5/14
to symfony-es
​Si cuidado, si usas anotaciones DEBES habilitar el bundle de SensioExtraBundle para tenerlas correctamente instaladas. Igual si estas usando otro bundle que use anotaciones (por ejemplo FosRestBundle) podría causar conflicto. ​
Absalon Valdés Ormeño.
http://es.gravatar.com/absalonvaldes
Reply all
Reply to author
Forward
0 new messages