====== DBAL Driver====== DBAL Driver - базовые классы для работы с базами данных, на основе которых построена остальнаях часть пакета DBAL. Условно driver можно разделить на 2 составляющие: * выполнение запросов к базе данных * получение meta-информации о базе данных (хотя это тоже подразумевает под собой выполнение некоторых запросов) Здесь мы рассмотрим архитектуру DBAL более подробно. ===== Классы и интерфейсы, отвечающие за выполнение запросов к базе данных ===== Если вы прочитали [[intro|"Введение в пакет DBAL"]], то уже знаете, что основными понятиями здесь являются * подключение (connection) * утверждения (statements) * наборы данных (record sets) ==== Выполнение SELECT-запросов ==== Рассмотрим, как выглядит статическая диаграмма классов, связанных с выполнением SELECT-запросов ( на примере %%MySql%%-драйвера): {{{limb3:ru:packages:dbal:limb3_dbal_query_statements.png|}}} Интерфейс **lmbDbConnection**, который реализует класс **lmbMysqlConnection** содержит метод newStatement($sql), который на базе sql-кода создает объект утверждения (statement) того или иного типа. При наличии ключевого слова SELECT, lmbMysqlConnection создает объект класса **lmbMysqlQueryStatement**. lmb_require('limb/dbal/src/drivers/mysql/lmbMysqlConnection.class.php'); $config = array('host' => 'localhost', 'user' => 'root', 'password' => 'secret', 'database' => 'my_datatase', 'charset' => 'utf8'); $connection = new lmbMysqlConnection($config); $sql = 'SELECT * FROM news'; $statement = $connection->newStatement($sql); // возвратит объект класса lmbMysqlQueryStatement Объект $statement класса lmnMysqlQueryStatement реализует интерфейс **lmbDbQueryStatement** и содержит ссылку на объект подключения класса lmbMysqlConnection. Создание объекта $statement еще не значит выполнение реального запроса к базе данных. Это происходит в DBAL как можно позже. До выполнения реального запроса можно произвести некоторые модификации, особенно это актуально при работе с наборами данных (см. ниже) Из $statement можно получить различные данные: * **getRecordSet()** - возвращает весь набор данных в виде итератора. Итератор поддерживает интерфейс **lmbDbRecordSet**. * **getOneRecord()** - возвращает первую запись из набора данных в виде объекта класса **lmbMysqlRecord**, который реализует интерфейс **lmbDbRecord**. Выполняет реальный запрос. * **getOneValue()** - возвращает первое значение из записи, полученной в результате выполнения запроса. Актуально для "SELECT count(*) as numb_of FROM ..." запросов. Выполняет реальный запрос. Самым интересным для нас является метод lmbDbQueryStatement :: getRecordSet(), который возвращает итератор с набором данных. По сути getRecordSet() возвращает курсор на результат запроса - это значит, что данные в память загружаются только по мере прохождения по итератору, а не сразу все. Итак, lmbDbQueryStatement :: getRecordSet() возвращает объект, который реализует интерфейс lmbDbRecordSet. В нашем случае это будет объект класса **lmbMysqlRecordSet**. lmbDbRecordSet является расширением для интерфейса [[limb3:ru:packages:core:lmb_collection_interface|lmbCollectionInterface]], это значит, что набор данных должен поддерживать: * стандартную работу через методы интерфейса **Iterator** (rewind, valid, next, current, key) * ограничение размера выборки при помощи метода **paginate($offset, $limit)** * сортировку при помощи метода sort**($sort_params = array())** * получение количества элементов в выборке при помощи метода **count()** Пример: [...] $statement = $connection->newStatement($sql); // возвратит объект класса lmbMysqlQueryStatement $rs = $statement->getRecordSet(); echo $rs->count(); // выведет количество элементов в выборке. count() приводит к выполнению отдельного запроса к базе данных. $rs->sort(array('title' => 'ASC')); $rs->paginate(0, 10); for($rs->rewind(); $rs->valid(); $rs->next()) { $record = $rs->current(); echo $record['title'] . "\n"; } Обратите внимание, методы **at($pos)** и **count()** интерфейса lmbDbRecordSet приводят к выполнению отдельных запросов к базе данных, отличных от "основного" запроса record_set-а. Реальный запрос к базе данных при работе с record_set-ом происходит при вызове метода rewind(), явно или неявно(например, при использовании конструкции foreach). Метод current() record_set-а возвращает объект, реализующий интерфейс lmbDbRecord (в нашем случае это объект класса lmbMysqlRecord), который в свою очередь является расширением интерфейса [[limb3:ru:packages:core:lmb_set_interface|lmbSetInterface]]. ==== Выполнение запросов на модификацию ==== Рассмотрим, как выглядит статическая диаграмма классов, связанных с выполнением запросов, связанных с модификацией базы данных(на примере %%MySql%%-драйвера): {{{limb3:ru:packages:dbal:limb3_dbal_modify_statements.png|}}} Эти запросы отличаются тем, что на запросы DELETE,UPDATE и INSERT, создаются объекты, реализующие интерфейсы lmbDbManipulationStatement и lmbDbInsertStatement. Основным методом для выполнения утверждения является метод lmbDbStatement :: **execute()**. Интерфейс lmbDbManipulationStatement также содержит метод **getAffectedRowsCount()** который возвращает количество записей в базе данных, затронутых выполнением запроса. $sql = "DELETE * FROM founding_fathers WHERE last_name LIKE :last_name:%"; $stmt = $connection->newStatement($sql); $stmt->setVarChar('last_name', 'Iva'); $stmt->execute(); // выполнит запрос. echo 'Removed ' . $stmt->getAffectedRowCount() . ' rows'; Конструкции вида :last_name: - это placeholder-ы для значений. Интейрфейс lmbDbStatement содержит различные методы для установки значений в placeholder-ы, type-cast и escape значений: * **setInteger($name, $value);** * **setChar($name, $value);** * **setFloat($name, $value);** * и другие. Есть более базовый метод lmbDbStatement :: **set($name, $value)**, который пытается самостоятельно угадать тип переданного значения. Интерфейс lmbDbInsertStatement содержит метод **insertId($field_name = 'id')**, который является аналогом execute(), однако возвращает значение поля $field_name только что вставленной записи. Этот метод полезен, например, если первичный ключ является автоинкрементным. ===== Подсистема получения meta-информаации ===== Driver также содержит средства для получения meta-информации о базе данных. Статическая диаграмма классов выглядит следующим образом: {{{limb3:ru:packages:dbal:limb3_dbal_meta.png|}}} Мы не будем заострять внимание на этой подсистеме, и подробно ее описывать. Meta-подсистема активно используется в классе [[lmb_table_gateway|lmbTableGateway]] и в пакете [[limb3:ru:packages:active_record|ACTIVE_RECORD]]. Получение информации о структу базы данных занимает некоторое время, поэтому для ускорения работы эти информацию лучше кешировать. Чтобы включить эту возможность нужно определить значение константы **LIMB_CACHE_DB_META_IN_FILE** как true, где-то в setup.php файле приложения, например: @define('LIMB_CACHE_DB_META_IN_FILE', true);