La validación no pertenece al controlador. Laravel resuelve un FormRequest desde el contenedor y lo valida antes de llamar a tu método, de modo que cuando tu código se ejecuta los datos ya son de fiar. (Documentación de Laravel: Validation › Form Request Validation.)
php artisan make:request StoreLicenseRequest
// app/Http/Requests/StoreLicenseRequest.php
class StoreLicenseRequest extends FormRequest
{
public function authorize(): bool
{
return true;
}
/**
* @return array<string, array<int, string>>
*/
public function rules(): array
{
return [
'name' => ['required', 'string', 'max:100'],
'domain' => ['required', 'string', 'max:100'],
];
}
}
El authorize() generado devuelve false. Así es Laravel, seguro por defecto: una petición en la que olvidaste pensar es una petición que nadie puede hacer. Aquí todo consumidor autenticado puede crear una licencia, así que devuelve true. Lo mismo hacen los demás FormRequests de este libro, porque quién puede llamar a un endpoint se decide en su controlador, a partir del Capítulo 4.
No hay método messages(). Laravel ya tiene un mensaje traducido para cada regla, en lang/en/validation.php. Ejecuta php artisan lang:publish para traer esos archivos a tu proyecto, y añade una carpeta lang/es cuando necesites español. Si un campo necesita una redacción más amable, los arrays attributes y custom de ese archivo son el lugar para ello. Escribir un mensaje por regla y por campo en cada request es un trabajo que el framework ya hizo. (Documentación de Laravel: Validation › Specifying Custom Messages in Language Files.)
Lo que ganas al mover la validación aquí:
- Un solo lugar que leer. Quien quiera saber qué acepta el endpoint abre un archivo.
- Un solo lugar que cambiar. Cuando
domaintenga que ser más largo, editas una regla. - Un controlador que puede dar cosas por sentadas. Pasada la frontera, los datos son válidos. Ninguna comprobación defensiva más adentro.
Los errores de validación como JSON
Envíale a ese endpoint un cuerpo sin nombre y Laravel responde con un 422:
{
"message": "The name field is required.",
"errors": {
"name": ["The name field is required."]
}
}
Ese es el error de validación estándar de Laravel. Consérvalo. Todo cliente que conozca Laravel lo entiende, los helpers de testing hacen sus aserciones contra él, y nadie de tu equipo tiene que aprender un formato inventado por ti.
Hay una trampa. Laravel genera JSON cuando la petición pide JSON, es decir, cuando trae la cabecera Accept: application/json. Un consumidor que olvida la cabecera recibe una redirección, que es lo correcto para un formulario y lo incorrecto para una API. No lo arregles en cada clase de request. Arréglalo una vez:
// bootstrap/app.php
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->shouldRenderJsonWhen(
fn (Request $request) => $request->is('api/*')
|| $request->expectsJson(),
);
})
Ahora todo lo que cuelga de /api responde en JSON, envíe lo que envíe el cliente, y eso incluye cualquier otro error que Laravel renderice, no solo la validación. La decisión se tomó una vez, en el lugar que el framework ofrece para ella, y ninguna petición suelta puede olvidarla. (Documentación de Laravel: Error Handling › Rendering Exceptions.)
El Capítulo 7 parte de aquí para los errores que no son de validación.