Symfony: EntityType para formularios con relaciones

Los formularios son una de las herramientas más potentes de Symfony. Aprender a trabajar con ellos nos ahorrará a la larga mucho trabajo. Una de las primeras necesidades que tendremos cuando creamos formularios de una entidad es la de poder relacionarla con otra que ya se encuentra registrada en la base de datos, y Symfony ya nos aporta un tipo de campo especial para estos casos: EntityType.

EntityType

Este tipo de campo hereda de ChoiceType, por lo que podemos deducir fácilmente que su objetivo es ofrecer una elección al usuario. En concreto, con esta clase podemos generar distintos tipos de controles en un formulario que automáticamente se rellenan con todos los registros que existan en la base de datos para la entidad seleccionada.

Tiene varias opciones como pueden ser la de filtrar los resultados o establecer manualmente las opciones que aparecerán, pero en este artículos nos centraremos en cómo crear una selección simple y cómo podemos indicarle que muestre la selección con distintos tipos de controles HTML. 

Puedes consultar la documentación oficial de este campo aquí.

Relaciones ManyToOne

Como ejemplo, imaginemos que tenemos dos entidades: la entidad Post, y la entidad Category. Cada post estará relacionado con una categoría, lo que indica que tenemos en nuestro proyecto de Symfony una relación ManyToOne siendo la entidad Post el lado N o Many de la relación.

Se presupone que ya tenemos ambas entidades relacionadas en el proyecto, la base de datos creada y que sabemos utilizar lo básico del ecosistema de Symfony como son los controladores, las vistas de Twig y los formularios simples.

Partiremos del formulario que crea Symfony si lo generamos mediante el comando doctrine:generate:form:

Campo tipo select

Lo primero que haremos será establecer el tipo de campo para cada propiedad de la entidad. El campo name es un input simple de texto por lo que añadiremos la clase TextType como segundo parámetro. Sin embargo, para el campo category usaremos EntityType como hemos comentado anteriormente.

Además de esto, queda lo más importante. Es obligatorio definir qué clase va a contener el campo, ya que si no lo establecemos, Symfony no sabría con qué entidad rellenar el listado y lanzaría el error ‘The required option “class” is missing.’. Por lo tanto añadimos en las opciones del campo la clave class junto con el nombre acortado de la entidad, que en este caso es AppBundle:Category.

También provecharemos para establecer un label a cada campo. Quedaría de la siguiente manera:

Ahora volcaremos el formulario completo en una vista simple con twig. El código usado es el siguiente:

Es importante que la entidad que va a rellenar el listado tenga definido el método toString, ya que Symfony tratará de usarlo para mostrar los resultados.

Sin embargo, si no quieres añadir este método o ya lo tienes definido de otra manera y no deseas que se use en el listado, puedes añadir la opción choice_label en las opciones del campo e indicar qué propiedad de la entidad se usará.

Si cargamos ahora la ruta en la que se muestra el formulario obtendremos lo siguiente:

EntityType select

Con solo este código tendremos un campo desplegable que lista todas las categorías que tenemos almacenadas en la base de datos. Pero aún mejor: cuando se envían los datos no necesitamos realizar nosotros el tratamiento, sino que Symfony automáticamente guardará en la base de datos el id de la categoría a la que apunta el post cuando le indiquemos que persista la entidad.

El código usado para manejar el formulario en el controlador es el mismo que usarías para una entidad simple sin relaciones, incluso el propio framework te lo genera automáticamente si usas el comando para generar las operaciones crud. El código en cuestión es el siguiente:

Y ya está, podemos registrar la nueva entidad que apunta a una categoría con muy poquito, sin preocuparnos de crear nosotros mismos el listado con una consulta SQL y luego de recoger el valor y establecerlo en la entidad Post.

Parece más largo ya que he mostrado el código de la vista y del controlador, pero si partimos de que ese código es simple y repetitivo en cualquier proyecto de Symfony y que incluso el propio framework es capaz de generarlo con un par de comandos, nuestro trabajo ha sido mínimo. Simplemente debemos indicar el tipo de campo que vamos a usar en el formulario y a qué clase pertenece.

Campos tipo radiobutton

Este campo tiene más opciones que podemos aprovechar para modificar el modo en el que se muestra la selección. Por ejemplo, quizá te interese mostrar radiobuttons en vez del listado de tipo select. Esto es tan simple como añadir la opción expanded al campo, de la siguiente manera:

Y obtendríamos el siguiente resultado:

EntityType radiobuttons

Relaciones ManyToMany

Pero esto no queda aquí. Ya somos capaces de generar un listado para elegir una entidad relacionada, pero, ¿y si resulta que un post puede tener varias categorías? Entonces lo que tenemos ahora no nos sirve. Supongamos que vamos a cambiar el modelo de datos y a indicar que la relación entre post y categorías es ManyToMany.

Campo tipo select multiple

Lo mejor es que prácticamente no tenemos que cambiar nada. Simplemente nos vamos al formulario, y en el campo category añadimos la opción multiple:

Ahora nuestro select que aparecía en la vista también se genera con la opción multiple, permitiéndonos seleccionar varios elementos. Symfony además a la hora de procesar los datos enviados entiende que se han marcado varios y crea las relaciones en la base de datos sin que nosotros nos tengamos que preocupar de nada.

EntityType select multiple

Campos tipo checkbox

Aunque personalmente no me gustan este tipo de campos de selección múltiple ya que muchos usuarios no se percatan de que pueden seleccionar varios o no saben cómo hacerlo. Por eso me gusta más mostrarlos como campos de tipo checkbox. Pero no te preocupes, Symfony también ha pensado en esto. Añade ahora en el formulario la opción expanded de la siguiente manera, al igual que hacíamos en el ejemplo de los radiobuttons:

Y ya lo tenemos:

EntityType checkboxes

Espero que te haya sido de ayuda, y si tienes alguna pregunta o apunte que hacer, no dudes en utilizar los comentarios.