mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-07 16:46:51 +01:00
246 lines
16 KiB
Plaintext
246 lines
16 KiB
Plaintext
Объекты доступа к данным (DAO)
|
||
==============================
|
||
|
||
Объекты доступа к данным (DAO) предоставляют общий API для доступа к данным, хранящимся в
|
||
различных СУБД. Это позволяет без проблем поменять используемую СУБД на любую другую
|
||
без необходимости изменения кода, использующего DAO для доступа к данным.
|
||
|
||
Yii DAO является надстройкой над [PHP Data Objects (PDO)](http://php.net/manual/en/book.pdo.php) -
|
||
расширением, которое предоставляет унифицированный доступ к данным многих популярных
|
||
СУБД, таких, как MySQL, PostgreSQL. Для использования Yii DAO необходимо, чтобы
|
||
были установлены расширение PDO и драйвер PDO, соответствующий используемой базе данных (например, `PDO_MYSQL`).
|
||
|
||
Yii DAO состоит из четырех основных классов:
|
||
|
||
- [CDbConnection]: представляет подключение к базе данных;
|
||
- [CDbCommand]: представляет выражение SQL и его исполнение;
|
||
- [CDbDataReader]: преставляет однонаправленный поток строк из данных, возвращаемых в ответ на SQL-запрос;
|
||
- [CDbTransaction]: представляет транзакции базы данных.
|
||
|
||
Ниже мы проиллюстрируем использование Yii DAO.
|
||
|
||
Соединение с базой данных
|
||
-------------------------
|
||
|
||
Для установления соединения с базой необходимо создать экземпляр класса [CDbConnection] и
|
||
активировать его. Дополнительную информацию, необходимую для подключения к БД (хост, порт, имя пользователя, пароль и пр.), указываем
|
||
в DSN. В случае возникновения ошибки в процессе соединения с БД, будет вызвано исключение
|
||
(например, неверный DSN или неправильные имя пользователя/пароль).
|
||
|
||
~~~
|
||
[php]
|
||
$connection=new CDbConnection($dsn,$username,$password);
|
||
// устанавливаем соединение. Можно попробовать try…catch возможных исключений
|
||
$connection->active=true;
|
||
…
|
||
$connection->active=false; // close connection
|
||
~~~
|
||
|
||
Формат DSN зависит от используемого драйвера PDO. Как правило, DSN состоит из имени драйвера PDO,
|
||
за которым следует двоеточие, а далее указываются параметры подключения, соответствующие синтаксису подключения
|
||
используемого драйвера. Подробнее с этим можно ознакомиться в [документации по PDO](http://php.net/manual/en/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-выражение с маркерами параметров (placeholder), которые в процессе привязки будут
|
||
заменяться на реальные значения.
|
||
|
||
Маркеры параметров могут быть именованными (уникальные маркеры) или неименованными (вопросительные знаки). Для замены маркеров на
|
||
реальные значения нужно вызвать [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 текущей строки
|
||
}
|
||
~~~
|
||
|
||
Использование префикса таблиц
|
||
-----------------------------
|
||
|
||
Начиная с версии 1.1.0, Yii обеспечивает встроенную поддержку
|
||
префиксов таблиц. Префикс таблиц — это строка, предваряющая имя таблиц в текущей
|
||
подключенной БД. В основном используется на общем хостинге, где к одной БД
|
||
подключается несколько приложений, использующих различные префиксы таблиц для
|
||
различения принадлежности таблиц к приложению. Например, одно приложение
|
||
использует префикс `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 2266 2010-07-17 13:58:30Z qiang.xue $</div> |