May 07

Появились у меня недавно некоторые наметки относительно проверки данных при передаче их в базу данных, да и вообще - Zend Db Table не помешало бы немного подлатать…

За модель в Zend Framework отвечает Zend_Db, а точнее - Zend_Db_Table. Т. е. мы создаем классы, которые соответствуют нашим таблицам в базе данных и работаем с ними. Т. к. ORM полноценной в ближайшем будущем в фреймворке не будет, то я попытаюсь за несколько статей сделать хотя бы некое подобие ORM’а.

Данные, которые мы добавляем/изменям в БД, в Zend_Db_Table поступают в следующем виде:

$data = array(
    'created_on'       => new Zend_Db_Expr('CURDATE()'),
    'bug_description'  => 'Something wrong',
    'bug_status'       => 'NEW'
);

т.е. в виде массива, элементы которого представляют собой пары “колонка - значение”.

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

/**
 * The table column names derived from Zend_Db_Adapter_Abstract::describeTable().
 *
 * @var array
 */
protected $_cols;

А если их там нет, то их всегда можно получить, вызвав метод _setupMetadata() класса Zend_Db_Table.

Для начала создадим класс ModelDb, который будет наследовать Zend_Db_Table. Это будет нашим полигоном к улучшению стандартного класса таблицы.

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

protected function _check_columns(array $data)
{
    if(empty($this->_cols)) {
        $this->_setupMetadata();
    }

    $ret = array();
    foreach($data as $key => $value)
    {
        if(isset($this->_cols[$key])) {
            $ret[$key] = $value;
        }
    }

    return $ret;
}

Сначала мы проверяем, есть ли у нас названия колонок и, если их нет, получаем их. Далее мы создаем массив “колонка - значение”, который уже содержит только те пары, ключи которых действительно являются колонками из таблицы и возвращаем их.

Также я сделаю на хотя бы какую-то проверку на переданные данные. Проверять эти данные яя буду с помощью regexp’ов (в будущем это все уберется, но об этом позже). Записывать эти regexp’ы я буду так:

protected $_validate = array(
    'название колонки' => 'regexp'
    'название колонки' => 'regexp'
    'название колонки' => 'regexp'
    ...
);

А теперь функция-метод для проверки данных:

protected function validate(array $data)
{
    if(isset($this->_validate)) {
        foreach($data as $key => $value)
        {
            if(@$this->_validate[$key] && !preg_match($this->_validate[$key], $value)) {
                require_once 'Zend/Db/Table/Exception.php';
                throw new Zend_Db_Table_Exception("Entered value \"$value\" is not valid");
            }
        }
    }
}

Что мы делаем - проверяем, есть ли у нас вообще массив регулярных выражений (если его нет - все переданные данные нам будут подходить), и если есть - проверяем соответствует ли значение колонки соответствующему регекспу, и если нет - бросаем исключение Zend_Db_Table_Exception.

Функции проверки есть, надо бы их где-нибудь использовать. Финальный листинг файла ModelDb.php:

require_once 'Zend/Db/Table.php';

abstract class ModelDb extends Zend_Db_Table
{
    /**
     * var array    Array of regexps
     */
    protected $_validate = array();

    /**
     * Remove pairs, not matched to table columns, from $data.
     *
     * @param array $data   Column-value pairs.
     * @return array        Column-value pairs
     */
    protected function _check_columns(array $data)
    {
        if(empty($this->_cols)) {
            $this->_setupMetadata();
        }

        $ret = array();
        foreach($data as $key => $value)
        {
            if(isset($this->_cols[$key])) {
                $ret[$key] = $value;
            }
        }

        return $ret;
    }

    /**
     * Validates data
     *
     * @param  array $data   Column-value pairs.
     * @return void
     * @throw  Zend_Db_Table_Exception, when data is not valid
     */
    protected function validate(array $data)
    {
        if(isset($this->_validate)) {
            foreach($data as $key => $value)
            {
                if(@$this->_validate[$key] && !preg_match($this->_validate[$key], $value)) {
                    require_once 'Zend/Db/Table/Exception.php';
                    throw new Zend_Db_Table_Exception("Entered value \"$value\" is not valid");
                }
            }
        }
    }

    public function insert(array $data)
    {
        $data = $this->check_columns($data);
        $this->validate($data);
        return parent::insert($data);
    }

    public function update(array $data, $where)
    {
        $data = $this->check_columns($data);
        $this->validate($data);
        return parent::update($data, $where);
    }
}

Как мы видим - добавилось 2 функции, которые вызывают наши validate() и check_columns() и которые заменяют функции с такими же именами класса Zend_Db_Table.

Вывод из всего - теперь нам нужно наследовать наши классы таблиц не от Zend_Db_Table, а от ModelDb; и для проверки введенных значений нужно в классе таблицы создать переменную $_validate с нужными вам regexp’ами.

На сегодня все, если что-то будет непонятно или вы найдете у меня ошибки - пишите в комменты.

В ближайшии дни - интеграция проверок с Zend_Validate, а также учет типов колонок в таблице.

written by fxposter \\ tags: , ,

One Ping to “Zend Database Table”

  1. Использование Zend_Db_Table » Блог FX'а Says:

    […] главу о Zend_Db из документации Zend Framework’а. А также – мой пост про Zend_Db_Table, посвященный его улучшению (правда, я не знаю, […]


Leave a Reply