Cómo usar Form Request Validation para formularios enviados con Ajax

Cuando trabajamos con formularios en Laravel, lo más común es validar la información usando Form Requests. Esto permite mantener los controladores limpios y centralizar las reglas de validación en clases independientes.

Sin embargo, cuando el formulario se envía por medio de Ajax, especialmente desde modales o componentes dinámicos, surge una duda común: ¿cómo devolvemos los errores de validación en formato JSON sin dejar de usar la estructura nativa de Laravel?

En este artículo veremos una forma práctica de trabajar con Request validation con formularios Ajax, respuestas JSON y códigos HTTP correctos.

El problema común

En un formulario tradicional, si la validación falla, Laravel redirige automáticamente al usuario a la página anterior y muestra los errores en la vista.

Pero cuando usamos Ajax, no queremos una redirección. Queremos una respuesta JSON que podamos procesar desde JavaScript para mostrar los errores dentro de un modal, un toast o una alerta.

Una solución rápida suele ser hacer la validación directamente en el controlador:

$validator = Validator::make($request->all(), $rules, $messages);

if ($validator->fails()) {
    return response()->json([
        'success' => false,
        'errors' => $validator->errors()->all(),
    ], 422);
}

Aunque esto funciona, tiene una desventaja importante: la validación queda mezclada dentro del controlador. Conforme el proyecto crece, esto vuelve el código más difícil de mantener.

La mejor opción: seguir usando Form Request

Laravel permite seguir usando Form Requests aun cuando el formulario se envía por Ajax. Lo único que necesitamos es personalizar la respuesta cuando la validación falla.

Primero, podemos crear un Form Request:

php artisan make:request Admin/User/StoreUserRequest

Luego definimos las reglas de validación:

namespace App\Http\Requests\Admin\User;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class StoreUserRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'last_name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', Rule::unique('users', 'email')],
            'phone' => ['nullable', 'digits:10'],
            'id_profile_type' => ['required', 'exists:profile_types,id'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
            'enabled' => ['nullable', 'boolean'],
        ];
    }
}

Personalizar la respuesta JSON de validación

Por defecto, Laravel devuelve los errores agrupados por campo:

{
    "message": "The given data was invalid.",
    "errors": {
        "email": [
            "Este correo electrónico ya está registrado."
        ],
        "password": [
            "La contraseña debe tener al menos 8 caracteres."
        ]
    }
}

Este formato es útil cuando queremos marcar errores campo por campo. Pero si nuestra interfaz usa modales y queremos mostrar una lista general de errores, puede ser más cómodo devolver una lista plana.

Para eso podemos sobrescribir el método failedValidation dentro del StoreUserRequest:

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;

protected function failedValidation(Validator $validator): void
{
    throw new HttpResponseException(response()->json([
        'success' => false,
        'message' => 'Hay errores en el formulario.',
        'errors' => $validator->errors()->all(),
    ], 422));
}

Con esto, la respuesta quedará así:

{
    "success": false,
    "message": "Hay errores en el formulario.",
    "errors": [
        "El nombre es obligatorio.",
        "Este correo electrónico ya está registrado.",
        "La contraseña debe tener al menos 8 caracteres."
    ]
}

Controlador limpio usando el Form Request

Una vez que el Form Request está listo, el controlador puede mantenerse limpio:

use App\Http\Requests\Admin\User\StoreUserRequest;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Hash;
use Throwable;

public function store(StoreUserRequest $request): JsonResponse
{
    try {
        $validated = $request->validated();

        $user = User::create([
            'name' => $validated['name'],
            'last_name' => $validated['last_name'],
            'email' => $validated['email'],
            'phone' => $validated['phone'] ?? null,
            'id_profile_type' => $validated['id_profile_type'],
            'password' => Hash::make($validated['password']),
            'enabled' => $validated['enabled'] ?? 1,
        ]);

        return response()->json([
            'success' => true,
            'message' => 'Usuario registrado correctamente.',
            'user_id' => $user->id,
        ], 201);

    } catch (Throwable $e) {
        report($e);

        return response()->json([
            'success' => false,
            'message' => 'No fue posible registrar el usuario. Inténtalo nuevamente.',
        ], 500);
    }
}

Códigos HTTP recomendados

Es importante regresar códigos HTTP correctos para que el frontend pueda interpretar bien la respuesta.

  • 200: operación correcta.
  • 201: registro creado correctamente.
  • 401: usuario no autenticado.
  • 403: usuario autenticado, pero sin permisos.
  • 404: recurso no encontrado.
  • 422: error de validación.
  • 500: error inesperado del servidor.

Para formularios Ajax, una convención práctica sería:

Validación fallida  → 422
Creación correcta   → 201
Actualización       → 200
Eliminación         → 200
No encontrado       → 404
Sin permisos        → 403
Error inesperado    → 500

Conclusión

Usar Form Requests con formularios Ajax es una forma limpia y profesional de validar datos en Laravel sin sacrificar la experiencia de usuario.

La clave está en no llevar las reglas de validación al controlador, sino mantenerlas dentro del Request y personalizar la respuesta JSON cuando la validación falla.

Con esta estructura logramos:

  • Controladores más limpios.
  • Validaciones reutilizables.
  • Respuestas JSON consistentes.
  • Códigos HTTP correctos.
  • Mejor integración con formularios en modales.
  • Mejor experiencia de usuario usando Ajax.

Esta forma de trabajo es especialmente útil en paneles administrativos, sistemas internos, CRMs, ERPs y plataformas donde los formularios se muestran en modales o secciones dinámicas.

Si necesitas ayuda para crear un proyecto de software en Maria Tech contamos con los expertos para apoyarte.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *