Контент


Шаблоны валидаторов

Думаю, всем известен стандартный механизм создания и использования объектов Validation в Kohana:

// подготавливаем данные - извлекаем только нужные поля
$data  = Arr::extract($this->request->post(), array('foo', 'bar'));
// создаем валидатор
$valid = Validation::factory($this->request->post());
// добавляем правила
$valid->rules('foo', array(...))->rules('bar', array(...));
// собственно валидируем
if ($valid->check()) {...}

Как часто бывает, что механизм проверки в разных частях сайта одинаков (или по большей части совпадает)? И тут в голову приходит мысль, что подготовительную часть можно спрятать в сам валидатор!

abstract class Validation_Template extends Validation {
 
    public static function factory(array $array)
    {
        return new static($array);
    }
 
    public function __construct(array $array)
    {
        if ( $fields = $this->_fields() ) 
        {
            $array = Arr::extract($array, $fields);
        }
 
        parent::__construct($array);
        // добавляем метки
        $this->labels($this->_labels());
        // добавляем правила
        foreach($this->_rules() as $field => $rules) 
        {
            $this->rules($field, $rules);
        }
    }
 
    /**
     * Позволяет фильтровать поля в массиве данных, оставляя только нужные
     * @return array
     */
    protected function _fields()
    {
        return array();
    }
 
    /**
     * Метод по аналогии с ORM-методом rules
     * @return array
     */
    protected function _rules()
    {
        return array();
    }
 
    /**
     * Задаем метки для проверяемых полей
     * @return array
     */
    protected function _labels()
    {
        return array();
    }
}
 
class Validation_Foo extends Validation_Template {
    protected function _fields()
    {
        return array('foo', 'bar');
    }
 
    protected function _rules()
    {
        return array(
            'foo' => array(
                array('not_empty'),
                array('max_length', array(':value', 32)),
            ),
            'bar' => array(
                array('not_empty'),
            ),
        );
    }
 
    protected function _labels() 
    {
        return array(
            'foo'  => 'Foo field',
            'bar'  => 'Bar field',
        );
    }
}
 
// в контроллере
$valid = Validation_Foo::factory($this->request->post());
if ($valid->check()) {...}

Итого, мы имеем возможность разгрузить контроллеры от необязательной логики, вдобавок облегчив себе повторное использование кода. Обратите внимание, что метки и правила устанавливаются не напрямую в свойства $_rules и $_labels, а через вызовы методов. Это сделано для того, чтобы можно было создать иерархию валидаторов, наследуя от предков созданные ими метки и правила:

class Validation_Baz extends Validation_Foo {
    public function _fields()
    {
        // добавляем поле baz к исходному списку
        return array_merge(array('baz'), parent::_fields());
    }
}

Конечно, это не самая востребованная фича, но знать о такой возможности стоит.

Как вариант, можно не создавать Validation_Template, а расширить класс Validation. Но в любом случае, придется при создании шаблонных валидаторов указывать их класс (Validation_Foo::factory($data) или new Validation_Foo($data))

Google Bookmarks Digg Reddit del.icio.us Ma.gnolia Technorati Slashdot Yahoo My Web News2.ru БобрДобр.ru RUmarkz Ваау! Memori.ru rucity.com МоёМесто.ru Mister Wong

Опубликовано в Kohana3.

Теги: , .


Комментарии (9)

Будьте в курсе обсуждения, подпишитесь на RSS ленту комментариев к этой записи.

  1. Sergey пишет:

    Добрый день.

    Скажи пожалуйста, при использовании этих идей в Gleez, какую ссылку мы должны оставлять в исходниках ссылаясь на эту статью или тебя конкретно?

  2. Sergey пишет:

    Только наверное не
    if ( ! empty($this->_fields() )
    а
    if ($this->_fields())

  3. biakaveron пишет:

    Ничего не надо :) я пишу за идею

  4. Sergey пишет:

    Кстати очень странное поведение..
    Дело в том, что во время выполнения Validation_Template::__construct метод _fields() возвращает 0. И если посмотреть на это дело со стороны класса потомка Validation_Foo то этот метод всё равно возвращает 0. Хоть блин в каждом классе заново описывай всё. Пробовал использовать не метод, а поле. В классе Validation_Foo объявляю protected $_validation, в этом же классе в конструкторе наполняю поле нужными значениями и выполняю parent::__construct($array) передавая выполнение классу Validation_Template, в родительском классе естественно поле так же объявлено, в конструкторе, по аналогии с твоим примером выше распаковываю это дело в $array и всё равно, какой бы я способ не использовал $_validation или _fields() в конструкторе fields не наполняются нужными значениями. Магия? :)

    У тебя вообще это дело работало?

  5. biakaveron пишет:

    Работало.. в первых версиях )) В последнюю я внес кое-что, что лень было проверять поздно ночью. Подправил код Validation_Template.

    Обрати внимание на метод Validation_Template::factory() — он переопределяет дефолтную фабрику и использует static (php 5.3+). Другой вариант — дергать контроллер вместо фабрики (new Validation_Foo($data)).

  6. Sergey пишет:

    Да!

    return new static($array);

    сильно упростило жизнь. Спасибо!

  7. wd-wd пишет:

    Сюда бы еще добавить _filters()
    Вообще не понятно, вроде были до версии 3.1, зачем убрали непонятно.
    Или как это красиво сделать? Я использовал array_map на весь $_POST

  8. biakaveron пишет:

    Убрали с формулировкой «фильтрация данных не имеет ничего общего с валидацией». В принципе, я с этим согласен. Если говорить про ORM, то там фильтрация предполагается на уровне присвоения значения полю (метод set(), насколько я помню).

    В нашем случае вполне можно добавить фильтры в шаблоны валидации, поскольку это уже не столько базовый валидатор, сколько сервисная обертка вокруг него, тут уже доп. функционал уместен.

  9. slider23 пишет:

    debug-toolbar сейчас вышел в топ most forked today на гитхабе — https://github.com/languages/PHP :)



Можно включить подсветку кода: <code><pre lang="">...</pre></code>
Разрешены некоторые HTML теги

или используйте trackback.