Skip to main content

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:

ReglaDescripciónEjemplo
requiredEl campo debe estar presente y no vacío'name' => 'required'
required:zeroableComo required, pero permite el valor 0'price' => 'required:zeroable'
emailDebe ser una dirección de correo válida'email' => 'email'
numericDebe ser un valor numérico'age' => 'numeric'
stringDebe ser una cadena de texto'name' => 'string'
lengthDebe tener exactamente la longitud especificada'code' => 'length:8'
minLongitud mínima de la cadena'password' => 'min:8'
maxLongitud máxima de la cadena'description' => 'max:255'
fiscalValida 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 como required deben estar presentes.
  • Para el método update(), los campos marcados como required solo 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:

  1. Configuración de validaciones a nivel de controlador para store() y update()
  2. Validación personalizada para métodos específicos
  3. Cómo usar diferentes reglas para diferentes tipos de datos
  4. 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.