mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-12 11:06:54 +01:00
244 lines
16 KiB
Plaintext
244 lines
16 KiB
Plaintext
Объекты доступа к данным (DAO)
|
||
==============================
|
||
|
||
Объекты доступа к данным (DAO) предоставляют общий API для доступа к данным, хранящимся в
|
||
различных СУБД. Это позволяет без проблем поменять используемую СУБД на любую другую
|
||
без необходимости изменения кода, использующего DAO для доступа к данным.
|
||
|
||
Yii DAO является надстройкой над [PHP Data Objects (PDO)](http://www.php.net/manual/ru/book.pdo.php) -
|
||
расширением, которое предоставляет унифицированный доступ к данным многих популярных
|
||
СУБД, таких как MySQL, PostgreSQL. Поэтому для использования Yii DAO необходимо, чтобы
|
||
были установлены расширение PDO и соответствующий используемой базе данных драйвер PDO (например, `PDO_MYSQL`).
|
||
|
||
Yii DAO состоит из четырёх основных классов:
|
||
|
||
- [CDbConnection]: представляет подключение к базе данных.
|
||
- [CDbCommand]: представляет запрос к базе данных, который необходимо выполнить.
|
||
- [CDbDataReader]: представляет однонаправленный поток строк данных, возвращаемых в ответ на запрос.
|
||
- [CDbTransaction]: представляет транзакцию базы данных.
|
||
|
||
Далее мы проиллюстрируем использование Yii DAO на различных примерах.
|
||
|
||
Соединение с базой данных
|
||
-------------------------
|
||
|
||
Для установления соединения с базой необходимо создать экземпляр класса [CDbConnection] и
|
||
активировать его. Дополнительная информация, необходимая для подключения к БД (хост, порт, имя пользователя, пароль и т.д.), указывается
|
||
в DSN (Data Source Name). В случае возникновения ошибки в процессе соединения с БД будет выброшено исключение
|
||
(например, неверный DSN или неправильные имя пользователя/пароль).
|
||
|
||
~~~
|
||
[php]
|
||
$connection=new CDbConnection($dsn,$username,$password);
|
||
// устанавливаем соединение
|
||
// можно использовать конструкцию try…catch для перехвата возможных исключений
|
||
$connection->active=true;
|
||
…
|
||
$connection->active=false; // close connection
|
||
~~~
|
||
|
||
Формат DSN зависит от используемого драйвера PDO. Как правило, DSN состоит из имени драйвера PDO,
|
||
за которым следует двоеточие, далее указываются параметры подключения, соответствующие синтаксису подключения
|
||
используемого драйвера. Подробнее с этим можно ознакомиться в [документации PDO](http://www.php.net/manual/ru/pdo.construct.php).
|
||
Ниже представлены несколько основных форматов 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], мы можем использовать его
|
||
в качестве [компонента приложения](/doc/guide/basics.application#application-component). Для этого нужно настроить
|
||
компонент `db` в [конфигурации приложения](/doc/guide/basics.application#application-configuration)
|
||
следующим образом:
|
||
|
||
~~~
|
||
[php]
|
||
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.
|
||
Этот способ даёт нам возможность использовать одно и то же подключение к БД в любом месте кода.
|
||
|
||
Выполнение SQL запросов
|
||
------------------------
|
||
|
||
Когда соединение с БД установлено, мы можем выполнять SQL запросы, используя [CDbCommand].
|
||
Для этого необходимо создать экземпляр класса [CDbCommand] путём вызова [CDbConnection::createCommand()], указав SQL выражение:
|
||
|
||
~~~
|
||
[php]
|
||
$connection=Yii::app()->db; // так можно делать, если в конфигурации настроен компонент соединения "db"
|
||
// В противном случае можно создать соединение явно:
|
||
// $connection=new CDbConnection($dsn,$username,$password);
|
||
$command=$connection->createCommand($sql);
|
||
// при необходимости SQL выражение можно изменить:
|
||
// $command->text=$newSQL;
|
||
~~~
|
||
|
||
Существуют два способа выполнения SQL запросов с использованием [CDbCommand]:
|
||
|
||
- [execute()|CDbCommand::execute]: выполняет SQL запросы `INSERT`, `UPDATE` и `DELETE`.
|
||
В случае успешного выполнения возвращает количество затронутых строк.
|
||
|
||
- [query()|CDbCommand::query]: выполняет SQL запросы, возвращающие наборы данных, например, запросы `SELECT`.
|
||
В случае успешного выполнения возвращает экземпляр класса [CDbDataReader], обеспечивающий доступ к полученным данным.
|
||
Для удобства также реализованы методы вида `queryXXX()`, возвращающие результаты напрямую.
|
||
|
||
Если в процессе выполнения SQL запроса возникнет ошибка, то будет выброшено исключение.
|
||
|
||
~~~
|
||
[php]
|
||
$rowCount=$command->execute(); // выполнение запроса типа `INSERT`, `UPDATE` или `DELETE`
|
||
$dataReader=$command->query(); // выполнение запроса типа `SELECT`
|
||
$rows=$command->queryAll(); // возвращает все строки результата запроса
|
||
$row=$command->queryRow(); // возвращает первую строку результата запроса
|
||
$column=$command->queryColumn(); // возвращает первый столбец результата запроса
|
||
$value=$command->queryScalar(); // возвращает значение первого поля первой строки результата запроса
|
||
~~~
|
||
|
||
Обработка результатов запроса
|
||
-----------------------------
|
||
|
||
После того как [CDbCommand::query()] создаст экземпляр класса [CDbDataReader], мы можем
|
||
получить результат запроса построчно путём повторения вызовов метода [CDbDataReader::read()].
|
||
Для получения данных строка за строкой можно также использовать [CDbDataReader] в конструкциях `foreach`.
|
||
|
||
~~~
|
||
[php]
|
||
$dataReader=$command->query();
|
||
// многократно вызываем read() до возврата методом значения false
|
||
while(($row=$dataReader->read())!==false) { … }
|
||
// используем foreach для построчного обхода данных
|
||
foreach($dataReader as $row) { … }
|
||
// получаем все строки разом в виде массива
|
||
$rows=$dataReader->readAll();
|
||
~~~
|
||
|
||
> Note|Примечание: Все методы `queryXXX()`, в отличие от [query()|CDbCommand::query], возвращают
|
||
данные напрямую. Например, метод [queryRow()|CDbCommand::queryRow] возвращает массив, соответствующий первой
|
||
строке результата запроса.
|
||
|
||
Использование транзакций
|
||
------------------------
|
||
|
||
В случае когда приложение выполняет несколько запросов, каждый из которых что-то пишет или
|
||
читает из БД, важно удостовериться, что набор запросов выполнен полностью, а не частично.
|
||
В этой ситуации можно воспользоваться транзакциями, представляющими собой экземпляры класса [CDbTransaction]:
|
||
|
||
- Начало транзакции.
|
||
- Выполнение запросов по очереди. В этот момент все изменения в базе недоступны извне.
|
||
- Подтверждение транзакции. Если транзакция проведена успешно, то изменения становятся общедоступны.
|
||
- При возникновении ошибки в ходе выполнения запросов происходит откат транзакции в начальное состояние.
|
||
|
||
Эту последовательность действий можно реализовать следующим образом:
|
||
|
||
~~~
|
||
[php]
|
||
$transaction=$connection->beginTransaction();
|
||
try
|
||
{
|
||
$connection->createCommand($sql1)->execute();
|
||
$connection->createCommand($sql2)->execute();
|
||
//… прочие SQL запросы
|
||
$transaction->commit();
|
||
}
|
||
catch(Exception $e) // в случае возникновения ошибки при выполнении одного из запросов выбрасывается исключение
|
||
{
|
||
$transaction->rollback();
|
||
}
|
||
~~~
|
||
|
||
Привязка параметров
|
||
---------------------
|
||
|
||
Для предотвращения [SQL-инъекций](http://ru.wikipedia.org/wiki/SQL_injection) и
|
||
повышения производительности при выполнении однотипных SQL запросов мы
|
||
можем «подготавливать» SQL выражения, используя маркеры параметров (placeholders), которые в процессе привязки будут
|
||
заменяться на реальные значения.
|
||
|
||
Маркеры параметров могут быть именованными (уникальные маркеры) или неименованными (вопросительные знаки). Для замены маркеров на
|
||
реальные значения нужно вызвать методы [CDbCommand::bindParam()] или [CDbCommand::bindValue()].
|
||
Экранировать или заключать в кавычки значения параметров не нужно, используемый драйвер базы данных всё сделает сам.
|
||
Привязку параметров необходимо осуществить до выполнения SQL запроса.
|
||
|
||
~~~
|
||
[php]
|
||
// выражение 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()|CDbCommand::bindParam] и [bindValue()|CDbCommand::bindValue] очень похожи.
|
||
Единственное различие состоит в том, что первый привязывает параметр к ссылке на переменную PHP,
|
||
а второй — к значению. Для параметров, представляющих большой объем данных, с точки зрения производительности
|
||
предпочтительнее использовать метод [bindParam()|CDbCommand::bindParam].
|
||
|
||
Подробнее о привязке параметров можно узнать в
|
||
[соответствующей документации PHP](http://php.net/manual/en/pdostatement.bindparam.php).
|
||
|
||
|
||
Привязка полей
|
||
----------------
|
||
|
||
При получении результатов запроса мы также можем привязать поля таблицы к переменным PHP.
|
||
Это позволяет автоматически присваивать значения переменным при чтении очередной строки:
|
||
|
||
~~~
|
||
[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 текущей строки
|
||
}
|
||
~~~
|
||
|
||
Использование префиксов таблиц
|
||
-----------------------------
|
||
|
||
Yii предоставляет встроенную поддержку префиксов таблиц.
|
||
Префикс таблиц — это строка, предваряющая имена таблиц в текущей подключённой БД.
|
||
В основном, префиксы используются на виртуальном (shared) хостинге, где к одной БД
|
||
подключаются несколько приложений, использующих различные префиксы таблиц в целях избежания конфликтов имён.
|
||
Например, одно приложение использует префикс `tbl_`, а другое — `yii_`.
|
||
|
||
Для использования префикса таблиц установите свойство [CDbConnection::tablePrefix].
|
||
Затем в SQL выражениях используйте `{{TableName}}` для указания имён таблиц, где `TableName` — имя
|
||
таблицы без префикса. Например, если БД содержит таблицу `tbl_user`, где `tbl_` — это префикс таблиц,
|
||
то мы можем использовать следующий код для получения списка пользователей:
|
||
|
||
~~~
|
||
[php]
|
||
$sql='SELECT * FROM {{user}}';
|
||
$users=$connection->createCommand($sql)->queryAll();
|
||
~~~
|
||
|
||
<div class="revision">$Id: database.dao.txt 2890 2011-01-18 15:58:34Z qiang.xue $</div> |