Symfony2: Configurar empty_data en formularios

Leyendo un artículo muy interesante de William Durand acerca de la encapsulación de los datos con los formularios en Symfony2 descubrí el atributo empty_data. Este atributo tiene una entrada en el cookbook de Symfony2 y no tiene nada que ver con el atributo empty_value que se usa en los formularios tipo choice. El atributo empty_data en un formulario nos permite especificar cómo se debe construir el objeto que maneja un formulario cuando el usuario envía el formulario si no se ha llamado al método setData antes.

Con código creo que quedará más claro. Normalmente cuando creamos un formulario lo hacemos de esta forma:

Cuando le pasamos el segundo argumento al método createForm, internamente nuestro formulario llamará a setData pasándole en este caso $task. Pero, ¿qué pasa si no le pasamos este argumento o no llamamos explícitamente al método setData?

Hasta que el cliente no envía el formulario no pasa nada, pero una vez se envía, el formulario creará una instancia de la clase que hayamos configurado en el atributo data_class. Es decir, teniendo este Type:

Podemos ver qué pasa ejecutando la siguiente acción:

Lo importante para este artículo es que se crea una instancia de la clase que hemos configurado en data_class.

Pero ¿y si nuestro modelo recibe parámetros en el constructor? Para esto sirve el atributo empty_data. Teniendo el siguiente modelo Task:

Podemos configurar el Type así:

Vamos a ver el por qué de cada una de estas opciones y las combinaciones. Empezamos por el atributo empty_data, en este caso le hemos pasado una Closure (podría ser directamente una instancia, en este caso no nos sirve porque queremos obtener el nombre del formulario) que recibe como parámetro el formulario y crea una nueva instancia de Task pasándole el nombre que obtiene del formulario. Esto significa que, como hemos visto antes, cuando el cliente envíe el formulario y si no se ha llamado a setData tendremos esta instancia de Task. Para hacer esto posible tenemos que configurar el campo name como mapped => false  para que no lo mapee con el objeto que maneja el formulario.

Otra opción sería que el campo name sí estuviera mapeado, en cuyo caso necesitamos el método getName en Task y configurar la opción data_class. ¿Por qué getName? porque el formulario llama a este método durante el mapeo para comprobar sí el valor del atributo name de Task es igual al que tiene el formulario y por eso no haría falta setName porque siempre va a ser el mismo valor, ya que se lo hemos pasado por el constructor y no hace falta llamar al setter.

La opción empty_data nos permite crear modelos con una mejor encapsulación sin innecesarios getters ni setters (en las Entities hacen falta getters y setters).

Para acabar con este artículo que en principio iba a ser de los cortos, vamos a ver la demo:

Ver Demo