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
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
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
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:
- Mi entidad Ciudad sería como tu entidad
Provincia
. - Mi entidad Oferta sería como tu entidad
Localidad
.
@javiereguiluz
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
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