Yii2: Formulario para relación uno a muchos

Publicado por Andrea Navarro en

En el presente artículo veremos como crear un formulario que permita manejar datos de dos tablas que tengan una relación de uno-a-muchos en Yii2.

Este ejemplo es una modificación de un artículo realizado por Mr. PHP en el que he simplificado la base de datos, modificado el sistema de validaciones y he agregado las funcionalidades de eliminación y vista. El programa resultante permite al usuario cargar un elemento padre y tantos elementos hijos como sea necesario utilizando un solo formulario unificado que mantiene las validaciones de los modelos originales y permite la actualización y eliminación de los datos de ambas tablas simultáneamente.

El código completo puede descargarse desde nuestro repositorio de GitLab.

Base de datos

Estrucutra base de datos

Para este ejemplo utilizaremos una base de datos sencilla que cuenta con dos tablas: Ventas y Productos que tienen una relación de uno a muchos. Cada Venta puede tener asociada varios productos y dicha relación se realiza a través de la clave foránea ventaId dentro de la tabla Productos.

Modelos

Lo primero que necesitamos son los modelos de ambas tablas. En este caso los modelos han sido generados por Gii a partir de las tablas de la base de datos.

Venta.php

Producto.php

Para poder crear un formulario que permita manejar ambas tablas simultáneamente es necesario crear un nuevo modelo que llamaremos VentaForm. Este modelo contará con dos atributos: uno encargado de almacenar el objeto Venta llamado $_venta y otro encargado de almacenar la lista de objetos tipo Producto llamado $_productos.

Puede observarse como tanto en la función save como en la función delete se hace uso de transacciones, esto evita que se realice la modificación en la base de datos hasta que todas las modificaciones hayan tenido lugar exitosamente lo que evita problemas de consistencia en el caso de ocurrir un error al realizar alguna acción en la base de datos.

VentaForm.php

Para poder manejar correctamente las actualizaciones la función saveProductos utiliza una lista llamada mantener. Al obtener los datos del formulario y guardar los cambios almacena los id de todos los Productos guardados en una lista. Luego busca en la base de datos todos los Productos asociados a la Venta que no se encuentren en esta lista y los elimina. De esta manera cualquier producto que haya sido guardado anteriormente en esa venta pero haya sido eliminado por el usuario en el formulario es también eliminado en la base de datos.

Los productos son almacenados en el atributo $_productos con una clave asociada que se utilizará para diferenciarlos. En la función setProductos puede verse como se recorren los productos y son asignados al atributo. Al principio de dicha función puede observarse como se elimina el producto con clave _id_ antes de recorrerlos, esto se debe a que se utilizará un formulario de producto que será usado como modelo para crear los nuevos formularios cuando el usuario los solicite. Este formulario al ser utilizado solo como modelo no tendrá clave asignada y por lo tanto no debe tenerse en cuenta a la hora de cargar los productos.

Controlador

En el controlador además de incluir el modelo VentaForm es necesario utilizarlo en las funciones de create, update y delete, esto provocará que se ejecuten las funciones de guardado de este modelo en vez de las de Venta. También es necesario cargar el valor del atributo Venta del modelo VentaForm ya sea instanciando uno nuevo en el caso de crear o buscando el modelo correspondiente a través del id en el caso de la actualización.

VentaController.php

Vistas

Al crear el formulario de carga de Ventas y Productos es necesario desactivar las validaciones del lado del cliente utilizando enableClientValidation. Esto evitará que el formulario de Producto que utilizaremos de modelo de errores ya que siempre estará vacío.

En este archivo además del código PHP deberá agregarse el código Jquery necesario para el manejo de eventos de agregado de nuevos formularios de Producto y la eliminación de los mismos. Cuando se selecciona Nuevo Producto se aumenta en uno el valor de la clave y se crea una copia del formulario modelo utilizando dicha clave.

_form.php

Esto dará como resultado el formulario de carga de Venta pero todavía no permitirá la carga de Productos ya que es necesario crear el archivo con el formulario de carga de Producto.

Captura nueva venta

El archivo que será renderizado cada vez que el usuario solicite agregar un nuevo Producto contendrá los campos y como parte de sus nombres se colocará la clave correspondiente permitiendo de esta manera diferenciar los diferentes Productos.

_form-producto-venta.php

Al seleccionar Nuevo Producto un nuevo formulario será renderizado en la sección de productos permitiendo al usuario cargar los datos.

Captura carga de productos

Al actualizar una Venta existente los formularios para los productos asociados serán mostrados con los datos correspondientes ya cargados.

Captura actualización de venta

Finalmente podemos modificar la vista de la venta para que muestre una tabla con la lista de productos asociado a la misma. Esto es posible gracias a la función getProductos definida en el modelo de Venta que permite obtener los productos asociados a través de la relación.

view.php

En la siguiente imagen puede verse la tabla con los datos de los Productos asociados a la Venta.

Captura vista de venta y productos

El código completo puede descargarse de nuestro repositorio de GitLab. Espero que les sirva. Hasta la próxima!

Categorías: Programación

Andrea Navarro

- Ingeniera en Informática - Docente universitaria - Investigadora