Validare
Sistemul de validare din Leto verifică și curăță datele de intrare ($_POST, $_GET, orice array) pe baza unor reguli declarate ca string-uri, similar cu Laravel. Clasa principală este Leto\Data\Validation\Validator.
HasValidation, care validează invarianții unui model la salvare. Ambele aruncă ValidationFailedException, dar operează pe straturi diferite: Validator pe input brut, HasValidation pe un model deja construit.Utilizare de bază
use Leto\Data\Validation\Validator;
$data = Validator::validate($_POST, [
'name' => 'required|trim',
'email' => 'required|email',
'age' => 'default:18|int',
]);Metoda validate() returnează array-ul cu datele validate și modificate. Dacă validarea eșuează, aruncă ValidationFailedException.
default:... înaintea conversiei de tip, nu după. Cu int|default:18, când câmpul lipsește, int primește null (rezultă null), apoi default întoarce valoarea implicită ca string "18". Cu default:18|int obții corect int(18).Din GET și POST
// Date din query string
$filters = Validator::validateGet([
'p' => 'default:1|int',
'rpp' => 'default:50|int',
'search' => 'nullable|trim',
]);
// Date din formular
$data = Validator::validatePost([
'name' => 'required|trim',
'email' => 'required|email',
]);Reguli de existență
Controlează dacă un câmp trebuie validat sau poate fi omis:
| Regulă | Efect |
|---|---|
required | Câmpul trebuie să existe și să nu fie gol |
nullable | Câmpul poate lipsi sau fi null; în acest caz restul regulilor sunt sărite, iar cheia rămâne în rezultat cu valoarea null |
optional | Câmpul poate lipsi complet; dacă lipsește, cheia este omisă din rezultat |
Validator::validate($data, [
'name' => 'required',
'bio' => 'nullable|max:500',
'avatar' => 'optional|url',
]);nullable vs. optional. Ambele fac câmpul opțional, dar diferă prin ce ajunge în array-ul returnat:
- Folosește
nullablecând vrei ca cheia să existe în rezultat, cu valoareanull(util pentru câmpuri care se scriu caNULLîn baza de date). - Folosește
optionalcând vrei ca cheia să lipsească dacă utilizatorul nu a trimis-o (util pentru filtre construite dinamic).
Atenție: nullable sare peste restul regulilor doar dacă valoarea lipsește sau este null. Un string gol '' nu este sărit, deci nullable|min:3 pe '' tot eșuează. Dacă vrei să tratezi și string-ul gol ca absent, adaugă null_on_empty.
Reguli de modificare
Modifică valoarea înainte de validare. Se aplică în ordinea în care sunt declarate:
| Regulă | Efect |
|---|---|
trim | Elimină spațiile de la început și sfârșit |
int / integer | Conversie la int |
float | Conversie la float |
decimal | Conversie la Decimal\Decimal (precizie arbitrară, din extensia ext-decimal). Acceptă opțional precizia și scala: decimal:10,2 |
bool / boolean | Conversie la bool |
date | Conversie la DateTimeImmutable |
array | Conversie la array |
array_of:Tip | Array cu elemente convertite la un tip scalar: int, float sau bool (ex: array_of:int). Alt tip aruncă \ValueError |
null_on_empty | Transformă orice valoare goală (empty(): '', '0', 0, false, []) în null |
default:valoare | Înlocuiește valoarea cu implicita ori de câte ori e goală (empty()), nu doar când lipsește |
phone:RO | Parsează număr de telefon (cod țară opțional) |
url | Validează formatul URL (nu normalizează valoarea) |
ipv4 | Asigură format IPv4 |
ipv6 | Asigură format IPv6 |
nin:RO | Validează un CNP românesc; valoarea rămâne neschimbată. Necesită argumentul de țară |
$data = Validator::validate($_POST, [
'age' => 'default:0|int',
'price' => 'decimal:10,2',
'bio' => 'nullable|null_on_empty|trim',
'phone' => 'nullable|phone:RO',
'active' => 'bool',
]);default acționează pe valori goale, nu doar lipsă. Sub capotă folosește empty(), deci un 0, '0', '', false sau [] legitim trimis de utilizator este înlocuit cu valoarea implicită. Dacă 0 este o intrare validă (ex: o cantitate), nu te baza pe default ca să o păstrezi.
nin necesită țara. Scris fără argument (nin simplu) aruncă \ValueError și oprește request-ul, nu produce o eroare de validare. Folosește întotdeauna nin:RO.
Reguli de validare
Verifică valoarea după ce s-au aplicat modificatorii:
| Regulă | Efect |
|---|---|
email | Validează format email |
numeric | Verifică să fie numeric |
not_empty | Nu poate fi gol |
max:100 | Lungime maximă (string), valoare maximă (numeric) sau număr maxim de elemente (array) |
min:3 | Lungime minimă (string), valoare minimă (numeric) sau număr minim de elemente (array) |
size:10 | Lungime / valoare / număr de elemente exact |
enum:val1,val2,val3 | Valoarea trebuie să fie una din lista dată |
Validator::validate($data, [
'username' => 'required|trim|min:3|max:50',
'email' => 'required|email',
'score' => 'int|min:0|max:100',
'status' => 'required|enum:active,inactive,banned',
]);Gestionarea erorilor
use Leto\Data\Validation\Exceptions\ValidationFailedException;
try {
$data = Validator::validate($_POST, [
'email' => 'required|email',
'name' => 'required|min:3',
]);
} catch (ValidationFailedException $e) {
$errors = $e->getErrors(); // ['email' => ['...'], 'name' => ['...']]
}Validator::validate() aruncă \ValueError (nu ValidationFailedException) când încerci să validezi un câmp care nu există în datele de intrare și nu are o regulă de existență (required, nullable sau optional). Un bloc try/catch(ValidationFailedException) nu va prinde această eroare: este un crash, nu o eroare de validare. Asigură-te că toate câmpurile din schema de validare au o regulă de existență.Exemplu real: filtrare cu paginare
$data = Validator::validateGet([
'rpp' => 'default:50|int',
'p' => 'default:1|int',
]);
$jobs = JobModel::all()
->orderBy('id', Order::DESC)
->paginate($data['p'], $data['rpp'], $paginationInfo);Exemplu real: login cu stare din sesiune
$data = Validator::validate($_SESSION['auth'] ?? [], [
'redirectURL' => 'nullable|null_on_empty|url',
'activeProvider' => 'nullable|null_on_empty|int',
'identifiedUser' => 'nullable|null_on_empty|int',
]);