8. Validación de datos
8.1. Sistema de validación en controladores
El framework implementa un sistema robusto de validación principalmente a través de la propiedad $validations en DomainController. Esta es la forma recomendada para implementar validaciones en los controladores.
En los controladores que heredan de DomainController, defines las reglas de validación en el constructor:
use App\Base\DomainController;
class ProductsController extends DomainController
{
protected $productsService;
public function __construct(ProductsService $service)
{
parent::__construct($service);
$this->validations = [
'headers' => ['x-tienda' => 'required|numeric'],
'body' => [
'name' => 'required|string',
'price' => 'required:zeroable|numeric',
'category_id' => 'required|numeric'
]
];
}
}
Con esta configuración, las validaciones se aplicarán automáticamente en los métodos store() y update() heredados de DomainController. El sistema distingue automáticamente entre operaciones de creación y actualización, haciendo las validaciones menos estrictas para actualizaciones parciales.
Para métodos personalizados, puedes utilizar directamente la clase ValidaRequest:
use App\Validations\ValidaRequest;
public function customMethod(Request $request)
{
ValidaRequest::make($request)
->validateHeaders(['x-tienda' => 'required|numeric'])
->validateBody(['parameter' => 'required|string'])
->handle();
// Procesar la solicitud
return response()->json($result, 200);
}
8.2. Reglas de validación disponibles
Las reglas de validación se implementan a través del patrón Factory en App\Validations\Rules. Las reglas disponibles incluyen:
| Regla | Descripción | Ejemplo |
|---|---|---|
required | El campo debe estar presente y no vacío | 'name' => 'required' |
required:zeroable | Como required, pero permite el valor 0 | 'price' => 'required:zeroable' |
email | Debe ser una dirección de correo válida | 'email' => 'email' |
numeric | Debe ser un valor numérico | 'age' => 'numeric' |
string | Debe ser una cadena de texto | 'name' => 'string' |
length | Debe tener exactamente la longitud especificada | 'code' => 'length:8' |
min | Longitud mínima de la cadena | 'password' => 'min:8' |
max | Longitud máxima de la cadena | 'description' => 'max:255' |
fiscal | Valida identificadores fiscales (requiere código de país) | 'taxId' => 'fiscal:ES' |
Puedes encadenar múltiples reglas usando el carácter pipe |:
'password' => 'required|string|min:8|max:64'
Las reglas de validación son facilemte extendibles. Basta con crear un nuevo archivo en la carpeta App\Validations\Rules y añadir las reglas que necesitemos, como se verá en el siguiente punto.
8.3. Validación de campos obligatorios
La regla required se utiliza para campos que deben estar presentes en la solicitud:
$this->validations = [
'body' => [
'name' => 'required',
'email' => 'required|email',
'age' => 'required|numeric'
]
];
Para campos que deben permitir explícitamente el valor cero, puedes usar required:zeroable:
$this->validations = [
'body' => [
'price' => 'required:zeroable|numeric',
'discount' => 'required:zeroable|numeric'
]
];
Este formato es especialmente útil para valores que pueden ser legítimamente cero (como precios o descuentos) pero que aún son obligatorios en la solicitud.
8.4. Validación condicional
El sistema de validación interno de DomainController distingue automáticamente entre operaciones de creación y actualización:
- Para el método
store(), todos los campos marcados comorequireddeben estar presentes. - Para el método
update(), los campos marcados comorequiredsolo se validan si están presentes en la solicitud.
Esta distinción permite actualizaciones parciales donde solo se necesitan modificar algunos campos.
8.5. Mensajes de error personalizados
Cuando ocurre un error de validación, el sistema lanza una ValidationException con los detalles de los errores. Esta excepción se captura automáticamente y se convierte en una respuesta HTTP 400 (Bad Request) con información sobre los campos que fallaron.
Ejemplo de respuesta de error:
{
"error": "Validation failed",
"code": 400,
"errors": {
"name": ["required"],
"email": ["email"]
}
}
8.6. Ejemplo completo
Veamos un ejemplo completo de un controlador con diferentes tipos de validaciones:
namespace ApiLayer\Modules\Shop\Domains\Products;
use App\Base\DomainController;
use App\Core\RequestFactory as Request;
use App\Validations\ValidaRequest;
use function App\Helpers\response;
class ProductsController extends DomainController
{
protected $productsService;
public function __construct(ProductsService $service)
{
parent::__construct($service);
// Estas validaciones se aplicarán automáticamente en store() y update()
$this->validations = [
'headers' => [
'x-tienda' => 'required|numeric',
'authorization' => 'required|string'
],
'body' => [
'name' => 'required|string',
'code' => 'required|string|length:8',
'price' => 'required:zeroable|numeric',
'category_id' => 'required|numeric'
]
];
}
// La validación se aplica automáticamente en los métodos heredados:
// - public function store(Request $request) { ... }
// - public function update($id, Request $request) { ... }
// Para métodos personalizados, usamos ValidaRequest directamente:
public function bulkUpdate(Request $request)
{
ValidaRequest::make($request)
->validateHeaders(['x-tienda' => 'required|numeric'])
->validateBody([
'products' => 'required',
'updated_by' => 'required|numeric'
])
->handle();
$result = $this->productsService->bulkUpdate($request->all());
return response()->json($result, 200);
}
public function changeStatus($id, Request $request)
{
ValidaRequest::make($request)
->validateHeaders(['x-tienda' => 'required|numeric'])
->validateBody(['status' => 'required|string'])
->handle();
$result = $this->productsService->changeStatus($id, $request->status);
return response()->json($result, 200);
}
}
Este ejemplo demuestra:
- Configuración de validaciones a nivel de controlador para
store()yupdate() - Validación personalizada para métodos específicos
- Cómo usar diferentes reglas para diferentes tipos de datos
- La combinación de validaciones de cabecera y cuerpo
La validación es una capa crítica que garantiza que los datos que llegan a tus servicios cumplen con los requisitos establecidos, mejorando así la robustez y seguridad de tu aplicación.