Este foro ya no está activo, así que no puedes publicar nuevas preguntas ni responder a las preguntas existentes.

Cómo llamar a un controlador Symfony desde la plantilla Twig

4 de junio de 2015

Hola, estoy aprendiendo poco a poco a usar Symfony y me surge una duda.

Tengo dos tablas:

  • Provincias: nombre, slug.
  • Localidades: nombre, slug, provincia.

La duda es cómo mostrar dentro de una tabla en una plantilla, todas las provincias, además del número de localidades de cada provincia.

En principio sé que es posible que complicando la consulta SQL pueda hacerlo, pero en principio quiero ver otra forma para resolver la duda que tengo.

Veamos:

En una página muestro una tabla con las regiones usando este controlador:

public function provinciasAction(Request $request)
{
     $em = $this->getDoctrine()->getManager();
     $provincias = $em->getRepository('AdminBundle:Provincia')->findAllProvincias();          
     return $this->render('AdminBundle:Default:provincias.html.twig',array('provincias' => $provincias));
}

Y usando esto en la plantilla twig:

...
   {% for provincia in provincias %}
   <tr>
      <td>{{ provincia.nombre }}</td>   
      <td>{{provincia.slug}}</td>
      <td>***DUDA: NUMERO DE LOCALIDADES***</td>
   </tr>
   {% endfor %}

Cuál sería la mejor solución para hallar el número de localidades de cada provincia y mostrarlo en la plantilla??? Sería llamando a un controlador desde la plantilla??

GRACIASSSS!!!


Respuestas

#1

Si tienes las relaciones definidas en tus entidades podría quedar así:

{% for provincia in provincias %}
   <tr>
      <td>{{ provincia.nombre }}</td>   
      <td>{{ provincia.slug }}</td>
      <td>{{ provincia.localidades|length }}</td>
   </tr>
   {% endfor %}

@iBet7o

4 junio 2015, 20:24
#2

No lo entiendo muy bien.

En la entidad Provincia no hay nada relacionado con las localidades.

Es en la entidad Localidad donde hay una relación 1 a muchos con la entidad Provincia (una localidad solo puede pertenecer a una provincia, pero una provincia puede tener varias localidades)

@antoniocarvajal

5 junio 2015, 9:51
#3

Vayamos por partes:

1) La respuesta que te indica @iBet7o es la forma correcta de resolver este problema, pero requiere alguna explicación adicional (sigue leyendo al final de este comentario).

2) La solución que proponías tu también se puede hacer, pero en este caso es muy ineficiente y no merece la pena. En cualquier caso, como referencia, te indico que sería así:

{% for provincia in provincias %}
   <tr>
      <td>{{ provincia.nombre }}</td>   
      <td>{{provincia.slug}}</td>
      <td>{{ render(controller('AppBundle:Default:localidades', {'provincia': provincia.id })) }}</td>
   </tr>
{% endfor %}

Este código ejecuta el controlador localidadesAction($provinciaId) definido en el DefaultController del bundle AppBundle. Aunque este código funciona bien, sería una locura utilizarlo para resolver este problema porque es muy ineficiente.


Como te decía antes, la solución propuesta por @iBet7o es la forma correcta de hacerlo. Pero para ello, tienes que configurar bien las relaciones entre las entidades (o sea, entre las tablas de la base de datos). El problema es que todo esto depende de un proyecto ajeno a Symfony llamado Doctrine y cuya documentación deja mucho que desear.

Lo mejor cuando empiezas es fijarte en algún ejemplo práctico. Si quieres, puedes echar un vistazo a la configuración de las entidades del proyecto Cupon que publiqué hace un tiempo:

@javiereguiluz

5 junio 2015, 10:15
#4

Buenos días Javier, en primer lugar agradecerte todo lo que haces respecto a symfony. No te quepa duda que si no fuese por ti estaría empezando con otro Framework o directamente programando mis proyectos a pelo como hasta ahora venía haciendo.

Tengo el proyecto Cupon descargado y funcional en mi servidor local. Al principio lo seguí a través del libro hasta que adquirí algunos conocimientos, pero la necesidad de empezar con mi propio proyecto me hizo abandonar un poco el seguimiento de Cupon y emprender la aventura por mi cuenta. Por cierto, el proyecto en el que lo voy a poner en marcha es en el rediseño de calendarioslaborales.com, que empezó como una tontería para probar cosas de SEO y ya tiene casi 2 millones de visitas al mes.

Volviendo a la duda. Lo he resuelto como comentáis y funciona perfectamente. El problema es que sólo definía la relación MuchosAUno en la entidad Localidad, y me faltaba definir la relacion UnoAMuchos en la Entidad Provincia. Me imagino que esto me pasa por pensar aún en registros de una tabla de base de datos y no en objetos.

Pongo por aquí como lo he hecho por si alguien tiene la misma duda:

En la entidad Provincia:

.....
    /**
     * @ORM\OneToMany(targetEntity="Calendarios\AdminBundle\Entity\Localidad", mappedBy="provincia")
     */
    protected $localidades;
 
    public function __construct()
    {
        $this->localidades = new ArrayCollection();
    }
 
    public function getLocalidades()
    {
        return $this->localidades;
    }
    .....

En la plantilla Twig

{% for provincia in provincias %}
<tr>
   <td>{{ provincia.nombre }}</td>
   <td>{{ provincia.region }}</td>
   <td>{{ provincia.localidades|length }}</td>
purple"><i class="fa fa-edit"></i> Editar </a></td>
</tr>
{% endfor %}

De nuevo muchas gracias a ambos!

@antoniocarvajal

5 junio 2015, 11:28
#5

Buenas, como un aporte más me parece importante destacar que el mostrar el número de localidades puede hacer que se ejecute una nueva consulta por cada provincia que sea mostrada, lo cual dependiendo de la cantidad de provincias en el for, puede ser tambien ineficiente.

Para solventar este inconveniente tienes varias opciones:

1. Crear una consulta personalizada que pida las provincias y sus localidades de una sola vez. Lee Doctrine: Obteniendo objetos relacionados

2. Cambiar la opción fetch de la asociación para que use "EXTRA_LAZY" ya que por defecto la opción fetch es lazy, y con este último valor al hacer un count de la colección de localidades, se ejecuta un sql y la colección de localidades es cargada en la provincia.

Por el contrario con el fetch="EXTRA_LAZY" cuando se hace un count de una colección, Doctrine ejecuta igualmente un sql en la base de datos, pero no para traerse las localidades, sino que solo ejecuta un SELECT COUNT(... de las localidades, lo cual puede ser un poco más óptimo para tus necesidades. Lee la doc oficial sobre EXTRA_LAZY


Por último mi recomendación personal es que aunque uses EXTRA_LAZY hagas la consulta de tal manera que te devuelva las localidades ya cargadas y así evitar consultas adicionales al momento de mostrar el número de dichas localidades.

Saludos!

@manuel_j555

5 junio 2015, 13:55