Проверка значения на уникальность — UniqueValidator в Yii2

16 декабря, 2016
Метки:

Вот уже почти год, как я работаю на Laravel (требование заказчика), но всем сердцем продолжаю любить Yii. С недавнего времени начала в свободное время контрибутить в сам фреимворк с мелкими баг-фиксами и улучшениями. И сегодня хочу рассказать про UniqueValidator, который проверяет уникальность значения. Документация на него немного запутанная, поэтому уверена что дополнительные разъяснения кому-нибудь да пригодятся.

Базовая запись валидатора выглядит так:

public function rules()
{
  return [
     ['login', 'unique'],
     ...
  ]
}

И тут все ясно — поле login должно быть уникальным (т.е в текущей таблице не должно быть уже такой записи). Если проверять нужно уникальность поля в другой таблице то можно задать в targetClass правильную модель, что тоже в принципе понятно. Аттрибут filter полезен если нужны какие-то дополнительные проверки, например что логин не должен совпадать с логином активного пользователя:

public function rules()
{
  return [
     ['login', 'unique', 'filter' => 'is_active = true'],
     ...
  ]
}

Фильтр может быть выражением (в запросе оно пойдет как AND) или анонимной функцией, в которой можно модефицировать $query

Как правильно указывать targetAttribute

При более сложных проверках наибольшие проблемы вызывает targetAttribute. Потому что:

     ['login', 'unique', 'targetAttribute' => 'name'],

проверит что текущее значение login у валидируемой модели не существует в таблице в поле name. А запись

     ['login', 'unique', 'targetAttribute' => ['name']],

проверит что текущее значение name у валидируемой модели не существует в таблице в поле name. Во втором случаи значение login в проверке учавствовать не будет совсем! Оно только получит сообщение об ошибке.

Баг или фича?

Фича 🙂 К примеру вам нужно проверить что у вас уникальная пара login AND name.

     ['login', 'unique', 'targetAttribute' => ['login', 'name']],

Каждый из targetAttribute будет сверяться со своим полем в модели, т.е login с login a name с name.
Если же нужно проверить значение login и для поля name то выглядить это будет так:

     ['login', 'unique', 'targetAttribute' => ['login', 'login' => 'name']],

НО тут важно понимать, что запись будет НЕ уникальной только тогда когда значение login будет в базе в поле login И name. Т.е если логин такой уже существует, а name у этой записи не совпадает с логином — то запись уникальна.
Если вам нужно проверить, что login не совпадает с существующим login ИЛИ существующем name то запись должна быть такой:

     ['login', 'unique'],
     ['login', 'unique', 'targetAttribute' => 'name'],

Запомните: все аттрибуты переданные в targetAttribute в виде массива в запросе к базе соединяются через AND.

Если остались вопросы — пишите в комментах. Так же можно задавать вопросы по любым другим валидаторам.
Напомню список все существующих стандартных валидаторов Yii2: boolean, captcha, compare, date, datetime, time, default, double, each, email, exist, file, filter, image, in, integer, match, number, required, safe, string, trim, unique, url и ip.


Метки:

Оставить комментарий

11 комментариев »

    Arman

    Какие правила должны быть в AR-модели? И должны ли они там быть?

      Developer

      В Yii валидация моделей происходит на уровне AR модели, какие правила нужны такие и пишите.
      Должны ли они там быть — риторический вопрос. С точки зрения архитектуры приложения не должны. С точки зрения RAPID development — это удобный и простой инструмент валидации данных.


    Evgenij

    При обновлении записи, например у нас изменился пароль, но логин остался прежним, валидатор не пропустит, так как в базе уже существует такой логин, какие есть варианты для обхода этой проблемы !?

      Developer

      UniqueValidator проверяет, что дубль записи это не таже запись, поэтому проблемы быть не должно.


    Иван

    ДД!
    Как будет выглядеть валидация, если мы хотим проверить на уникальность комбинацию 3-х или 4-х полей?

      Developer

      Все зависит от того, что вы понимаете под комбинацией. Если уникальным должно быть сочетание всех полей, то:

       ['login', 'unique', 'targetAttribute' => ['login', 'name', 'username']] 
      /*
      $a = ['login' => a, 'name' => 'my a name', 'username' => 'my a username'];
      $b = ['login' => a, 'name' => 'my a name', 'username' => 'my a username'];
      $c = ['login' => c, 'name' => 'my a name', 'username' => 'my a username'];
      a и b не уникальны, c уникально, т.к хотя бы одно поле уникально 
      */
      

      Если уникальным должно быть каждое поле в сравнении с самим собой:

       [['login', 'name', 'username'], 'unique'] 
      

      Если уникальным должно быть сочетание пары полей:

       ['login', 'unique', 'targetAttribute' => ['login', 'name']],
      ['login', 'unique', 'targetAttribute' => ['login', 'username']] 
      /*
      $a = ['login' => a, 'name' => 'my a name', 'username' => 'my a username'];
      $b = ['login' => a, 'name' => 'my a name', 'username' => 'my b username'];
      первое правило пройдет валидацию, второе нет
      */
      

    Иван

    я правильно понял, код
    [[‘doc_num’], ‘unique’, ‘targetAttribute’ => [‘doc_num’, ‘date’, ‘BIN_payer’]],
    провалидирует комбинацию из этих 3-х полей на уникальность?

      Developer

      Да, проверит наличие «doc_num AND date AND BIN_payer» и запишет ошибку в doc_num если она есть


    Матвей

    А можно ли сделать валидацию на уникальность без перезагрузки ?

      Developer

      Только ajax запросом, т.к для проверки на уникальность нужно сделать запрос в БД.


    Юрий

    Если обновлять запись, и не менять уникальные поля, то валидация не пропускает


Оставить комментарий:









Копирование материалов разрешено при наличии активной ссылки на источник