Como ustedes saben, la validación de datos es algo fundamental en el desarrollo de sitios web y aplicaciones. La capa de Validation de Laravel nos provee distintas formas de hacerlo pero tiene una característica que nos ayuda mucho mas, los Form Request.
Introducción: Mejorar capa de Validation de Laravel con Form Requests
Estamos acostumbrados a validar nuestros datos de entrada en los controladores haciendo uso de la función validate()
o de la clase Validator
. Algo así:
No hay nada de malo de implementar la Validation de Laravel en el controlador, pero es una mala práctica porque le estamos agregando responsabilidad al controlador que no debería tener.
El controlador solo debe tener la responsabilidad de tratar las solicitudes, manejar las solicitudes desde la ruta y devolver respuestas. Escribir la lógica de validación en el controlador romperá el principio de responsabilidad única.
Por otro lado, sabemos que los requerimientos del sistema van cambian con el tiempo y tener muchas responsabilidades en una sola clase hace que sea muy difícil de mantener.
Por estos motivos, la capa Validation de Laravel nos brinda los Form Request que son clases encargadas de manejar todo lo relacionado con la validación de datos (validación, mensajes de errores y redirecciones si la validación falla).
¿Cómo crear un Form Request?
Tenemos un comando artisan que hace todo por nosotros:
$ php artisan make:request PostCreateRequest
Su nomenclatura es [Nombre-del-modelo-que-impacta]+[Acción]+Request. Por ejemplo, si quieres crear un Form Request para validar los datos que actualizarán el modelo Post, deberías ponerle el siguiente nombre:
$ php artisan make:request PostUpdateRequest
El comando creará una carpeta llamada Requests en app/Http (si no la tienes aun) y dentro de ella, nuestra clase que se verá como está:
Como pueden ver, tenemos un método para definir las reglas de validación y otro método para definir la lógica de autorización, por ejemplo, si el usuario actual puede realizar dicha solicitud o no.
NOTA: Ten en cuenta que el método authorize()
se crea con valor false
por defecto y debes cambiarlo a true
si no vas a implementar una lógica de autorización.
Además, tenemos mas métodos y propiedades que podemos sobrescribir en nuestro Form Request y hacer la capa de Validation mas poderosa y flexible.
Método Messages() de Form Request
Sobrescribir el método messages()
nos permite definir el mensaje de error a mostrar por cada atributo y por cada regla de validación que no fue aprobada. Debe devolver un array donde la clave debe ser [atributo].[regla] y su valor debe ser el mensaje a mostrar donde puedes utilizar :attribute
que será reemplazado por el nombre del atributo.
Método Attributes() de Form Request
Sobrescribir el método attirbutes nos permite cambiar el valor de :attribute
de esta forma reemplazamos el valor por defecto que se mostrará en el mensaje de error.
Sobrescribir propiedades para Redireccionar los Form Request
En caso que la validación no pase, podemos definir a donde queremos que se redirija la solicitud. Tenemos tres formas de definir la redirección:
- redirect: La propiedad redirect debe ser definida con una URI.
- redirectRoute: La propiedad
redirectRoute
debe ser definida con el nombre de una ruta. - redirectAction: La propiedad
redirectAction
debe ser definida con un método de un controlador.
¿Cómo usar un Form Request?
Todo muy lindo, pero ¿cómo se usa un Form Request? Muy fácil, acá es donde entra la inyección de dependencias. Simplemente tenemos que cambiar la clase Request de nuestro controlador por nuestro Form Request.
Mucho mas limpio, no? Vamos a explicar como funciona.
¿Cómo funciona un Form Request?
La solicitud será tratada por nuestro Form Request antes de llegar al controlador. Ejecutará las reglas de validación y autorización.
Si falla, redirigirá la solicitud a la pagina anterior (en caso que no hayamos definido una redirección) con los mensajes definidos (o los mensajes por defecto) en un array flash.
En caso que los datos pasen las reglas de validación, la solicitud seguirá por el controlador y se modificará nuestro modelo con los datos validados.
¿Se puede usar con peticiones AJAX?
Obviamente que si! Y lo mejor de todo es que no tenemos que tocar nuestro código, el mismo Form Request se encarga de devolver los mensajes de error en formato json y con código de error 422 cuando la solicitud AJAX falla. Genial, no?
Versión disponible
Los Form Request y la posibilidad de crearlos por comando artisan, esta disponible desde la versión 5.0 de Laravel.
Conclusión
Con los Form Request pudimos producir una capa de Validación mas definida que se dedica puramente a las validaciones de solicitudes y datos. Gracias a esto, obtuvimos controladores mas delgados, fáciles de mantener y que no necesitan preocuparse por ninguna lógica de validación. Tenemos nuestra propia clase de validación con una sola responsabilidad para manejar la validación.
Al principio puede parecer molesto tener que crear una clase para manejar las validaciones pero tu yo del futuro te agradecerán por mantener tu código limpio y ordenado. 😁
Muchas gracias por leer mi artículo. Me gustaría mucho escuchar tu opinión al respecto y si tienes alguna pregunta o sugerencia, por favor déjala en la caja de comentarios a continuación.
Cómo se pueden validar los datos de una tabla que se crea con javascript?
Excelente post. Súper detallado.
Muchas gracias! 😀
Podrías decirme cómo hacer un Form Request para la actualización de un recurso que tenga un email como índice único en sus propiedades, ya he tenido muchos inconvenientes con ese detalle
Hola Henry, discúlpame por la demora en responder, pero estuve de vacaciones totalmente desconectado. Según entiendo, quieres hacer un Form Request para un formulario que actualiza datos no? Si es así, deberias tener las reglas algo así:
return [
'user.name.first' => 'required',
'user.name.last' => 'required',
'user.email' => 'required|email|unique:users,email,'.$user->id,
];
Mira como definí la regla para el email, para que así no de error por «emial único» cuando es el mismo usuario el que esta queriendo actualizar sus propios datos.
Espero que te sirva.
Saludos.
Buenas Matias, excelente tu respuesta de hecho me sirvió para un proyecto. Sin embargo, en otro proyecto la tablas están en diferentes esquemas dentro de una misma BD postgres y al colocar el nombre del esquema y tabla Laravel me da error que la conexión no existe. Tienes alguna alternativa de resolver esta escenario?, gracias de antemano
Hola Dennys, si mal no recuerdo, no tenes que poner el nombre del esquema, sino el nombre del driver que configuraste en config/database.php para ese esquema.
También, si estas utilizando Laravel 6.x (o superior), y en el modelo tenes configurado la conexión a donde debe consultar, tenes la posibilidad de utilizar el modelo en la regla, así por ejemplo:
'email' => 'unique:App\User'
Gracias Matías, me viene de lujo!
Hola quiero usar form request en mi api, tengo el método store, ya cree mi form request y si valida, pero al fallar quiere regresar un redirect, y yo solo quiero que me regrese los errores para devolverlos en el response de mi API.
Excelente me pareció el Artículo, El caso o la duda es el siguiente: mi código proviene de una ruta llamada ‘users.create’ la cual retorna a una vista ‘users.create.php’, el cual es un formulario que captura los datos. Por defecto Laravel redirecciona a esta vista y no se me muestra nada en el navegador. tuve que redireccionar según lo que aprendí en tu buen artículo a otra ruta ‘users.index’, que no es la que yo quiero, pero ni modo, funciona pero, seguiré leyendo a ver como soluciono.
Amigos actualmente estoy montando un Validador con request, que lee en una tabla los privilegios de los usuarios para dejarlos acceder a los recursos de la administración, más temprano que tarde se los comparto para que les saquen el debido provecho.
En el post sobre SOLIDA ( el cual es excelente ) el request lo nombras [Acción][Modelo]Request y aqui diferente.
Tenes razón, fue mi error. Lo ideal es [Acción][Modelo]Request. Muchas gracias por tu comentario y la observación. Saludos.
Me ha ayudado mucho este tutorial. Muchas gracias.
Solo que me ha surgido una duda, ¿Se le puede pasar un parámetro al form request? Lo que pretendo es hacer un form request y una regla de validación para impedir que use la misma contraseña que uso con anterioridad a la hora de una actualización de la misma.
Hola Uriel, como estas? Tene en cuenta que en el FormRequest tendrías acceso al usuario logueado (con auth()->user()). De esta forma podrías obtener sus contraseñas antiguas.
Por otro lado, también tienes acceso a los datos que se esta validando. Para acceder a ellos tenes que usar
$this->nombre-del-campo
.Espero que te sirva mi respuesta, si no volve a comentar con mas detalles. Saludos bro.
Estoy de maravilla, gracias por preguntar, ¿Tú, cómo estás?
¡Excelente! Me funciono tu respuesta, gracias por ver algo que «era obvio» y no lo vi 😅
Solo una duda, ¿Si es la mejor practica hacerlo de esa manera? Lo digo mas que nada por los principios SOLID. Saludos 😀
Muy bien, gracias.
Si, para respetar los principios SOLID te recomendaria crear la regla utilizando las clases Rules y utilizar esta en tu FormRequest.
De esta forma estarás respetando el principio de Open/Close (O de SOLID) y Single Responsibility (S de SOLID).
Saludos!
Excelente post, muchas gracias.
Excelente explicación! Muchísimas gracias, voy a implementarlo ya!
Muy buena explicación! Gracias por tan valioso aporte!
Aprovecho para pedir algo de ayuda:
He desarrollado un muy pequeño proyecto en Laravel 5.8, el cual consiste en un par de vistas para consultar comprobantes de pago registrados en una BBDD, la primera muestra un Formulario que pide los datos para la consulta y la segunda muestra el resultado. Implementé las rutas necesarias en el archivo web.php, el Controller y un Form Request para validar los datos del formulario. En el formulario he colocado el token ( @csrf ) porque uso el método POST al enviar la solicitud.
Todo me había funcionado perfectamente, haciendo las validaciones respectivas y de la noche a la mañana, sin hacer ningún cambio en el proyecto, al hacer una consulta salta el error: 419 de página expirada, he modificado el mildleware y agregado la ruta del formulario como excepción(aunque no es aconsejable) y ya no me da el error 419 pero tampoco me valida, es decir, no me hace las validaciones del form request.
Mucho sabría agradecer un poco de orientación.
Me suena que se te ha expirado el token csrf. Pero deja tu consulta en el grupo de facebook «Hablemos de Laravel» y te ayudaremos mejor, más si pones algo de código. Saludos.
Hola Matías, gracias por responder.
Si, efectivamente, también hice la consulta en el fanpage de «Hablemos de Laravel» donde expliqué con más detalle el problema así como también unas imágenes del código.
Acá el link:
https://www.facebook.com/groups/HablemosDeLaravel/?multi_permalinks=2500878680188667&comment_id=2501108806832321¬if_id=1574883303884251¬if_t=feedback_reaction_generic
Gracias de antemano!
Bárbaro, ahí lo miro.
Excelente post, está súper claro.
Tengo una duda, dentro de los Form Request ¿existe una forma de ver cual fue el Request que fue enviado para validar?
Espero me puedas ayudar!
A qué te refieres con que request fue enviada? Utiliza un forn request por request.
Suponiendo que en mi Form Request quiero validar lo siguiente:
public function rules()
{
return [
‘month’ => ‘required’,
‘year’ => ‘required’,
‘file’ => ‘required|file’
];
}
¿Cómo puedo saber el valor que fue enviado para month, year y file dentro de mi Form Request?
Sé que al hacer validación sin Form Request dentro de mi controlador puedo imprimir un log info para ver los valores de la siguiente manera:
info($request)
Y eso me devolvería lo siguiente dentro de los logs de Laravel:
local.INFO: array (
‘_token’ => ‘obtPjxMr49NgaeH3ZFcLvbibiipgfA091NmNY3V4’,
‘month’ => ‘1’,
‘year’ => ‘1995’,
‘file’ =>
Illuminate\Http\UploadedFile::__set_state(array(
‘test’ => false,
‘originalName’ => ‘7up_relacion_premios.csv’,
‘mimeType’ => ‘text/csv’,
‘error’ => 0,
‘hashName’ => NULL,
)),
)
Para saber el valor de los input en tu FormRequest tenes que usar $this. Por ejemplo, $this->month. Recuerda que cuando estas fuera del FormRequest $request es un objecto de FormRequest. Por eso, dentro del FormRequest debes utilizar $this.
Espero que te sirva, saludos amigo.
Saludos amigo, resulta que ando implementando este método que enseñas del FormRequest pero la petición entra al servidor por el métido POST y justo cuando indico en el $redirectAction hacia donde debe continuar recibo el error: The GET method is not supported for this route. Supported methods: POST.
Tal parece que el $redirectAction redirecciona usando el métido GET. Alguna recomendación para solventar esto? ya que estoy usando a Laravel para la API REST nada más.
Lo he logrado así:
use Illuminate\Http\JsonResponse;
use Illuminate\Validation\ValidationException;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;
/**
* Handle a failed validation attempt.
*
* @param \Illuminate\Contracts\Validation\Validator $validator
* @return void
*
* @throws \Illuminate\Validation\ValidationException
*/
protected function failedValidation(Validator $validator)
{
$errors = (new ValidationException($validator))->errors();
throw new HttpResponseException(response()->json([‘errors’ => $errors
], JsonResponse::HTTP_UNPROCESSABLE_ENTITY));
}
Cómo se pueden validar los datos de una tabla que se crea con javascript? los datos los tengo en unos inputs y los name los tengo asi name=nombre[ ] name=apellido[ ] como deberia de colocarlos en el formRequest?? ‘edad’=>’required’, o ‘edad[ ]’=>’required’, cual de las formas es la correcta?? o como se deberia colocar ?? la verdad no me reconoce ninguna de las dos
Buenas como siempre tus posts son muy bueno, te hago una consulta para crear una validación de una clave compuesta suponiendo que tengo esta tabla:
Schema::create(‘curso_materia’, function (Blueprint $table) {
$table->bigIncrements(‘id’);
$table->integer(‘cursoId’)->unsigned();
$table->integer(‘materiaId’)->unsigned();
$table->unique([‘cursoId’, ‘materiaId’]); <– clave unica compuesta
});
como haría la validacion del request?? porque estuve buscando pero no logro hacerla.
me ayudo mucho en un refactor que estoy haciendo. Gracias
Muchas gracias Heriberto por tu comentario. Me alegra que te haya ayudado.
hola si lo ideal es crear los request asi [Acción][Modelo]Request
es bueno o no ordenarlos dentro de carpetas? cuando por ejm tengas +20 modelos ya q en total serian +100 archivos de request
Si, lo ideal es ordenarlos por modelo y ahí ya se podría sacar el [Modelo] en el nombre del Request
hola que sucede cuando los datos de eloquent llegan nesteados al usar un with ??
public function getAllProyectos(){
$proyecto = Proyecto::with(‘servicio’,’estado’,’cliente’)->where(‘estado_id’, ‘=’, ‘2’)->orderBy(‘fechaAlta’, ‘DESC’)->get();
return response()->json($proyecto, 200);
}
y queremos hacer la validacion ???
con elementos de la raiz de los datos me funciona pero con los nesteados no hay manera , como se realizaria ??
lo tengo asi
return [
‘servicio’ => ‘required’,
‘fechaAlta’ => ‘required’,
‘estado_id’ => ‘required’,
‘pvp’ => ‘required’,
// ‘cliente.nombre’ => ‘required’,
// ‘cliente.nombreFiscal’ => ‘required’,
// ‘cliente.dni’ => ‘required’,
// ‘cliente.telefono’ => ‘required’,
// ‘cliente.email’ => ‘required’,
];