Стандартные валидаторы Yii2 покрывают большинство потребностей: обязательность, числовой формат, email, уникальность и так далее. Но иногда бизнес-логика требует проверок, которых нет из коробки. Например, убедиться, что пароль содержит хотя бы одну заглавную букву и цифру, или что дата начала не позже даты окончания. В таких случаях на помощь приходят кастомные валидаторы. В этой статье я расскажу, как их создавать, поделюсь примерами и лайфхаками.
Зачем нужны кастомные валидаторы?
Yii2 предоставляет богатый набор встроенных валидаторов, но они не могут предусмотреть все сценарии. Представьте, что вы разрабатываете форму регистрации, и вам нужно проверить, что возраст пользователя не меньше 18 лет. Или что введённый промокод существует в базе. Стандартные правила тут не помогут. Кастомные валидаторы позволяют инкапсулировать логику проверки в отдельный метод или класс, делая код чистым и переиспользуемым.
Кроме того, кастомные валидаторы упрощают поддержку: если правило изменится, вы поправите его в одном месте, а не будете искать по всему проекту.
Два способа создания кастомных валидаторов
В Yii2 есть два основных подхода: использовать inline-валидатор (метод в модели) или создать отдельный класс валидатора. Выбор зависит от сложности проверки и необходимости повторного использования.
Inline-валидатор
Самый простой способ — определить метод в модели и указать его в правилах. Имя метода должно начинаться с validate. Например, проверим, что поле birth_date соответствует возрасту не младше 18 лет.
public function rules()
{
return [
[['birth_date'], 'validateAge'],
];
}
public function validateAge($attribute, $params)
{
$date = strtotime($this->$attribute);
$minAge = strtotime('-18 years');
if ($date > $minAge) {
$this->addError($attribute, 'Вам должно быть не менее 18 лет.');
}
}
Всё просто: в правилах указываем имя метода, а в самом методе проверяем условие и добавляем ошибку через addError().
Отдельный класс валидатора
Если проверка сложная или будет использоваться в разных моделях, лучше вынести её в отдельный класс. Класс должен наследоваться от yii\validators\Validator и реализовывать метод validateAttribute() (или validateValue(), если нужно проверять значение вне модели).
Пример: валидатор, проверяющий, что строка содержит хотя бы одну заглавную букву и одну цифру.
namespace app\validators;
use yii\validators\Validator;
class StrongPasswordValidator extends Validator
{
public function validateAttribute($model, $attribute)
{
$value = $model->$attribute;
if (!preg_match('/[A-Z]/', $value) || !preg_match('/[0-9]/', $value)) {
$this->addError($model, $attribute, 'Пароль должен содержать хотя бы одну заглавную букву и одну цифру.');
}
}
}
Затем в модели указываем:
public function rules()
{
return [
[['password'], 'app\validators\StrongPasswordValidator'],
];
}
Такой валидатор можно переиспользовать в любой модели.
Параметры и настройка валидаторов
Кастомные валидаторы можно параметризовать. Например, задать минимальную длину или список допустимых значений. В inline-валидаторе параметры передаются через массив в правилах, а в классе — через публичные свойства.
Рассмотрим пример inline-валидатора с параметром min:
public function rules()
{
return [
[['age'], 'validateMinAge', 'min' => 18],
];
}
public function validateMinAge($attribute, $params)
{
if ($this->$attribute < $params['min']) {
$this->addError($attribute, "Возраст должен быть не менее {$params['min']} лет.");
}
}
Для класса валидатора параметры задаются как публичные свойства и указываются в правилах:
namespace app\validators;
use yii\validators\Validator;
class MinAgeValidator extends Validator
{
public $min = 18;
public function validateAttribute($model, $attribute)
{
if ($model->$attribute < $this->min) {
$this->addError($model, $attribute, "Возраст должен быть не менее {$this->min} лет.");
}
}
}
Использование:
[['age'], 'app\validators\MinAgeValidator', 'min' => 21]
В таблице ниже приведены основные параметры, которые можно переопределить в кастомном валидаторе.
| Параметр | Описание | По умолчанию |
|---|---|---|
| $message | Сообщение об ошибке | null |
| $skipOnEmpty | Пропускать валидацию, если значение пустое | true |
| $skipOnError | Пропускать, если уже есть ошибки | true |
| $when | Условие выполнения (callable) | null |
Эти параметры помогают тонко настроить поведение валидатора.
Практические советы и лайфхаки
Вот несколько рекомендаций, которые сэкономят вам время и нервы.
- Используйте отдельные классы для сложной логики. Если проверка включает запросы к базе или несколько условий, лучше вынести её в класс — так код будет чище.
- Не забывайте про перевод сообщений. Yii2 поддерживает интернационализацию. Вместо жёсткого текста используйте
Yii::t('app', '...'). - Тестируйте валидаторы. Напишите unit-тесты для своих валидаторов, чтобы убедиться, что они работают корректно при разных входных данных.
- Избегайте дублирования. Если одно и то же правило нужно в нескольких моделях, создайте класс — это избавит от копипаста.
addError(), а не через исключения. Yii2 ожидает, что валидатор просто добавит ошибку в модель, а не прервёт выполнение.
Ещё один лайфхак: если вам нужно проверить значение, не привязанное к модели (например, в форме без модели), используйте статический метод validateValue() в кастомном валидаторе. Это позволяет вызывать валидатор напрямую.
$validator = new app\validators\StrongPasswordValidator();
if (!$validator->validate('qwerty')) {
echo $validator->getErrorMessage();
}
Такой подход удобен для API или консольных скриптов.
Заключение
Кастомные валидаторы в Yii2 — мощный инструмент, который делает валидацию гибкой и соответствующей требованиям проекта. Вы можете создавать их двумя способами: через inline-методы или отдельные классы. Первый подходит для простых проверок, второй — для сложных и переиспользуемых. Не забывайте про параметризацию, тестирование и перевод сообщений. Надеюсь, эта статья поможет вам писать более надёжный код.
Студия WNDER
