Laravel: Validar formularios (parte 15)
Archivos:
- resources/views/cursos/create.blade.php
- appHttpControllersCursoController.php
- lang/
- config/app.php
- app/Http/Requests
Validar campos requeridos
Campos no vacíos
Cuando un campo de la base de datos no tenga la propiedad nullable, no deberíamos intentar guardar campos vacíos sin mostrar un error. Para eso tenemos que validar los campos del formulario.
En la función store, usaremos un método del objeto request llamado validate. Dentro del método tendremos un array con las reglas de validación:
$request->validate([
]);
Así especificamos que queremos que los campos sean requeridos:
$request->validate([
'name' => 'required',
'description' => 'required',
'categoria' => 'required',
]);
Mostrar errores
Debajo de cada campo, mostraremos un mensaje de error, usando una directiva de blade:
@error('record')
*{{$message}}
@enderror
El error lo maquetaremos de la siguiente manera:
(...)
<label>
Nombre:<br>
<input type="text" name="name">
</label>
@error('name')
<br>
<small>*{{$message}}</small>
<br>
@enderror
<br>
Hacemos lo mismo con todos los campos:
<br> <form action="{{route('cursos.store')}}" method="POST">
@csrf
<label>
Nombre:<br>
<input type="text" name="name">
</label>
@error('name')
<br>
<small>*{{$message}}</small>
<br>
@enderror
<br>
<label>
Descripción:<br>
<textarea name="description" rows="5"></textarea>
</label>
@error('description')
<br>
<small>*{{$message}}</small>
<br>
@enderror
<br>
<label>
Categoría:<br>
<input type="text" name="categoria">
</label>
@error('categoria')
<br>
<small>*{{$message}}</small>
<br>
@enderror
<br>
<button type="submit">Enviar formulario</button>
</form>
Traducción de los mensajes
Vamos a la carpeta lang, donde podemos ver todos los mensajes en inglés de la paginación, la valicación…. Duplicamos la carpeta y le cambiamos el nombre a es.
Los mensajes ya están traducidos por la comunidad de Laravel, los podemos descargar de aquí: https://github.com/Laraveles/spanish. Reemplazamos el contenido de nuestros archivos con el contenido de los de spanish/resources/lang/es/.
Nos dirigimos al archivo config/app.php y editamos el valor «locale»:
'locale' => 'es',
Más personalización
Dentro del archivo de traducción español validation.php encontraremos este array:
'attributes' => [],
El campo nombre de nuestro formulario se llama name, con lo que podemos hacer lo siguiente:
'attributes' => [
'name' => 'nombre'
],
Asi nuestro mensaje de validación no será:
*El campo name es obligatorio.
Sino así:
*El campo nombre es obligatorio.
Dejaremos el array de la siguiente forma:
'attributes' => [
'name' => 'nombre del curso',
'description' => 'descripción'
],
Mantener los valores de los campos aunque haya un error
Si el usuario olvida un campo pero rellena otros, el valor de los campos válidos se mantendrá cuando se recargue el formulario.
Usamos el método old en tags value de los input del formulario, y dentro del textarea:
<form action="{{route('cursos.store')}}" method="POST">
@csrf
<label>
Nombre:<br>
<input type="text" name="name" value="{{old('name')}}">
</label>
@error('name')
<br>
<small>*{{$message}}</small>
<br>
@enderror
<br>
<label>
Descripción:<br>
<textarea name="description" rows="5">{{old('description')}}</textarea>
</label>
@error('description')
<br>
<small>*{{$message}}</small>
<br>
@enderror
<br>
<label>
Categoría:<br>
<input type="text" name="categoria" value="{{old('categoria')}}">
</label>
@error('categoria')
<br>
<small>*{{$message}}</small>
<br>
@enderror
<br>
<button type="submit">Enviar formulario</button>
</form>
En el proyecto tenemos otro formulario, el de edición. Antes de continuar con el siguiente paso de la validación, haremos los cambios convenientes al formulario de edición: añadimos la regla de validación al método update y los mensajes de error a cada uno de los labels.
Un caso específico de los formularios de edición
Comentemos un caso concreto: Si en el formulario de editar curso hago ciertos cambios en un campo, pero dejo otro vacío, al recargar el formulario mostrará el mensaje de error, pero no hará caso de los recientes cambios, sino que mostrará el valor anterior ya almacenado en la base de datos.
Para ello, el método old que antes utilizamos podemos volver a usarlo con un segundo parámetro, el valor por defecto del campo:
(...)
<h1>Edición de curso</h1>
<form action="{{route('cursos.update',$curso)}}" method="POST">
@csrf
@method('put')
<label>
Nombre:<br>
<input type="text" name="name" value="{{old('name',$curso->name)}}">
</label>
@error('name')
<br>
<small>*{{$message}}</small>
<br>
@enderror
<br>
<label>
Descripción:<br>
<textarea name="description" rows="5">{{old('description',$curso->description)}}</textarea>
</label>
@error('description')
<br>
<small>*{{$message}}</small>
<br>
@enderror
<br>
<label>
Categoría:<br>
<input type="text" name="categoria" value="{{old('categoria',$curso->categoria)}}">
</label>
@error('categoria')
<br>
<small>*{{$message}}</small>
<br>
@enderror
<br>
<button type="submit">Actualizar curso</button>
</form>
Es decir, el campo por defecto seguirá siendo el mismo, pero rescatará el valor más reciente antes de actualizar el formulario gracias al método old.
Más de una regla de validación
Si quisiéramos que el campo nombre, además de que no estuviera vacío, tuviera un máximo de 10 caracteres y la descripción un mínimo de 5, usaremos una segunda regla de validación con el símbolo |. En la función store:
$request->validate([
'name' => 'required|max:10',
'description' => 'required|min:5',
'categoria' => 'required',
]);
Hemos hecho la validación en el controlador. En ocasiones, las reglas de validación son muchas, y en este caso es recomendable hacerlo en un archivo a parte. De hecho, es lo recomendado en cualquier caso.
Archivo de validación
Enlace a la documentación de Laravel: https://laravel.com/docs/9.x/validation#form-request-validation
Usaremos un comando de artisan para crear un archivo de validación del formulario para guardar registros:
php artisan make:request StoreCurso
Se ha creado un archivo php en la ruta:Http/Requests/ . En el mismo hay dos métodos: auth() sirve para establecer las reglas de los permisos de usuario, el cual debemos hacer que de momento devuelva un valor true. En el método rules() allí ponemos nuestras reglas de validación, que antes estaban en el método store:
public function authorize()
{
return true;
}
(...)
public function rules()
{
return [
'name' => 'required|max:10',
'description' => 'required|min:5',
'categoria' => 'required',
];
}
En la parte superior de controlador añadimos la línea:
use App\Http\Requests\StoreCurso;
Borramos donde antes teníamos las reglas de validación (el método store) y añadimos la nueva clase como parámetro de la función:
public function store(StoreCurso $request){
(...)
Los campos ya deberían validarse como anteriormente.
Función attributes
Bajo el método rules() tenemos la posibilidad de usar este otro método para personalizar el nombre de los atributos del formulario en el mensaje de error, como ya hicimos en el archivo de traducción:
public function attributes()
{
return [
'name' => 'del nombre del curso'
];
}
Función message
public function attributes()
{
return [
'name' => 'del nombre del curso'
];
}
Para personalizar el mensaje de error completo del campo description, para la regla de validación required:
public function messages()
{
return [
'description.require' => 'Es obligatoria una descripción.'
];
}
Vemos que como el campo tiene dos reglas de validación, necesitamos especificar para qué regla vamos a mostrar este mensaje personalizado.
Recursos:
Enlace de Youtube: https://www.youtube.com/watch?v=KbpbqZshUus&list=PLZ2ovOgdI-kWWS9aq8mfUDkJRfYib-SvF&index=20, https://www.youtube.com/watch?v=Ze-Sg2BT3mc&list=PLZ2ovOgdI-kWWS9aq8mfUDkJRfYib-SvF&index=21