Объекты доступа к данным (DAO)

Объекты доступа к данным (DAO) предоставляют общий API для доступа к данным, хранящимся в различных СУБД. Это позволяет без проблем поменять используемую СУБД на любую другую без необходимости изменения кода, использующего DAO для доступа к данным.

Yii DAO является надстройкой над PHP Data Objects (PDO) - расширением, которое предоставляет унифицированный доступ к данным многих популярных СУБД, таких как MySQL, PostgreSQL. Поэтому для использования Yii DAO необходимо, чтобы были установлены расширение PDO и соответствующий используемой базе данных драйвер PDO (например, PDO_MYSQL).

Yii DAO состоит из четырёх основных классов:

  • CDbConnection: представляет подключение к базе данных.
  • CDbCommand: представляет запрос к базе данных, который необходимо выполнить.
  • CDbDataReader: представляет однонаправленный поток строк данных, возвращаемых в ответ на запрос.
  • CDbTransaction: представляет транзакцию базы данных.

Далее мы проиллюстрируем использование Yii DAO на различных примерах.

1. Соединение с базой данных

Для установления соединения с базой необходимо создать экземпляр класса CDbConnection и активировать его. Дополнительная информация, необходимая для подключения к БД (хост, порт, имя пользователя, пароль и т.д.), указывается в DSN (Data Source Name). В случае возникновения ошибки в процессе соединения с БД будет выброшено исключение (например, неверный DSN или неправильные имя пользователя/пароль).

$connection=new CDbConnection($dsn,$username,$password);
// устанавливаем соединение
// можно использовать конструкцию try…catch для перехвата возможных исключений
$connection->active=true;
…
$connection->active=false;  // close connection

Формат DSN зависит от используемого драйвера PDO. Как правило, DSN состоит из имени драйвера PDO, за которым следует двоеточие, далее указываются параметры подключения, соответствующие синтаксису подключения используемого драйвера. Подробнее с этим можно ознакомиться в документации PDO. Ниже представлены несколько основных форматов DSN:

  • SQLite: sqlite:/path/to/dbfile
  • MySQL: mysql:host=localhost;dbname=testdb
  • PostgreSQL: pgsql:host=localhost;port=5432;dbname=testdb
  • SQL Server: mssql:host=localhost;dbname=testdb
  • Oracle: oci:dbname=//localhost:1521/testdb

Поскольку CDbConnection наследует класс CApplicationComponent, мы можем использовать его в качестве компонента приложения. Для этого нужно настроить компонент db в конфигурации приложения следующим образом:

array('components'=>array('db'=>array(
            'class'=>'CDbConnection',
            'connectionString'=>'mysql:host=localhost;dbname=testdb',
            'username'=>'root',
            'password'=>'password',
            'emulatePrepare'=>true,  // необходимо для некоторых версий инсталляций MySQL
        ),
    ),
)

Теперь мы можем получить доступ к соединению с БД через Yii::app()->db. Чтобы соединение не активировалось автоматически, необходимо установить значение CDbConnection::autoConnect в false. Этот способ даёт нам возможность использовать одно и то же подключение к БД в любом месте кода.

2. Выполнение SQL-запросов

Когда соединение с БД установлено, мы можем выполнять SQL-запросы, используя CDbCommand. Для этого необходимо создать экземпляр класса CDbCommand путём вызова CDbConnection::createCommand(), указав SQL-выражение:

$connection=Yii::app()->db; // так можно делать, если в конфигурации настроен компонент соединения "db"
// В противном случае можно создать соединение явно:
// $connection=new CDbConnection($dsn,$username,$password);
$command=$connection->createCommand($sql);
// при необходимости SQL-выражение можно изменить:
// $command->text=$newSQL;

Существуют два способа выполнения SQL-запросов с использованием CDbCommand:

  • execute(): выполняет SQL-запросы INSERT, UPDATE и DELETE. В случае успешного выполнения возвращает количество затронутых строк.

  • query(): выполняет SQL-запросы, возвращающие наборы данных, например, запросы SELECT. В случае успешного выполнения возвращает экземпляр класса CDbDataReader, обеспечивающий доступ к полученным данным. Для удобства также реализованы методы вида queryXXX(), возвращающие результаты напрямую.

Если в процессе выполнения SQL-запроса возникнет ошибка, то будет выброшено исключение.

$rowCount=$command->execute();   // выполнение запроса типа `INSERT`, `UPDATE` или `DELETE`
$dataReader=$command->query();   // выполнение запроса типа `SELECT`
$rows=$command->queryAll();      // возвращает все строки результата запроса
$row=$command->queryRow();       // возвращает первую строку результата запроса
$column=$command->queryColumn(); // возвращает первый столбец результата запроса
$value=$command->queryScalar();  // возвращает значение первого поля первой строки результата запроса

3. Обработка результатов запроса

После того как CDbCommand::query() создаст экземпляр класса CDbDataReader, мы можем получить результат запроса построчно путём повторения вызовов метода CDbDataReader::read(). Для получения данных строка за строкой можно также использовать CDbDataReader в конструкциях foreach.

$dataReader=$command->query();
// многократно вызываем read() до возврата методом значения false
while(($row=$dataReader->read())!==false) {}
// используем foreach для построчного обхода данных
foreach($dataReader as $row) {}
// получаем все строки разом в виде массива
$rows=$dataReader->readAll();

Примечание: Все методы queryXXX(), в отличие от query(), возвращают данные напрямую. Например, метод queryRow() возвращает массив, соответствующий первой строке результата запроса.

4. Использование транзакций

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

  • Начало транзакции.
  • Выполнение запросов по очереди. В этот момент все изменения в базе недоступны извне.
  • Подтверждение транзакции. Если транзакция проведена успешно, то изменения становятся общедоступны.
  • При возникновении ошибки в ходе выполнения запросов происходит откат транзакции в начальное состояние.

Эту последовательность действий можно реализовать следующим образом:

$transaction=$connection->beginTransaction();
try
{
    $connection->createCommand($sql1)->execute();
    $connection->createCommand($sql2)->execute();
    //… прочие SQL-запросы
    $transaction->commit();
}
catch(Exception $e) // в случае возникновения ошибки при выполнении одного из запросов выбрасывается исключение
{
    $transaction->rollback();
}

5. Привязка параметров

Для предотвращения SQL-инъекций и повышения производительности при выполнении однотипных SQL-запросов мы можем «подготавливать» SQL-выражения, используя маркеры параметров (placeholders), которые в процессе привязки будут заменяться на реальные значения.

Маркеры параметров могут быть именованными (уникальные маркеры) или неименованными (вопросительные знаки). Для замены маркеров на реальные значения нужно вызвать методы CDbCommand::bindParam() или CDbCommand::bindValue(). Экранировать или заключать в кавычки значения параметров не нужно, используемый драйвер базы данных всё сделает сам. Привязку параметров необходимо осуществить до выполнения SQL-запроса.

// выражение SQL с двумя именованными маркерами «:username» и «:email»
$sql="INSERT INTO tbl_user(username, email) VALUES(:username,:email)";
$command=$connection->createCommand($sql);
// заменяем маркер «:username» на соответствующее значение имени пользователя
$command->bindParam(":username",$username,PDO::PARAM_STR);
// заменяем маркер «:email» на соответствующее значение электронной почты
$command->bindParam(":email",$email,PDO::PARAM_STR);
$command->execute();
// вставляем следующую строку с новыми параметрами
$command->bindParam(":username",$username2,PDO::PARAM_STR);
$command->bindParam(":email",$email2,PDO::PARAM_STR);
$command->execute();

Методы bindParam() и bindValue() очень похожи. Единственное различие состоит в том, что первый привязывает параметр к ссылке на переменную PHP, а второй — к значению. Для параметров, представляющих большой объем данных, с точки зрения производительности предпочтительнее использовать метод bindParam().

Подробнее о привязке параметров можно узнать в соответствующей документации PHP.

6. Привязка полей

При получении результатов запроса мы также можем привязать поля таблицы к переменным PHP. Это позволяет автоматически присваивать значения переменным при чтении очередной строки:

$sql="SELECT username, email FROM tbl_user";
$dataReader=$connection->createCommand($sql)->query();
// привязываем первое поле (username) к переменной $username
$dataReader->bindColumn(1,$username);
// привязываем второе поле (email) к переменной $email
$dataReader->bindColumn(2,$email);
while($dataReader->read()!==false)
{
    // переменные $username и $email получают значения полей username и email текущей строки
}

7. Использование префиксов таблиц

Yii предоставляет встроенную поддержку префиксов таблиц. Префикс таблиц — это строка, предваряющая имена таблиц в текущей подключённой БД. В основном, префиксы используются на виртуальном (shared) хостинге, где к одной БД подключаются несколько приложений, использующих различные префиксы таблиц в целях избежания конфликтов имён. Например, одно приложение использует префикс tbl_, а другое — yii_.

Для использования префикса таблиц установите свойство CDbConnection::tablePrefix. Затем в SQL-выражениях используйте {{TableName}} для указания имён таблиц, где TableName — имя таблицы без префикса. Например, если БД содержит таблицу tbl_user, где tbl_ — это префикс таблиц, то мы можем использовать следующий код для получения списка пользователей:

$sql='SELECT * FROM {{user}}';
$users=$connection->createCommand($sql)->queryAll();

Be the first person to leave a comment

Please to leave your comment.