Валидиране на формуляри
Задължителни елементи, които трябва да се попълнят
Контролите се маркират като задължителни с помощта на метода
setRequired()
, чийто аргумент е текстът на съобщението за грешка, което ще се покаже, ако
потребителят не успее да го изпълни. Ако не е посочен аргумент, се
използва съобщението за грешка по подразбиране.
$form->addText('name', 'Имя:')
->setRequired('Пожалуйста, заполните ваше имя.');
Правила
Добавяме правила за валидиране към контролите, като използваме
метода addRule()
. Първият параметър е правилото, вторият е съобщението за грешка, а третият е аргументът на
правилото за валидиране.
$form->addPassword('password', 'Пароль:')
->addRule($form::MinLength, 'Пароль должен состоять не менее чем из %d символов', 8);
Правилата за валидиране се проверяват само ако потребителят е попълнил елемента.
Nette се предлага с редица предварително дефинирани правила, чиито
имена са константи от класа Nette\Forms\Form
. Можем да прилагаме тези
правила към всички елементи:
константа | описание | аргументи |
---|---|---|
Required |
псевдоним setRequired() |
– |
Filled |
псевдоним setRequired() |
– |
Blank |
не трябва да се попълва | – |
Equal |
стойността е равна на параметъра | mixed |
NotEqual |
стойността не е равна на | mixed |
IsIn |
стойността е равна на някой елемент от масива | array |
IsNotIn |
стойността не е равна на нито един елемент от масива | array |
Valid |
записът преминава валидиране (за Условие) | – |
Въвеждане на текст
За елементите addText()
, addPassword()
, addTextArea()
,
addEmail()
, addInteger()
, addFloat()
могат да се приложат и някои
от следните правила:
MinLength |
минимална дължина на реда int |
|
MaxLength |
максимална дължина на реда int |
|
Length |
дължината в диапазон или точната дължина | двойка [int, int] или int |
Email |
валиден имейл адрес | – |
URL |
валиден URL адрес | – |
Pattern |
съвпада с регулярен модел | string |
PatternInsensitive |
като Pattern , но без да се отчитат малките и големите букви. |
string |
Integer |
цяло число | – |
Numeric |
псевдоним Integer |
– |
Float |
цяло число или число с плаваща запетая | – |
Min |
минимална стойност в цяло число | int|float |
Max |
максимална стойност в цяло число | int|float |
Range |
стойност в диапазона | para [int|float, int|float] |
Правилата Integer
, Numeric
и Float
автоматично
преобразуват стойността в цяло число (или съответно в число с плаваща
запетая). Освен това правилото URL
приема и адрес без схема (напр.
nette.org
) и допълва схемата (https://nette.org
). Изразите в
Pattern
и PatternInsensitive
трябва да са валидни за цялата стойност,
т.е. все едно са обвити в ^
и $
.
Брой на елементите
За елементите addMultiUpload()
, addCheckboxList()
, addMultiSelect()
можете да използвате и следните правила, за да ограничите броя на
избраните елементи или качените файлове:
MinLength |
минимален брой | int |
MaxLength |
максимален брой | int |
Length |
брой в диапазон или точен брой | двойки [int, int] или int |
Качване на файлове
Следните правила могат да се използват и за контроли addUpload()
,
addMultiUpload()
:
MaxFileSize |
максимален размер на файла в байтове | int |
MimeType |
Тип MIME, приема заместващи символи ('video/*' ) |
string|string[] |
Image |
изтегленият файл е JPEG, PNG, GIF, WebP | – |
Pattern |
името на файла отговаря на регулярен израз | string |
PatternInsensitive |
като Pattern , но без да се отчитат малките и големите букви. |
string |
За MimeType
и Image
се изисква разширението на PHP fileinfo
.
Правилният тип на файла или изображението се определя от неговия
подпис. Целостта на целия файл не се проверява. Можете да проверите
дали дадено изображение е повредено, например като се опитате да го заредите.
Съобщения за грешки
Всички предварително дефинирани правила, с изключение на Pattern
и PatternInsensitive
, имат съобщение за грешка по подразбиране, така че
можете да ги пропуснете. Въпреки това, като предавате и формулирате
всички отделни съобщения, ще направите формуляра по-удобен за
ползване.
Можете да промените съобщенията по подразбиране в configuration, като промените текстовете в
масива Nette\Forms\Validator::$messages
или като използвате преводач.
В текста на съобщенията за грешка могат да се използват следните заместващи символи:
%d |
поетапно замества правилата след аргументите |
%n$d |
замества аргумента на n-тото правило |
%label |
заменя с етикет на полето (без двоеточие) |
%name |
замества името на полето (например name ) |
%value |
заменя със стойност, въведена от потребителя |
$form->addText('name', 'Имя:')
->setRequired('Пожалуйста, заполните %label');
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'не менее %d и не более %d', [5, 10]);
$form->addInteger('id', 'ID:')
->addRule($form::Range, 'не более %2$d и не менее %1$d', [5, 10]);
Условия
В допълнение към правилата за валидиране могат да се задават и
условия. Те се задават по същия начин като правилата, но вместо
addCondition()
използваме addRule()
и, разбира се, ги оставяме без
съобщение за грешка (условието просто пита):
$form->addPassword('password', 'Password:')
// ако паролата не е по-дълга от 8 символа ...
->addCondition($form::MaxLength, 8)
// ... тогава трябва да съдържа число
->addRule($form::Pattern, 'Must contain a number', '.*[0-9].*';
Условието може да бъде обвързано с елемент, различен от текущия, с
помощта на addConditionOn()
. Първият параметър е връзка към полето. В
следния случай имейлът ще се изисква само ако полето е маркирано (т.е.
стойността му е true
):
$form->addCheckbox('newsletters', 'send me newsletters');
$form->addEmail('email', 'Email:')
// ако квадратчето е маркирано ...
->addConditionOn($form['newsletters'], $form::Equal, true)
// ... изискване на имейл
->setRequired('Въведете вашия имейл адрес');
Условията могат да бъдат групирани в сложни структури с помощта на
методите elseCondition()
и endCondition()
.
$form->addText(/* ... */)
->addCondition(/* ... */) // ако първото условие е изпълнено
->addConditionOn(/* ... */) // и второто условие и за другия елемент
->addRule(/* ... */) // изисква това правило
->elseCondition() // ако второто условие не е изпълнено
->addRule(/* ... */) // изисква спазването на тези правила
->addRule(/* ... */)
->endCondition() // връщаме се към първото условие
->addRule(/* ... */);
В Nette е много лесно да се реагира на изпълнението или неизпълнението
на дадено условие от страна на JavaScript, като се използва методът
toggle()
, вж. раздел Динамичен JavaScript.
Препратка към друг елемент
Като аргумент за правило или условие можете да предадете и друг
елемент от формуляра. Тогава правилото ще използва стойността,
въведена по-късно от потребителя в браузъра. Това може да се използва
например за динамично потвърждаване, че елементът password
съдържа
същия низ като елемента password_confirm
:
$form->addPassword('password', 'Password');
$form->addPassword('password_confirm', 'Confirm Password')
->addRule($form::Equal, 'The passwords do not match', $form['password']);
Потребителски правила и условия
Понякога се сблъскваме със ситуация, в която вградените правила за валидиране в Nette не са достатъчни и трябва да валидираме данните от потребителя по наш собствен начин. В Nette това е много лесно!
Можете да предадете всяко обратно извикване като първи параметър на
методите addRule()
или addCondition()
. Обратното извикване приема
самия елемент като първи параметър и връща булева стойност, указваща,
че проверката е била успешна. Когато добавяте правило с addRule()
,
можете да подадете допълнителни аргументи, които се предават като
втори параметър.
По този начин може да се създаде персонализиран набор от валидатори като клас със статични методи:
class MyValidators
{
// проверява дали стойността е делима на аргумента
public static function validateDivisibility(BaseControl $input, $arg): bool
{
return $input->getValue() % $arg === 0;
}
public static function validateEmailDomain(BaseControl $input, $domain)
{
// допълнителни валидатори
}
}
Тогава използването е много просто:
$form->addInteger('num')
->addRule(
[MyValidators::class, 'validateDivisibility'],
'Значение должно быть кратно %d',
8,
);
В JavaScript могат да се добавят и потребителски правила за валидиране.
Единственото изискване е правилото да е статичен метод. Името му за
валидатора на JavaScript се създава чрез конкатенация на името на класа без
обратни наклонени черти \
, подчеркивания _
и името на
метода. Например запишете App\MyValidators::validateDivisibility
като
AppMyValidators_validateDivisibility
и го добавете към обекта Nette.validators
:
Nette.validators['AppMyValidators_validateDivisibility'] = (elem, args, val) => {
return val % args === 0;
};
Събитие OnValidate
След като формулярът бъде изпратен, валидирането се извършва чрез
проверка на отделните правила, добавени с addRule()
, и след това чрез
извикване на събитието onValidate
.
Неговият манипулатор може да се използва за допълнително валидиране,
обикновено за проверка дали комбинация от стойности в няколко
елемента на формуляра е правилна.
Ако бъде открита грешка, тя се предава на формуляра чрез метода
addError()
. Това може да бъде извикано за конкретен елемент или
директно за формата.
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('Эта комбинация невозможна.');
}
}
Грешки при обработката
В много случаи откриваме грешка при обработката на самия формуляр,
например когато записваме нов запис в базата данни и срещаме дублиран
ключ. В този случай предаваме грешката обратно към формуляра, като
използваме метода addError()
. Това може да бъде извикано за
конкретен елемент или директно за формата:
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('Неверный пароль.');
}
}
Ако е възможно, препоръчваме да добавите грешката директно към елемента на формата, тъй като при използване на изобразяването по подразбиране тя ще се показва до него.
$form['date']->addError('Извините, эта дата уже занята.');
Можете да извикате addError()
няколко пъти, за да предадете няколко
съобщения за грешки на дадена форма или елемент. Извличате ги с
функцията getErrors()
.
Обърнете внимание, че $form->getErrors()
връща обобщение на всички
съобщения за грешки, дори и тези, които са били предадени директно на
отделни елементи, а не само директно на формата. Съобщенията за грешки,
предадени само на формата, се извличат чрез $form->getOwnErrors()
.
Промяна на входните стойности
С помощта на метода addFilter()
, можем да променим стойността,
въведена от потребителя. В този пример ще разрешим и премахнем
интервалите в пощенския код:
$form->addText('zip', 'postcode:')
->addFilter(function ($value) {
return str_replace(' ', '', $value); // премахване на интервалите от индекса zip
})
->addRule($form::Pattern, 'Postcode is not five digits', '\d{5}';
Филтърът е включен между правилата за валидиране и условията и
следователно зависи от реда на методите, т.е. филтърът и правилото се
извикват в същия ред като реда на методите addFilter()
и
addRule()
.
Удостоверяване на JavaScript
Езикът на правилата и условията за валидиране е мощен. Въпреки че
всички конструкции работят както от страна на сървъра, така и от страна
на клиента, в JavaScript. Правилата се предават в атрибути на HTML
data-nette-rules
като JSON. Самото валидиране се извършва от друг скрипт,
който прихваща всички събития на формуляра submit
, преминава през
всички входове и изпълнява съответните валидирания.
Този скрипт е netteForms.js
, който е достъпен от няколко възможни
източника:
Можете да вградите скрипта директно в HTML страница от CDN:
<script src="https://unpkg.com/nette-forms@3"></script>
Или копирайте локално в обща папка на проекта (например от
vendor/nette/forms/src/assets/netteForms.min.js
):
<script src="/path/to/netteForms.min.js"></script>
Или инсталирайте чрез npm:
npm install nette-forms
След това го изтеглете и стартирайте:
import netteForms from 'nette-forms';
netteForms.initOnLoad();
Освен това можете да го изтеглите директно от папката vendor
:
import netteForms from '../path/to/vendor/nette/forms/src/assets/netteForms.js';
netteForms.initOnLoad();
Динамичен JavaScript
Искате ли да показвате полета с адреси само ако потребителят реши да
изпрати елемента по пощата? Няма проблем. Ключът е двойка методи
addCondition()
и toggle()
:
$form->addCheckbox('send_it')
->addCondition($form::Equal, true)
->toggle('#address-container');
Този код казва, че ако условието е изпълнено, т.е. ако квадратчето за
отметка е маркирано, HTML елементът #address-container
ще стане видим. И
обратното. Така че поставяме елементите на формата с адреса на
получателя в контейнер с този идентификатор и когато щракнем върху
квадратчето за отметка, те се скриват или показват. С това се занимава
скриптът netteForms.js
.
Всеки селектор може да бъде предаден като аргумент на метода
toggle()
. По исторически причини буквено-цифровият низ без други
специални знаци се третира като идентификатор на елемента, точно както
ако беше предшестван от #
. Второй необязательный параметр
позволяет нам изменить поведение, т. е. если бы мы использовали
toggle('#address-container', false)
, елементът ще се показва само когато
квадратчето за отметка е премахнато.
Изпълнението по подразбиране на JavaScript променя свойството hidden
за елементите. Въпреки това можем лесно да променим поведението,
например чрез добавяне на анимация. Просто заместете метода
Nette.toggle
в JavaScript със свое собствено решение:
Nette.toggle = (selector, visible, srcElement, event) => {
document.querySelectorAll(selector).forEach((el) => {
// скрыть или показать 'el' в зависимости от значения 'visible'
});
};
Деактивиране на валидирането
В някои случаи е необходимо да се деактивира валидирането. Ако
бутонът за изпращане не трябва да се валидира след изпращане (като
например бутон Cancel или Preview), можете да деактивирате
валидирането, като извикате $submit->setValidationScope([])
. Можете също
така да валидирате формуляра частично, като посочите елементи за
валидиране.
$form->addText('name')
->setRequired();
$details = $form->addContainer('details');
$details->addInteger('age')
->setRequired('age');
$details->addInteger('age2')
->setRequired('age2');
$form->addSubmit('send1'); // проверява цялата форма
$form->addSubmit('send2')
->setValidationScope([]); // нищо не се валидира
$form->addSubmit('send3')
->setValidationScope([$form['name']]); // Валидира само полето 'name'
$form->addSubmit('send4')
->setValidationScope([$form['details']['age']]; // Проверява се само полето 'age'
$form->addSubmit('send5')
->setValidationScope([$form['details']]); // Проверява контейнер 'details'
Събитието onValidate на формата се извиква винаги и
не зависи от setValidationScope
. Събитието onValidate
on container се
извиква само когато този контейнер е зададен за частично валидиране.