Validacija obrazcev
Obvezni elementi
Obvezne elemente označimo z metodo setRequired()
, katere argument je besedilo sporočila o napakah, ki se prikaže, če uporabnik elementa ne izpolni. Če argumenta ne
navedemo, se uporabi privzeto sporočilo o napaki.
$form->addText('name', 'Ime:')
->setRequired('Prosimo, vnesite ime');
Pravila
Validacijska pravila dodajamo elementom z metodo addRule()
. Prvi parameter je pravilo, drugi je besedilo sporočila o napakah in tretji je argument validacijskega pravila.
$form->addPassword('password', 'Geslo:')
->addRule($form::MinLength, 'Geslo mora imeti vsaj %d znakov', 8);
Validacijska pravila se preverjajo samo v primeru, da je uporabnik element izpolnil.
Nette prihaja s celo vrsto preddefiniranih pravil, katerih imena so konstante razreda Nette\Forms\Form
. Pri vseh
elementih lahko uporabimo ta pravila:
konstanta | opis | tip argumenta |
---|---|---|
Required |
obvezen element, alias za setRequired() |
– |
Filled |
obvezen element, alias za setRequired() |
– |
Blank |
element ne sme biti izpolnjen | – |
Equal |
vrednost je enaka parametru | mixed |
NotEqual |
vrednost ni enaka parametru | mixed |
IsIn |
vrednost je enaka nekateremu elementu v polju | array |
IsNotIn |
vrednost ni enaka nobenemu elementu v polju | array |
Valid |
je element pravilno izpolnjen? (za pogoje) | – |
Besedilni vnosi
Pri elementih addText()
, addPassword()
, addTextArea()
, addEmail()
,
addInteger()
, addFloat()
lahko uporabimo tudi nekatera naslednja pravila:
MinLength |
minimalna dolžina besedila | int |
MaxLength |
maksimalna dolžina besedila | int |
Length |
dolžina v obsegu ali natančna dolžina | par [int, int] ali int |
Email |
veljaven e-poštni naslov | – |
URL |
absolutni URL | – |
Pattern |
ustreza regularnemu izrazu | string |
PatternInsensitive |
kot Pattern , vendar neodvisno od velikosti črk |
string |
Integer |
celoštevilska vrednost | – |
Numeric |
alias za Integer |
– |
Float |
število | – |
Min |
minimalna vrednost numeričnega elementa | int|float |
Max |
maksimalna vrednost numeričnega elementa | int|float |
Range |
vrednost v obsegu | par [int|float, int|float] |
Validacijska pravila Integer
, Numeric
in Float
takoj pretvorijo vrednost v integer oz.
float. In nadalje pravilo URL
sprejme tudi naslov brez sheme (npr. nette.org
) in shemo dopolni
(https://nette.org
). Izraz v Pattern
in PatternIcase
mora veljati za celotno vrednost, tj.
kot da bi bil obdan z znakoma ^
in $
.
Število elementov
Pri elementih addMultiUpload()
, addCheckboxList()
, addMultiSelect()
lahko uporabimo tudi
naslednja pravila za omejitev števila izbranih elementov oz. naloženih datotek:
MinLength |
minimalno število | int |
MaxLength |
maksimalno število | int |
Length |
število v obsegu ali natančno število | par [int, int] ali int |
Nalaganje datotek
Pri elementih addUpload()
, addMultiUpload()
lahko uporabimo tudi naslednja pravila:
MaxFileSize |
maksimalna velikost datoteke v bajtih | int |
MimeType |
MIME tip, dovoljeni nadomestni znaki ('video/*' ) |
string|string[] |
Image |
slika JPEG, PNG, GIF, WebP, AVIF | – |
Pattern |
ime datoteke ustreza regularnemu izrazu | string |
PatternInsensitive |
kot Pattern , vendar neodvisno od velikosti črk |
string |
MimeType
in Image
zahtevata PHP razširitev fileinfo
. Da je datoteka ali slika
zahtevanega tipa, zaznajo na podlagi njene signature in ne preverjajo integritete celotne datoteke. Ali slika ni
poškodovana, lahko ugotovite na primer s poskusom njenega nalaganjem.
Sporočila o napakah
Vsa preddefinirana pravila z izjemo Pattern
in PatternInsensitive
imajo privzeto sporočilo
o napaki, zato ga lahko izpustite. Vendar z navedbo in oblikovanjem vseh sporočil po meri naredite obrazec uporabniku
prijaznejši.
Spremeniti privzeta sporočila lahko v konfiguraciji,
s prilagoditvijo besedil v polju Nette\Forms\Validator::$messages
ali z uporabo prevajalniku.
V besedilu sporočil o napakah lahko uporabljate te nadomestne nize:
%d |
postopoma nadomesti z argumenti pravila |
%n$d |
nadomesti z n-tim argumentom pravila |
%label |
nadomesti z oznako elementa (brez dvopičja) |
%name |
nadomesti z imenom elementa (npr. name ) |
%value |
nadomesti z vrednostjo, ki jo je vnesel uporabnik |
$form->addText('name', 'Ime:')
->setRequired('Izpolnite prosim %label');
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'najmanj %d in največ %d', [5, 10]);
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'največ %2$d in najmanj %1$d', [5, 10]);
Pogoji
Poleg pravil lahko dodajamo tudi pogoje. Ti se zapisujejo podobno kot pravila, le da namesto addRule()
uporabimo
metodo addCondition()
in seveda ne navajamo nobenega sporočila o napaki (pogoj se samo sprašuje):
$form->addPassword('password', 'Geslo:')
// če geslo ni daljše od 8 znakov
->addCondition($form::MaxLength, 8)
// potem mora vsebovati števko
->addRule($form::Pattern, 'Mora vsebovati števko', '.*[0-9].*');
Pogoj je mogoče vezati tudi na drug element kot trenutni s pomočjo addConditionOn()
. Kot prvi parameter
navedemo sklic na element. V tem primeru bo e-pošta obvezna le takrat, ko se označi potrditveno polje (njegova vrednost
bo true):
$form->addCheckbox('newsletters', 'pošiljajte mi novice');
$form->addEmail('email', 'E-pošta:')
// če je potrditveno polje označeno
->addConditionOn($form['newsletters'], $form::Equal, true)
// potem zahtevaj e-pošto
->setRequired('Vnesite e-poštni naslov');
Iz pogojev je mogoče ustvarjati kompleksne strukture s pomočjo elseCondition()
in
endCondition()
:
$form->addText(/* ... */)
->addCondition(/* ... */) // če je izpolnjen prvi pogoj
->addConditionOn(/* ... */) // in drugi pogoj na drugem elementu
->addRule(/* ... */) // zahtevaj to pravilo
->elseCondition() // če drugi pogoj ni izpolnjen
->addRule(/* ... */) // zahtevaj ta pravila
->addRule(/* ... */)
->endCondition() // vračamo se k prvemu pogoju
->addRule(/* ... */);
V Nette je mogoče zelo enostavno reagirati na izpolnitev ali neizpolnitev pogoja tudi na strani JavaScripta s pomočjo
metode toggle()
, glej dynamický JavaScript.
Sklic na drug element
Kot argument pravila ali pogoja lahko predamo tudi drug element obrazca. Pravilo potem uporabi vrednost, ki jo je kasneje
vnesel uporabnik v brskalniku. Tako lahko npr. dinamično validiramo, da element password
vsebuje enak niz kot
element password_confirm
:
$form->addPassword('password', 'Geslo');
$form->addPassword('password_confirm', 'Potrdite geslo')
->addRule($form::Equal, 'Vneseni gesli se ne ujemata', $form['password']);
Pravila in pogoji po meri
Včasih se znajdemo v situaciji, ko nam vgrajena validacijska pravila v Nette ne zadostujejo in moramo podatke od uporabnika validirati po svoje. V Nette je to zelo enostavno!
Metodam addRule()
ali addCondition()
lahko kot prvi parameter predamo poljuben povratni klic. Ta
sprejme kot prvi parameter sam element in vrača boolean vrednost, ki določa, ali je validacija potekala v redu. Pri dodajanju
pravila s pomočjo addRule()
je mogoče vnesti tudi druge argumente, ti so nato predani kot drugi parameter.
Lasten nabor validatorjev tako lahko ustvarimo kot razred s statičnimi metodami:
class MyValidators
{
// testira, ali je vrednost deljiva z argumentom
public static function validateDivisibility(BaseControl $input, $arg): bool
{
return $input->getValue() % $arg === 0;
}
public static function validateEmailDomain(BaseControl $input, $domain)
{
// drugi validatorji
}
}
Uporaba je nato zelo enostavna:
$form->addInteger('num')
->addRule(
[MyValidators::class, 'validateDivisibility'],
'Vrednost mora biti večkratnik števila %d',
8,
);
Lastna validacijska pravila lahko dodajamo tudi v JavaScript. Pogoj je, da je pravilo statična metoda. Njeno ime za
JavaScript validator nastane s spojitvijo imena razreda brez povratnih poševnic \
, podčrtaja _
in
imena metode. Npr. App\MyValidators::validateDivisibility
zapišemo kot
AppMyValidators_validateDivisibility
in dodamo v objekt Nette.validators
:
Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
return val % args === 0;
};
Dogodek onValidate
Po pošiljanju obrazca se izvede validacija, kjer se preverijo posamezna pravila, dodana s pomočjo addRule()
, in
nato se sproži dogodek onValidate
. Njegov obravnavalnik
lahko uporabimo za dodatno validacijo, tipično preverjanje pravilne kombinacije vrednosti v več elementih obrazca.
Če se odkrije napaka, jo predamo v obrazec z metodo addError()
. To lahko pokličemo bodisi na konkretnem
elementu ali neposredno na obrazcu.
protected function createComponentSignInForm(): Form
{
$form = new Form;
// ...
$form->onValidate[] = [$this, 'validateSignInForm'];
return $form;
}
public function validateSignInForm(Form $form, \stdClass $data): void
{
if ($data->foo > 1 && $data->bar > 5) {
$form->addError('Ta kombinacija ni mogoča.');
}
}
Napake pri obdelavi
V mnogih primerih se o napaki zavemo šele v trenutku, ko obdelujemo veljaven obrazec, na primer zapisujemo novo postavko
v bazo podatkov in naletimo na podvojitev ključev. V takem primeru napako spet predamo v obrazec z metodo
addError()
. To lahko pokličemo bodisi na konkretnem elementu ali neposredno na obrazcu:
try {
$data = $form->getValues();
$this->user->login($data->username, $data->password);
$this->redirect('Home:');
} catch (Nette\Security\AuthenticationException $e) {
if ($e->getCode() === Nette\Security\Authenticator::InvalidCredential) {
$form->addError('Neveljavno geslo.');
}
}
Če je mogoče, priporočamo, da napako priključite neposredno elementu obrazca, ker se bo prikazala poleg njega pri uporabi privzetega rendererja.
$form['date']->addError('Oprostite, ampak ta datum je že zaseden.');
Lahko addError()
kličete večkrat in tako predaste obrazcu ali elementu več sporočil o napakah. Dobite jih
s pomočjo getErrors()
.
Pozor, $form->getErrors()
vrača povzetek vseh sporočil o napakah, tudi tistih, ki so bila predana neposredno
posameznim elementom, ne le neposredno obrazcu. Sporočila o napakah, predana samo obrazcu, dobite preko
$form->getOwnErrors()
.
Spreminjanje vnosa
S pomočjo metode addFilter()
lahko spremenimo vrednost, ki jo je vnesel uporabnik. V tem primeru bomo
tolerirali in odstranjevali presledke v poštni številki:
$form->addText('zip', 'Poštna št.:')
->addFilter(function ($value) {
return str_replace(' ', '', $value); // odstranimo presledke iz poštne številke
})
->addRule($form::Pattern, 'Poštna št. ni v obliki petih števk', '\d{5}');
Filter se vključi med validacijska pravila in pogoje, zato je vrstni red metod pomemben, tj. filter in pravilo se kličeta
v takem vrstnem redu, kot je vrstni red metod addFilter()
in addRule()
.
Validacija JavaScript
Jezik za oblikovanje pogojev in pravil je zelo močan. Vse konstrukcije pri tem delujejo tako na strani strežnika kot tudi na
strani JavaScripta. Prenašajo se v HTML atributih data-nette-rules
kot JSON. Samo validacijo nato izvaja skript, ki
prestreže dogodek obrazca submit
, pregleda posamezne elemente in izvede ustrezno validacijo.
Ta skript je netteForms.js
in je na voljo iz več možnih virov:
Skript lahko vstavite neposredno v HTML stran iz CDN:
<script src="https://unpkg.com/nette-forms@3"></script>
Ali ga kopirate lokalno v javno mapo projekta (npr. iz vendor/nette/forms/src/assets/netteForms.min.js
):
<script src="/path/to/netteForms.min.js"></script>
Ali namestite preko npm:
npm install nette-forms
In nato naložite in zaženete:
import netteForms from 'nette-forms';
netteForms.initOnLoad();
Alternativno ga lahko naložite neposredno iz mape vendor
:
import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();
Dinamični JavaScript
Želite prikazati polja za vnos naslova samo, če uporabnik izbere pošiljanje blaga po pošti? Ni problema. Ključ je par
metod addCondition()
& toggle()
:
$form->addCheckbox('send_it')
->addCondition($form::Equal, true)
->toggle('#address-container');
Ta koda pravi, da ko je pogoj izpolnjen, torej ko je potrditveno polje označeno, bo viden HTML element
#address-container
. In obratno. Elemente obrazca z naslovom prejemnika tako postavimo v vsebnik s tem ID-jem in ob
kliku na potrditveno polje se skrijejo ali prikažejo. To zagotavlja skript netteForms.js
.
Kot argument metode toggle()
je mogoče predati poljuben selektor. Iz zgodovinskih razlogov se alfanumerični niz
brez drugih posebnih znakov razume kot ID elementa, torej enako, kot če bi mu predhajal znak #
. Drugi neobvezni
parameter omogoča obrniti vedenje, tj. če bi uporabili toggle('#address-container', false)
, bi se element nasprotno
prikazal samo takrat, če potrditveno polje ne bi bilo označeno.
Privzeta implementacija v JavaScriptu spreminja elementom lastnost hidden
. Vedenje pa lahko enostavno spremenimo,
na primer dodamo animacijo. Dovolj je, da v JavaScriptu prepišemo metodo Nette.toggle
z lastno rešitvijo:
Nette.toggle = (selector, visible, srcElement, event) => {
document.querySelectorAll(selector).forEach((el) => {
// skrijemo ali prikažemo 'el' glede na vrednost 'visible'
});
};
Izklop validacije
Včasih se lahko zgodi, da je treba validacijo izklopiti. Če pritisk na gumb za pošiljanje ne sme izvajati validacije
(primerno za gumbe Prekliči ali Predogled), jo izklopimo z metodo $submit->setValidationScope([])
.
Če naj izvaja le delno validacijo, lahko določimo, katera polja ali vsebnikov obrazca se naj validirajo.
$form->addText('name')
->setRequired();
$details = $form->addContainer('details');
$details->addInteger('age')
->setRequired('age');
$details->addInteger('age2')
->setRequired('age2');
$form->addSubmit('send1'); // Validira celoten obrazec
$form->addSubmit('send2')
->setValidationScope([]); // Sploh ne validira
$form->addSubmit('send3')
->setValidationScope([$form['name']]); // Validira samo element name
$form->addSubmit('send4')
->setValidationScope([$form['details']['age']]); // Validira samo element age
$form->addSubmit('send5')
->setValidationScope([$form['details']]); // Validira vsebnik details
setValidationScope
ne vpliva na dogodek onValidate pri obrazcu, ki bo vedno
poklican. Dogodek onValidate
pri vsebniku bo sprožen samo, če je ta vsebnik označen za delno validacijo.