Files
yii/docs/guide/ru/database.dao.txt
2012-07-25 00:29:17 +04:00

244 lines
16 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Объекты доступа к данным (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>