Валідація форм
Обов'язкові для заповнення елементи
Елементи керування позначаються як обов'язкові за допомогою методу
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 |
значення в діапазоні | пара [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
або використовуючи translator.
У тексті повідомлень про помилки можна використовувати такі символи підстановки:
%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]);
Умови
Крім правил валідації, можна задати умови. Вони встановлюються так
само, як і правила, але ми використовуємо addRule()
замість
addCondition()
і, звісно, залишаємо їх без повідомлення про помилку
(умова просто запитує):
$form->addPassword('password', 'Пароль:')
// якщо пароль не довший за 8 символів ...
->addCondition($form::MaxLength, 8)
// ... тоді він має містити число
->addRule($form::Pattern, 'Повинен містити номер', '.*[0-9].*');
Умова може бути прив'язана до елемента, відмінного від поточного, за
допомогою addConditionOn()
. Перший параметр – це посилання на поле. У
наступному випадку електронна пошта знадобиться тільки в тому
випадку, якщо прапорець установлено (тобто його значення дорівнює
true
):
$form->addCheckbox('newsletters', 'надсилати мені інформаційні бюлетені');
$form->addEmail('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', 'Поштовий індекс:')
->addFilter(function ($value) {
return str_replace(' ', '', $value); // видалити пробіли з поштового індексу
})
->addRule($form::Pattern, 'Поштовий індекс не складається з п'яти цифр', '\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
стане видимим. І навпаки. Отже, ми
поміщаємо елементи форми з адресою одержувача в контейнер із цим ID, і
під час натискання на прапорець вони приховуються або показуються. Цим
займається скрипт 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'
});
};
Вимкнення валідації
У деяких випадках необхідно відключити валідацію. Якщо кнопка submit не
повинна виконувати перевірку після відправлення (наприклад, кнопка
Скасування або Предварительный просмотр), ви можете
відключити перевірку, викликавши $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']]); // Перевіряє тільки поле 'ім'я'
$form->addSubmit('send4')
->setValidationScope([$form['details']['age']]); // Перевіряється тільки поле 'вік'
$form->addSubmit('send5')
->setValidationScope([$form['details']]); // Перевіряє контейнер 'details'
Подія onValidate на формі викликається завжди і не
залежить від setValidationScope
. Подія onValidate
на контейнері
викликається тільки тоді, коли цей контейнер вказано для часткової
валідації.