Стандартные валидаторы Yii2 покрывают большинство потребностей: обязательность, числовой формат, email, уникальность и так далее. Но иногда бизнес-логика требует проверок, которых нет из коробки. Например, убедиться, что пароль содержит хотя бы одну заглавную букву и цифру, или что дата начала не позже даты окончания. В таких случаях на помощь приходят кастомные валидаторы. В этой статье я расскажу, как их создавать, поделюсь примерами и лайфхаками.

Digital-студия WNDER

Зачем нужны кастомные валидаторы?

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