Schema: validace dat
Praktická knihovna pro validaci a normalizaci datových struktur oproti danému schématu s chytrým srozumitelným API.
Instalace:
Základní použití
V proměnné $schema
máme validační schéma (co to přesně znamená a jak takové schéma vytvořit si
řekneme vzápětí) a v proměnné $data
datovou strukturu, kterou chceme validovat a normalizovat. Může jít
například o data odeslaná uživatelem skrze rozhraní API, konfigurační soubor, atd.
Úkol obstará třída Nette\Schema\Processor, která zpracuje vstup a buď vrátí normalizovaná data, nebo v případě chyby vyhodí výjimku Nette\Schema\ValidationException.
Metoda $e->getMessages()
vrací pole všech zpráv jakožto řetězců a
$e->getMessageObjects()
vrací všechny zprávy jako objekty Nette\Schema\Message.
Definování schématu
A nyní vytvoříme schéma. K jeho definování slouží třída Nette\Schema\Expect, definujeme vlastně očekávání,
jak mají data vypadat. Řekněme, že vstupní data musí tvořit strukturu (například pole) obsahující prvky
processRefund
typu bool a refundAmount
typu int.
Věříme, že definice schématu vypadá srozumitelně, i když ji vidíte úplně poprvé.
Pošleme k validaci následující data:
Výstupem, tedy hodnotou $normalized
, je objekt stdClass
. Pokud bychom chtěli, aby výstupem bylo
pole, doplníme schéma o přetypování Expect::structure([...])->castTo('array')
.
Všechny prvky struktury jsou volitelné a mají výchozí hodnotu null
. Příklad:
To, že výchozí hodnotou je null
, neznamená, že by se akceptovalo ve vstupních datech
'processRefund' => null
. Nikoliv, vstupem musí být boolean, tedy pouze true
nebo
false
. Povolit null
bychom museli explicitně pomoci Expect::bool()->nullable()
.
Položku lze učinit povinnou pomocí Expect::bool()->required()
. Výchozí hodnotu změníme třeba na
false
pomocí Expect::bool()->default(false)
nebo zkráceně Expect::bool(false)
.
A co kdybychom chtěli kromě booleanu akceptovat ještě 1
a 0
? Pak uvedeme výčet hodnot, které
navíc necháme normalizovat na boolean:
Nyní už znáte základy toho, jak se definuje schéma a jak se chovají jednotlivé prvky struktury. Nyní si ukážeme, jaké všechny další prvky lze při definici schématu použít.
Datové typy: type()
Ve schématu lze uvést všechny standardní datové typy PHP:
A dále všechny typy, podporované třídou
Validators, například Expect::type('scalar')
nebo zkráceně Expect::scalar()
. Také názvy tříd
či rozhraní, například Expect::type('AddressEntity')
.
Lze použít i union zápis:
Výchozí hodnota je vždy null
s výjimkou pro array
a list
, kde je to prázdné pole.
(List je pole indexované podle vzestupné řady numerických klíčů od nuly, tedy neasociativní pole).
Pole hodnot: arrayOf() listOf()
Pole představuje příliš obecnou strukturu, užitečnější je specifikovat, jaké přesně může obsahovat prvky. Například pole, jehož prvky mohou být pouze řetězce:
Druhým parametrem lze specifikovat klíče (od verze 1.2):
List je indexované pole:
Parametrem může být i schéma, můžeme tedy zapsat:
Výchozí hodnota je prázdné pole. Pokud zadáte výchozí hodnotu, bude sloučena s předanými daty. To lze deaktivovat
pomocí mergeDefaults(false)
(od verze 1.1).
Výčet: anyOf()
anyOf()
představuje výčet hodnot nebo schémat, které může hodnota nabývat. Takto zapíšeme pole prvků,
které mohou být buď 'a'
, true
nebo null
:
Prvky výčtu mohou být i schémata:
Metoda anyOf()
přijímá varianty jako jednotlivé parametry, nikoliv pole. Pokud jí chcete předat pole hodnot,
použijte unpacking operátor anyOf(...$variants)
.
Výchozí hodnota je null
. Metodou firstIsDefault()
učiníme první prvek výchozí:
Struktury
Struktury jsou objekty s definovanými klíči. Každá z dvojic klíč ⇒ hodnota je označována jako „vlastnost“:
Struktury přijímají pole a objekty a vrací objekty stdClass
.
Ve výchozím nastavení jsou všechny vlastnosti volitelné a mají výchozí hodnotu null
. Povinné vlastnosti
můžete definovat pomocí required()
:
Pokud nechcete mít na výstupu vlastnosti s výchozí hodnotou, použijte skipDefaults()
:
Ačkoliv je null
výchozí hodnota vlastnosti optional
, ve vstupních datech povolený není
(hodnotou musí být string). Vlastnosti akceptující null
definujeme pomocí nullable()
:
Pole všech vlastností struktury vrací metoda getShape()
.
Ve výchozím nastavení nemohou být ve vstupních datech žádné položky navíc:
Což můžeme změnit pomocí otherItems()
. Jako parametr uvedeme schéma, podle kterého se budou prvky navíc
validovat:
Novou strukturu můžete vytvořit odvozením od jiné pomocí extend()
:
Pole
Pole s definovanými klíči. Platí pro něj vše co struktury.
Lze definovat také indexované pole, známé jako tuple:
Zastaralé vlastnosti
Můžete označit vlastnost jako deprecated pomocí metody deprecated([string $message])
. Informace o ukončení
podpory jsou vrácena pomocí $processor->getWarnings()
:
Rozsahy: min() max()
Pomocí min()
a max()
lze u polí omezit počet prvků:
U řetězců omezit jejich délku:
U čísel omezit jejich hodnotu:
Samozřejmě je možné uvést pouze min()
, nebo pouze max()
:
Regulární výrazy: pattern()
Pomocí pattern()
lze uvést regulární výraz, kterému musí odpovídat celý vstupní řetězec (tj.
jako by byl obalen znaky ^
a $
):
Vlastní omezení: assert()
Libovolná další omezení zadáme pomocí assert(callable $fn)
.
Nebo
Ke každému omezení můžete přidat vlastní popis. Ten bude součástí chybové zprávy.
Metodu lze volat opakovaně a tak přidat více omezení. Lze ji prokládat s voláním transform()
a
castTo()
.
Transformace: transform()
Úspěšně zvalidovaná data lze upravovat pomocí vlastní funkce:
Metodu lze volat opakovaně a tak přidat více transformací. Lze ji prokládat s voláním assert()
a
castTo()
. Operace se provedou v pořadí, v jakém jsou deklarovány:
Metoda transform()
může současně transformovat i validovat hodnotu. To je často jednodušší a méně
duplicitní než řetězení transform()
a assert()
. K tomuto účelu funkce obdrží objekt Context s metodou addError()
, kterou lze
použít k přidání informací o problémech s validací:
Přetypování: castTo()
Úspěšně zvalidovaná data lze přetypovat:
Kromě nativních PHP typů lze přetypovávat i na třídy. Přitom se rozlišuje, zda jde o jednoduchou třídu bez kontruktoru, nebo třídu s konstruktorem. Pokud třída nemá konstruktor, vytvoří se její instance a všechny prvky struktury se zapíší do properties:
Pokud třída konstruktor má, prvky struktury se předají jako pojmenované parametry konstruktoru:
Přetypování v kombinaci se skalárním parametrem vytvoří objekt a hodnotu předá jako jediný parametr konstrukturu:
Normalizace: before()
Před samotnou validací lze data normalizovat pomocí metody before()
. Jako příklad uveďme prvek, který musí
být pole řetězců (například ['a', 'b', 'c']
), avšak přijímá vstup ve formě řetězce
a b c
:
Mapování na objekty: from()
Schéma struktury si můžeme nechat vygenerovat ze třídy. Příklad:
Podporovány jsou i anonymní třídy:
Protože informace získané z definice třídy nemusí být dostačující, můžete druhým parametrem doplnit prvkům vlastní schéma: