Hablemos de validaciones en Laravel
Alguna vez pensaste que las validaciones en Laravel (controlador) te molestaban o quizá no sabías que existía otra forma de hacer validaciones que no sea esta, por ejemplo
1 2 3 4 | $validatedData = $request->validate([ 'title' => 'required|unique:posts|max:255', 'body' => 'required', ]); |
Ojo no digo que esté mal, simplemente que para validar formularios pequeños funciona bien, pero qué pasa si el formulario es más grande, que pasa si necesitas enviar mensajes personalizados, tal vez poner alias a las variables para que los mensajes se vean más amigables o que simplemente necesitas que tu proyecto tenga una estructura más ordenada.
Bueno para todo lo anterior laravel tiene una solución muy buena y se llaman Form Request Validation , el cual será el tema que veremos hoy , quizás lo hayas visto, pero quédate, que tal vez se te olvidó algo.
“Así que tomate un café o tu bebida favorita y sigamos en esto, que se pone interesante, Además al final pondré el link del repositorio para que descargues este proyecto”
Y, para empezar, crea tu proyecto porque empezaremos creando nuestro primer controlador con el comando artisan
1 | php artisan make:controller FormularioController |
y le crearemos dos funciones base que serán necesarias para poder hacer las validaciones, una será para poder acceder a la vista que creamos llamada index y la otra será mostrarDatos que será la que haga las validaciones, pero que tendrá un solo cambio que explicaremos más adelante, te dejo el código para que veas como queda.
1 2 3 4 5 6 7 8 9 | public function index() { return view('formulario'); } public function mostrarDatos(Request $request) { } |
Luego crearemos nuestra vista, que es donde pondremos nuestro código base, el archivo lo he llamado: formulario.blade.php al cual le agregaremos el siguiente código base
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"> <style> body{ background-color: #a7d4ef; } </style> </head> <body> <div class="container"> <div class="row"> <div class="col-md-6 mx-auto mt-5"> </div> </div> </div> </body> </html> |
Use el CDN de Bootstrap 4 para poder darle estilo al formulario para que visualmente se vea más ameno 😉
“Y continuando…”
Para la siguiente parte modificaremos las rutas en el archivo web.php que está ubicado en routes/web.php, y ponemos el siguiente código
1 2 | Route::get('/', 'FormularioController@index'); Route::post('/enviodatos','FormularioController@mostrarDatos')->name('enviodatos'); |
Te explico esta parte en el caso de que tengas dudas, la primera línea de código llama a la función index del de la clase llamado FormularioController, la segunda línea será que la que envíe los datos a la función mostrarDatos.
Después de hacer todo esto y explicarle cuáles serán sus funciones. Crearemos el formulario que validaremos, para darle contexto a esto, simulé un formulario de creación de un usuario con un rol que es bastante usado en los sistemas, básico a mi parecer, pero te dará una idea de lo que puedes llegar hacer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | <div class="card"> <div class="card-header tex"> Formulario </div> <div class="card-body"> <form method="post" action=""> {{ csrf_field() }} <div class="form-row"> <div class="col-md-6"> <div class="form-group"> <label for="nombre">Nombre</label> <input type="text" class="form-control" id="nombre" name="nombre"> <small class="form-text text-danger">Error de nombre.</small> </div> </div> <div class="col-md-6"> <div class="form-group"> <label for="ap_paterno">Apellido Paterno</label> <input type="text" class="form-control" id="ap_paterno" name="ap_paterno"> <small class="form-text text-danger">Error de apellido paterno.</small> </div> </div> <div class="col-md-6"> <div class="form-group"> <label for="ap_materno">Apellido Materno</label> <input type="text" class="form-control" id="ap_materno" name="ap_materno"> <small class="form-text text-danger">Error de apellido materno.</small> </div> </div> <div class="col-md-6"> <div class="form-group"> <label for="alias">Alias</label> <input type="text" class="form-control" id="alias" name="alias"> <small class="form-text text-danger">Error de alias.</small> </div> </div> </div> <div class="form-group"> <label for="email">Email</label> <input type="text" class="form-control" id="email" name="email"> <small class="form-text text-danger">Error de email.</small> </div> <div class="form-group"> <label for="nivel_acceso">Seleccione Cuenta:</label> <select class="form-control" id="nivel_acceso" name="nivel_acceso"> <option value="">Seleccione cuenta...</option> <option value="administrador">Administrador</option> <option value="superadmin">Super Administrador</option> <option value="usuario">Usuario</option> <option value="cliente">Cliente</option> </select> <small class="form-text text-danger">Error de select.</small> </div> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" name="sexo" value="m"> <label class="form-check-label">Masculino</label> </div> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" name="sexo" value="f"> <label class="form-check-label" >Femenino</label> </div> <small class="form-text text-danger">Error de radio.</small> <div class="form-group"> <label for="edad">Edad</label> <input type="number" class="form-control" id="edad" name="edad"> <small class="form-text text-danger">Error de edad.</small> </div> <button type="submit" class="btn btn-info"> Enviar </button> </form> </div> </div> |
Todo este código lo pones dentro del div base que creamos en el archivo formulario.blade.php, y lo pegamos dentro, siendo más específico dentro de esto
1 2 3 | <div class="col-md-6 mx-auto mt-5"> <!—Aquí pega el código!!!! à </div> |
Una vez hecho eso y si abrimos nuestro proyecto tendremos que ver algo como esto
Como puedes ver el formulario se ven los campos correspondientes con los mensajes como ejemplo de lo que debería resultar una vez hagamos todas las validaciones.
Seguido de eso vamos hacer lo siguiente, en el formulario agregaremos lo siguiente para que podamos conectar con la ruta que creamos, esta vez usaremos route() que nos permite asociarlo a la ruta que tiene por name enviodatos , y se tendrá que ver así
{{ route(‘enviodatos’) }}
¡Y si!, tiene que quedar como lo siguiente
1 | <form method="post" action="{{ route('enviodatos') }}"> |
¡Con esto tenemos los necesario para poder hacer las validaciones, pero… que nos falta?, si!! Exacto, nos falta los más importante para poder hacer las validaciones
“Viste que te he sorprendido, bueno si llegaste hasta aquí tómate otro sorbo de bebida para poder seguir, se te debe cansar la boca o el cerebro de leer ¬¬.”
Aprovecho para recordarte que si quieres saber mas sobre la organización de las vistas en Laravel, he creado un articulo muy detallado que seguro te va a interesar.
Si yo fuera un Request querría estar separado
Como lo dice ese subtitulo hacer las validaciones en este caso lo aremos aparte, ¿Por qué?, porque al hacer las validaciones en la función del controlador queda todo muy engorroso y muchas veces las validaciones son muchas, además estás quedarán más legible y personalizadas
Después de esa lata que te escribí arriba, con el siguiente comando crearemos el archivo Request
1 | php artisan make:request ValidarFormularioRequest |
Lo que hace eso comando es que crea una carpeta llamada Requests, como lo muestra la imagen siguiente
Y dentro de ella tendrás el archivo que usaremos para hacer las validaciones
“Cuando crees otro request la carpeta no se crea nuevamente, solo se crea el archivo”
Este archivo lo abres y tendrás unas funciones como la siguiente por defecto
1 2 3 4 5 6 7 8 9 10 | public function authorize() { return false; } public function rules() { return [ // ]; } |
Y como es costumbre explicamos para que sirven esas funciones
- La función que se llama authorize nos permite saber si podemos usar este request o no, con solo cambiar a true el return nos autoriza a poder usar esto.
- La segunda función es donde se hacen la reglas de validaciones, hay un montón, las puedes ver aquí Reglas de Validación.
Luego de la explicación cambiaremos esas funciones por lo siguiente
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public function authorize() { //esto lo cambiamos a true para poder que nos dé permiso para acceder al request return true; } public function rules() { return [ ]; } public function messages() { return [ ]; } public function attributes() { return [ ]; } |
Si te has dado cuenta se agregaron dos funciones messages y attributes:
- La función messages se encargará de hacer los mensajes para nuestras reglas de validaciones
- La función attribute nos permitirá poder poner “Alias” a nuestros atributos que llegan desde el formulario.
¿Qué cuales llegan y con qué nombre?
Es sencillo la forma de poder acceder a ellos es porque en el formulario a los inputs le pusiste name y con eso accederemos en este archivo request, así que no te compliques.
“Entonces ,manos a la obra…”
Mi proyecto, mis reglas
Una vez hecho lo anterior, dentro de la función rules crearemos las reglas de validaciones que harán que tenga cierto comportamiento, por ejemplo, que el campo no llegue vacío o que tenga cierta cantidad de caracteres, etc.
Y el código será el siguiente
1 2 3 4 5 6 7 8 9 10 11 12 13 | public function rules() { return [ 'nombre' => 'required|min:1|max:30', 'ap_paterno' => 'required|min:1|max:30', 'ap_materno' => 'required|min:1|max:30', 'alias' => 'required|min:1|max:30', 'email' => 'required|email', 'nivel_acceso' => 'required', 'sexo' => 'required', 'edad' => 'required|integer', ]; } |
Como puedes ver, las reglas son super básicas.
“Ojo queda a tu criterio cuantas usar”
¿Entonces que hacen esas reglas o como debería funcionar?
Para ello la explicación tampoco es tan complicada, y para dejártelo más claro tomaremos una línea y la explicaremos los más sencillo posible:
‘nombre’ => ‘required|min:1|max:30’,
Lo que se hace acá es que “nombre” es el que llega desde el formulario, ¿En cuál?, en el name del input, los siguientes son required, min y max:
- required dice que el campo es requerido
- min dice que hay un mínimo de caracteres que acepta.
- max dice que acepta un máximo de caracteres.
Como nota: el signo “|” se describe como filtro, también aparece en vue js si es que alguien lo ha usado.
Mi proyecto, mis mensajes
Para esta sección vamos a definir cuáles son los mensajes por regla dada al name que llega desde el formulario
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | public function messages() { return [ 'nombre.required' => 'El :attribute es obligatorio.', 'nombre.min' => 'El :attribute debe contener mas de una letra.', 'nombre.max' => 'El :attribute debe contener max 30 letras.', 'ap_paterno.required' => 'El :attribute es obligatorio.', 'ap_paterno.min' => 'El :attribute debe contener mas de una letra.', 'ap_paterno.max' => 'El :attribute debe contener max 30 letras.', 'ap_materno.required' => 'El :attribute es obligatorio.', 'ap_materno.min' => 'El :attribute debe contener mas de una letra.', 'ap_materno.max' => 'El :attribute debe contener max 30 letras.', 'alias.required' => 'El :attribute es obligatorio.', 'alias.min' => 'El :attribute debe contener mas de una caracter.', 'alias.max' => 'El :attribute debe contener max 30 letras.', 'email.required' => 'El :attribute es obligatorio.', 'email.email' => 'El :attribute debe ser un correo válido.', 'nivel_acceso.required' => 'Debe seleccionar un :attribute .', 'sexo.required' => 'Debes seleccionar tu :attribute .', 'edad.required' => 'El :attribute es obligatorio.', 'edad.integer' => 'El :attribute debe ser entero.', ]; } |
En este caso también es sencillo saber cuál es la funcionalidad y al igual que la anterior sección te explicare una línea
‘nombre.required’ => ‘El :attribute es obligatorio.’,
En ese código yo hago que el atributo nombre con la regla required le asigno un mensaje definido por mí, ahora si te diste cuenta hay una palabra :attribute hace referencia al atributo nombre y que en la siguiente sección veremos.
Mi proyecto, mis …. Bueno ya sabes
Mis atributos ajajajaj
Pero bueno, siguiendo con el tema y estando ya en la fase final de este artículo, veremos la función attribute la cual se encargará de asignar “alias” a nuestros atributos
“Como nota te debo decir que el uso de esta función es opcional ya que perfectamente puedes poner en vez de :atributte solo el alias en los mensajes, pero yo la uso igualmente porque es algo que está a mano para usar.”
1 2 3 4 5 6 7 8 9 10 11 12 13 | public function attributes() { return [ 'nombre' => 'nombre de usuario', 'ap_paterno' => 'apellido paterno', 'ap_materno' => 'apellido materno', 'alias' => 'alias del usuario', 'email' => 'correo electronico', 'nivel_acceso' => 'nivel de acceso', 'sexo' => 'sexo', 'edad' => 'edad', ]; } |
Y sacando deducciones creo que te has dado cuenta y si no es así te lo explico.
Viste que arriba puse :atributte , lo que estaba haciendo ahí es para poder acceder desde la función attribute y asignarles un alias, Pero ¿cómo?, yo te responderé así de sencillo
‘nombre’ => ‘nombre de usuario’,
En esa línea yo le digo que al atributo nombre, asignarle un alias definido por mi llamado “nombre de usuario”
Una vez llegado hasta acá tendríamos listo el request
¿Ahora como lo puedo usar?
Ahora iremos al controlador que creamos anteriormente, importaremos este request creado de la siguiente manera
1 | use App\Http\Requests\ValidarFormularioRequest; |
y para poder darle uso, en la función reemplazamos Request por ValidarFormularioRequest, por lo tanto, quedará así
1 2 3 4 | public function mostrarDatos(ValidarFormularioRequest $request) { //código que desees agregar acá } |
Dejándolo así ya funciona, porque dentro de la función podrás hacer los procesos que quieres con la data que te llega, guardar, editar y demás, esa función solo tiene como objetivo aclara como es que funciona un Form Validation request
¿No te falta hacer algo más?
«Claro se me había olvidado ajaja.»
Para ver los resultados que retornan y poder mostrarlos en la vista es necesarios hacer validaciones, y lo aremos de la siguiente manera en el formulario (esta es la forma individual)
1 2 3 4 5 | @if ($errors->has('nombre')) <small class="form-text text-danger"> {{ $errors->first('nombre') }} </small> @endif |
O las puedes hacer así para que te muestre una lista de errores
1 2 3 4 5 6 7 8 9 10 11 | @if ($errors->any()) <div class="col-md-12"> <div class="alert alert-danger"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div> </div> @endif |
Pero como yo quiero que se vea como lo simulamos anteriormente usaremos la forma individual.
Explicaremos entonces que la condición if me va a validar que si la variable que retorna “$errors” tiene un atributo “nombre” me muestre el primer mensaje que llegue “$errors->first(‘nombre’)”, al explicarte esto debo tener una base y si la tengo, te la muestro en una imagen
Ese es el resultado de hacer un dd en la vista cuando me retornan los errores, haciendo esto {{ dd($errors) }} .
Si te fijas bien aparecen todos los errores que retornaron desde el request.
“Lo de {{ dd($errors) }} es solo para hacer pruebas de cómo llega la data , no es necesario que lo agregues”
Una vez listo el código del formulario quedará así
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | <div class="card"> <div class="card-header tex"> Formulario </div> <div class="card-body"> <form method="post" action="{{ route('enviodatos') }}"> {{ csrf_field() }} <div class="form-row"> <div class="col-md-6"> <div class="form-group"> <label for="nombre">Nombre</label> <input type="text" class="form-control" id="nombre" name="nombre"> @if ($errors->has('nombre')) <small class="form-text text-danger">{{ $errors->first('nombre') }}</small> @endif </div> </div> <div class="col-md-6"> <div class="form-group"> <label for="ap_paterno">Apellido Paterno</label> <input type="text" class="form-control" id="ap_paterno" name="ap_paterno"> @if ($errors->has('ap_paterno')) <small class="form-text text-danger">{{ $errors->first('ap_paterno') }}</small> @endif </div> </div> <div class="col-md-6"> <div class="form-group"> <label for="ap_materno">Apellido Materno</label> <input type="text" class="form-control" id="ap_materno" name="ap_materno"> @if ($errors->has('ap_materno')) <small class="form-text text-danger">{{ $errors->first('ap_materno') }}</small> @endif </div> </div> <div class="col-md-6"> <div class="form-group"> <label for="alias">Alias</label> <input type="text" class="form-control" id="alias" name="alias"> @if ($errors->has('alias')) <small class="form-text text-danger">{{ $errors->first('alias') }}</small> @endif </div> </div> </div> <div class="form-group"> <label for="email">Email</label> <input type="text" class="form-control" id="email" name="email"> @if ($errors->has('email')) <small class="form-text text-danger">{{ $errors->first('email') }}</small> @endif </div> <div class="form-group"> <label for="nivel_acceso">Seleccione Cuenta:</label> <select class="form-control" id="nivel_acceso" name="nivel_acceso"> <option value="">Seleccione cuenta...</option> <option value="administrador">Administrador</option> <option value="superadmin">Super Administrador</option> <option value="usuario">Usuario</option> <option value="cliente">Cliente</option> </select> @if ($errors->has('nivel_acceso')) <small class="form-text text-danger">{{ $errors->first('nivel_acceso') }}</small> @endif </div> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" name="sexo" value="m"> <label class="form-check-label">Masculino</label> </div> <div class="form-check form-check-inline"> <input class="form-check-input" type="radio" name="sexo" value="f"> <label class="form-check-label" >Femenino</label> </div> @if ($errors->has('sexo')) <small class="form-text text-danger">{{ $errors->first('sexo') }}</small> @endif <div class="form-group"> <label for="edad">Edad</label> <input type="number" class="form-control" id="edad" name="edad"> @if ($errors->has('edad')) <small class="form-text text-danger">{{ $errors->first('edad') }}</small> @endif </div> <button type="submit" class="btn btn-info"> Enviar </button> </form> </div> </div> |
Ahora ve hacia la vista creada y podrás ver un formulario sin mensajes
Ahora para que vea que funciona la validación presiona ese botón enviar y te mostrará los mensajes en cada parte que no cumple con las reglas que se impusieron
Como ves en las imágenes, los resultados son más que geniales, hemos logrado hacer validaciones, sin necesidad de agregar mucho código al controlador , además de que pudimos personalizarlos a nuestro gusto.
El plus de Ajax
Como plus para este proyecto, si eres de las personas que busca cosas como
¿Se puede trabajar con ajax?
Yo te responderé , si se puede solo que el retorno llegará al error de ajax, un ejemplo con nuestro proyecto sería algo así
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | $.ajax({ url:"/ruta", data:{ 'nombre': 'nombre', //lista de campos que quieres enviar }, type:"post", success: function (datos) { }, error:function (error) { //valido que llegue errors if(error.responseJSON.hasOwnProperty('errors')){ //valido que tenga el error nombre if(error.responseJSON.errors.nombre){ console.log(error.responseJSON.errors.nombre); } } } }); |
Como puedes ver primero pregunto si la propiedad «errors» se encuentra, entonces entro y pregunto si el «errors» tiene nombre , que me muestre en el console.log el resultado., obviamente puedes usar otra forma de mostrarlo , usando toastr, agregar el texto con jquery u otra forma.
Y por fin llegamos al final
Llegamos hasta acá con esfuerzo y me gustaría darte una conclusión, como corresponde en los artículos.
La gracia de hacer todo esto que cada vez tenga menos código que tratar en el controlador y que puedas hacer que las validaciones sean más amenas tanto para ti como para el usuario que las va a ver, podrás hacer mensajes personalizados, asignarles alias a los atributos y entre otras cosas más que no podre poner acá porque si hacía este artículo más largo se pondría pesado para el lector.
Pon en práctica esta forma de hacer estas validaciones del lado del servidor ya que en los sistemas que estarás desarrollando, validar siempre será necesario.
Y como lo prometí aquí está el link:
https://github.com/Metalwarcry/formularioValidacion
Si te gusta el artículo dóname un café http://buymeacoff.ee/mKwgzIQAd , Saludos.