7. Formularios y Validación en Laravel
Introducción
Section titled “Introducción”Los formularios son una parte fundamental de cualquier aplicación web, ya que permiten a los usuarios interactuar con el sistema. Laravel ofrece herramientas robustas para la creación de formularios y la validación de datos, lo que facilita el desarrollo de aplicaciones seguras y confiables.
Crear formularios con Blade
Section titled “Crear formularios con Blade”Blade es el motor de plantillas de Laravel que nos permite crear formularios HTML de manera sencilla y elegante. A continuación, veremos cómo crear diferentes tipos de formularios utilizando Blade.
Estructura básica de un formulario
Section titled “Estructura básica de un formulario”<form action="{{ route('products.store') }}" method="POST" enctype="multipart/form-data"> @csrf
<div class="mb-3"> <label for="name" class="form-label">Nombre del producto</label> <input type="text" class="form-control" id="name" name="name" value="{{ old('name') }}"> </div>
<div class="mb-3"> <label for="price" class="form-label">Precio</label> <input type="number" class="form-control" id="price" name="price" step="0.01" value="{{ old('price') }}"> </div>
<div class="mb-3"> <label for="description" class="form-label">Descripción</label> <textarea class="form-control" id="description" name="description" rows="3">{{ old('description') }}</textarea> </div>
<div class="mb-3"> <label for="image" class="form-label">Imagen</label> <input type="file" class="form-control" id="image" name="image"> </div>
<button type="submit" class="btn btn-primary">Guardar producto</button></form>Elementos clave en formularios Blade
Section titled “Elementos clave en formularios Blade”- @csrf: Genera un token CSRF para proteger contra ataques de falsificación de solicitudes entre sitios.
- route(): Genera URLs para rutas con nombre, evitando URLs hardcodeadas.
- old(): Recupera valores antiguos de la sesión en caso de errores de validación.
- enctype=“multipart/form-data”: Necesario para formularios que incluyen carga de archivos.
Formularios para diferentes métodos HTTP
Section titled “Formularios para diferentes métodos HTTP”Blade permite trabajar con diferentes métodos HTTP como PUT, PATCH o DELETE, que no son soportados nativamente por los formularios HTML:
<form action="{{ route('products.update', $product->id) }}" method="POST"> @csrf @method('PUT')
<!-- Campos del formulario -->
<button type="submit">Actualizar producto</button></form>Formularios con relaciones
Section titled “Formularios con relaciones”Para formularios que manejan relaciones entre modelos, podemos usar selectores:
<div class="mb-3"> <label for="category_id" class="form-label">Categoría</label> <select class="form-control" id="category_id" name="category_id"> <option value="">Seleccione una categoría</option> @foreach($categories as $category) <option value="{{ $category->id }}" {{ old('category_id') == $category->id ? 'selected' : '' }}> {{ $category->name }} </option> @endforeach </select></div>Validaciones en controladores
Section titled “Validaciones en controladores”Laravel proporciona varias formas de validar datos. La más directa es utilizando el método validate() en el controlador.
Validación básica en controladores
Section titled “Validación básica en controladores”public function store(Request $request){ $validated = $request->validate([ 'name' => 'required|string|max:255', 'price' => 'required|numeric|min:0', 'description' => 'required|string', 'category_id' => 'required|exists:categories,id', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', ]);
// Crear el producto con los datos validados Product::create($validated);
return redirect()->route('products.index') ->with('success', 'Producto creado correctamente');}Reglas de validación comunes
Section titled “Reglas de validación comunes”- required: El campo debe estar presente y no vacío.
- string: El campo debe ser una cadena de texto.
- numeric: El campo debe ser un número.
- integer: El campo debe ser un número entero.
- min:valor: El campo debe tener un valor mínimo (para números) o longitud mínima (para strings).
- max:valor: El campo debe tener un valor máximo o longitud máxima.
- email: El campo debe ser una dirección de email válida.
- unique:tabla,columna: El campo debe ser único en la tabla especificada.
- exists:tabla,columna: El campo debe existir en la tabla especificada.
- confirmed: El campo debe tener un campo de confirmación (ej: password_confirmation).
Mensajes de error personalizados
Section titled “Mensajes de error personalizados”Podemos personalizar los mensajes de error de validación:
$validated = $request->validate( [ 'name' => 'required|string|max:255', 'price' => 'required|numeric|min:0', ], [ 'name.required' => 'El nombre del producto es obligatorio', 'price.required' => 'El precio es obligatorio', 'price.numeric' => 'El precio debe ser un número', 'price.min' => 'El precio no puede ser negativo', ]);Validación con FormRequest
Section titled “Validación con FormRequest”Para validaciones más complejas o reutilizables, Laravel ofrece las clases FormRequest, que encapsulan la lógica de validación.
Crear un FormRequest
Section titled “Crear un FormRequest”php artisan make:request StoreProductRequestEsto generará una nueva clase en app/Http/Requests/StoreProductRequest.php.
Estructura de un FormRequest
Section titled “Estructura de un FormRequest”<?php
namespace AppHttpRequests;
use IlluminateFoundationHttpFormRequest;
class StoreProductRequest extends FormRequest{ /** * Determine if the user is authorized to make this request. */ public function authorize(): bool { return true; // Cambia a true para permitir la solicitud }
/** * Get the validation rules that apply to the request. * * @return array<string, IlluminateContractsValidationValidationRule|array|string> */ public function rules(): array { return [ 'name' => 'required|string|max:255', 'price' => 'required|numeric|min:0', 'description' => 'required|string', 'category_id' => 'required|exists:categories,id', 'image' => 'nullable|image|mimes:jpeg,png,jpg,gif|max:2048', ]; }
/** * Get custom messages for validator errors. * * @return array<string, string> */ public function messages(): array { return [ 'name.required' => 'El nombre del producto es obligatorio', 'price.required' => 'El precio es obligatorio', 'price.numeric' => 'El precio debe ser un número', 'price.min' => 'El precio no puede ser negativo', ]; }}Usar FormRequest en el controlador
Section titled “Usar FormRequest en el controlador”public function store(StoreProductRequest $request){ // La validación ya se ha realizado automáticamente // Si llegamos aquí, los datos son válidos
$product = Product::create($request->validated());
// Manejar la carga de imágenes si es necesario if ($request->hasFile('image')) { $path = $request->file('image')->store('products', 'public'); $product->update(['image_path' => $path]); }
return redirect()->route('products.index') ->with('success', 'Producto creado correctamente');}Mostrar errores en vistas
Section titled “Mostrar errores en vistas”Cuando la validación falla, Laravel redirige automáticamente al usuario a la página anterior y almacena los errores en la sesión. Podemos mostrar estos errores en nuestras vistas Blade.
Verificar si hay errores
Section titled “Verificar si hay errores”@if ($errors->any()) <div class="alert alert-danger"> <ul> @foreach ($errors->all() as $error) <li>{{ $error }}</li> @endforeach </ul> </div>@endifMostrar errores específicos
Section titled “Mostrar errores específicos”<div class="mb-3"> <label for="name" class="form-label">Nombre del producto</label> <input type="text" class="form-control @error('name') is-invalid @enderror" id="name" name="name" value="{{ old('name') }}"> @error('name') <div class="invalid-feedback">{{ $message }}</div> @enderror</div>Componente de error reutilizable
Section titled “Componente de error reutilizable”Podemos crear componentes Blade para mostrar errores de manera consistente:
@props(['name', 'label', 'type' => 'text'])
<div class="mb-3"> <label for="{{ $name }}" class="form-label">{{ $label }}</label> <input type="{{ $type }}" class="form-control @error($name) is-invalid @enderror" id="{{ $name }}" name="{{ $name }}" value="{{ old($name) }}" {{ $attributes }} > @error($name) <div class="invalid-feedback">{{ $message }}</div> @enderror</div>Y usarlo en nuestros formularios:
<x-form.input name="name" label="Nombre del producto" /><x-form.input name="price" label="Precio" type="number" step="0.01" />Validaciones personalizadas
Section titled “Validaciones personalizadas”Laravel permite crear reglas de validación personalizadas para casos específicos que no están cubiertos por las reglas estándar.
Usando Closures
Section titled “Usando Closures”$validator = Validator::make($request->all(), [ 'code' => [ 'required', function ($attribute, $value, $fail) { if (strtoupper(substr($value, 0, 3)) !== 'PRD') { $fail('El código del producto debe comenzar con PRD'); } }, ],]);Creando una regla personalizada
Section titled “Creando una regla personalizada”php artisan make:rule ProductCodeEsto generará una nueva clase en app/Rules/ProductCode.php:
<?php
namespace AppRules;
use Closure;use IlluminateContractsValidationValidationRule;
class ProductCode implements ValidationRule{ /** * Run the validation rule. */ public function validate(string $attribute, mixed $value, Closure $fail): void { if (strtoupper(substr($value, 0, 3)) !== 'PRD') { $fail('El código del producto debe comenzar con PRD'); } }}Y usarla en la validación:
use AppRulesProductCode;
$request->validate([ 'code' => ['required', new ProductCode],]);Validadores condicionales
Section titled “Validadores condicionales”A veces necesitamos aplicar reglas de validación solo bajo ciertas condiciones:
$request->validate([ 'payment_method' => 'required|in:credit_card,bank_transfer', 'card_number' => 'required_if:payment_method,credit_card', 'expiration_date' => 'required_if:payment_method,credit_card', 'cvv' => 'required_if:payment_method,credit_card', 'bank_account' => 'required_if:payment_method,bank_transfer',]);Conclusión
Section titled “Conclusión”Laravel proporciona un sistema completo y flexible para la creación de formularios y la validación de datos. Desde validaciones simples en controladores hasta reglas personalizadas complejas, Laravel ofrece todas las herramientas necesarias para garantizar que los datos que ingresan a nuestra aplicación sean válidos y seguros.
Recuerda siempre validar los datos de entrada para proteger tu aplicación contra datos maliciosos o incorrectos, y proporcionar retroalimentación clara a los usuarios cuando se producen errores de validación.