Написание custom валидатора для Zend_Form > Snowcore’s блог

Написание custom валидатора для Zend_Form

Очень удобная вещь - Zend_Form. Данный компонент предоставляет множество возможностей, таких как фильтрация и валидация данных формы. Zend_Form имеет ряд собственных валидаторов и фильтров, но он достаточно гибок, и есть возможность создавать свои классы для валидации.

Рассмотрим на примере создание собственного валидатора для формы регистрации.

Возьмем простейшую форму для регистрации, с минимальным набором полей:

  • логин
  • email
  • пароль
  • пароль повторно

Требования к полям:

  1. Логин - обязательное поле, строка из цифро-буквенных символов длиною от 3 до 15 символов, уникальное.
  2. email - обязательное поле, имеет правильный формат, уникальное.
  3. Пароль - обязательное поле, строка из любых символов, не короче 6 символов
  4. Пароль повторно - поле должно совпадать с полем ‘пароль’.

Исходя их требований, можно сделать вывод, что нам нужно написать 3 собственных валидатора:

  1. Проверка уникальности логина.
  2. Проверка уникальности email.
  3. Проверка совпадения паролей.

Для создания custom validator, нужно наследовать класс от абстрактного Zend_Validate_Abstract, и реализовать в нем метод isValid.

В своих проектах валидаторы я храню в директории models/validator.

Создание валидатора уникальности логина

Создаем новый файл models/validator/UniqueNickname.php

<?php
class Validator_UniqueNickname extends Zend_Validate_Abstract
{
    const NOT_UNIQUE = ‘notUnique’;
    
    /*
     * Сообщения об ошибках валидации
     */
    protected $_messageTemplates = array(
        self::NOT_UNIQUE => “Username ‘%value%’ has already been taken”
    );
    
    public function isValid($value)
    {
        $this->_setValue($value);
        $isValid = true;
        
        /*
         * Метод User::checkLogin проверяет используется ли в базе такой логин,
         * если да, то возвращает user id
         */  
        $userId = User::checkLogin($value);
        if ($userId) {
            $this->_error(self::NOT_UNIQUE);
            $isValid = false;
        }
        return $isValid;
    }
}

Создание валидатора уникальности email

Создаем новый файл models/validator/UniqueEmail.php

<?php
class Validator_UniqueEmail extends Zend_Validate_Abstract
{
    const NOT_UNIQUE = ‘notUnique’;
    
    protected $_messageTemplates = array(
        self::NOT_UNIQUE => “Email ‘%value%’ has already been taken”
    );
    
    public function isValid($value)
    {
        $this->_setValue($value);
        $isValid = true;
        $userId = User::checkEmail($value);
        if ($userId) {
            $this->_error(self::NOT_UNIQUE);
            $isValid = false;
        }
        return $isValid;
    }
}

Создание валидатора для паролей

С этим валидатором дела обстоят интереснее. Создаем новый файл models/validator/EqualValues.php.

<?php
class Validator_EqualValues extends Zend_Validate_Abstract
{
    const NOT_EQUAL = ‘notEqual’;
    
    protected $_messageTemplates = array(
        self::NOT_EQUAL => ‘Passwords are not equal’
    );
    
    protected $_contextKey;
    
    public function __construct($key)
    {
        $this->_contextKey = $key;
    }
    
    public function isValid($value, $context = null)
    {
        if (is_array($context)) {
            if (isset($context[$this->_contextKey]) && ($value === $context[$this->_contextKey])) {
                return true;
            }
        }
        if ($value === $context) {
            return true;
        }
        $this->_error(self::NOT_EQUAL);
        return false;
    }
}

Форма регистрации

Теперь создаем непосредственно саму форму регистрации (models/form/Register.php)

<?php
class Form_Register extends Zend_Form
{
    public function __construct($options = null)
    {
        parent::__construct($options);
        
        $this->setAction(HOME_URL . ‘register/’)
             ->setMethod(‘post’);
        
        $login = new Zend_Form_Element_Text(‘login’);
        $login->setLabel(‘Логин’)
              ->setRequired(true)
              ->addFilter(‘StripTags’)
              ->addFilter(‘StringTrim’)
              ->addFilter(‘StringToLower’)
              ->addValidator(‘StringLength’, true, array(‘min’ => 3, ‘max’ => 15))
              ->addValidator(‘alnum’)
              ->addValidator(new Validator_UniqueNickname())
              ;
              
        $email = new Zend_Form_Element_Text(‘email’);
        $email->setLabel(‘email’)
              ->setRequired(true)
              ->addFilter(‘StripTags’)
              ->addFilter(‘StringTrim’)
              ->addFilter(‘StringToLower’)
              ->addValidator(new Zend_Validate_EmailAddress())
              ->addValidator(new Validator_UniqueEmail())
              ;
        
        $password = new Zend_Form_Element_Password(‘password’);
        $password->setLabel(‘Пароль’)
                 ->setRequired(true)
                 ->addValidator(‘StringLength’, true, array(‘min’ => 6))
                 ;
        
        $passwordAgain = new Zend_Form_Element_Password(‘passwordAgain’);
        $passwordAgain->setLabel(‘Повторите пароль’);
        
        // Валидатор для совпадения паролей 
        $passwordAgain->addValidator(new Validator_EqualValues(‘password’))
                      ->setAllowEmpty(false);
        
        $submit = new Zend_Form_Element_Submit(’submit’);
        $submit->setLabel(‘Ok’);
        
        $this->addElements(array($login, $email, $password, $passwordAgain, $submit));
    }
}

При добавлении валидатора для паролей нужно вызывать метод setAllowEmpty со значением false. Это делается для того чтобы валидатор паролей срабатывал при пустом значении “Пароль повторно”.

Сам процесс регистрации не входит в рамки данной статьи, возможно позже напишу продолжение данной статьи.

10 Responses

  1. lcf Says:

    А зачем у вас проверка
    if (is_string($context) && ($value === $context)) {
    ? ^_^

    Что будет если не строка?

  2. admin Says:

    :)
    Эта часть была скопирована с примера, который приводил san.
    Но, во всяком случае $context будет строкой, так что проверку is_string можно убрать.
    Спасибо за замечание.

  3. Александр Махомет Says:

    Вместо того что бы писать для каждого поля свой валидатор на уникальность, я использую универсальный http://zendframework.ru/articles/zend-validate-examples#nodbexists

  4. admin Says:

    Согласен, а то получилось дублирование кода.
    Спасибо за совет.

  5. CharnaD Says:

    Зендом увлекся? А я вот на симфони)) похоливарим?

  6. admin Says:

    Не то слово увлекся!
    С утра до вечера - один Zend Framework :)
    Холиварить нет смысла, т.к. symfony я тоже люблю!

  7. terix Says:

    Очень интересно, почему валидатор лежит именно в models/validator/ а не где - то ещё. Это продиктовано вкусовыми пристрастиями или суровой необходимостью?
    Интересно также почему форма лежит в models/form. В зендовском туториале формы кладут в application/form вроде.

  8. [YS.PRO] Says:

    Вот черт, формы через php объекты действительно рулят, надо и мне переучить себя ). Ко всему вышенаписанному хочу добавить, что в самом процессе регистрации я бы не надеялся на метод User::checkLogin() или User::checkEmail(), а ловил бы Exception, который генерится при попытке вставки в таблицу дублированных данных (поля с UNIQUE INDEX)

  9. admin Says:

    @terix:
    В принципе, разработчик сам в праве выбирать место для своих классов - мне так удобнее.

  10. admin Says:

    @[YS.PRO]:
    ну на счет Exception: по-моему это слишком… По крайней мере такого еще нигде не встречал.

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

I'm not spammer :)

Внимание: Комментарии проходят ручную модерацию