mirror of
https://github.com/yiisoft/yii.git
synced 2026-03-06 00:04:07 +01:00
266 lines
11 KiB
Plaintext
266 lines
11 KiB
Plaintext
Data Access Objects (DAO)
|
|
=========================
|
|
|
|
Data Access Objects (DAO, Objetos de Acesso a Dados), fornecem uma API genérica
|
|
para acessar dados em diferentes tipos de bancos de dados. Como resultado, pode-se
|
|
alterar o sistema de banco de dados sem haver a necessidade de alterar o código
|
|
que utiliza DAO para fazer o acesso.
|
|
|
|
O DAO do Yii é feito utilizando a extensão [PHP Data Objects (PDO)](http://php.net/manual/en/book.pdo.php),
|
|
que fornece um acesso de dados unificado para muitos SGBD populares, tais como
|
|
MySQL e PostgreSQL. Por esse motivo, para utilizar o DAO no Yii, a extensão PDO
|
|
deve estar instalada, bem como o driver PDO específico para o banco de dados
|
|
utilizado (por exemplo, `PDO_MYSQL`).
|
|
|
|
O DAO no Yii, consiste, basicamente, das quatro classes seguinte:
|
|
|
|
- [CDbConnection]: representa uma conexão com o banco de dados.
|
|
- [CDbCommand]: representa uma instrução SQL a ser executada no banco de dados.
|
|
- [CDbDataReader]: representa um conjunto de registros retornados, navegável apenas para frente.
|
|
- [CDbTransaction]: representa uma transação (transaction) no banco de dados.
|
|
|
|
A seguir, apresentaremos a utilização do DAO no Yii em diferente cenários.
|
|
|
|
Estabelecendo uma Conexão com o Banco de Dados
|
|
----------------------------------------------
|
|
|
|
Para estabelecer uma conexão com o banco de dados, criamos uma instância de
|
|
[CDbConnection] e a ativamos. É necessário o nome da fonte de dados (data source
|
|
name, DSN) para conectar-se ao banco. Também podem ser necessários o nome de
|
|
usuário e senha para o acesso. Uma exceção será disparada caso ocorra um erro
|
|
ao estabelecer a conexão (por exemplo, um DSN incorreto, ou usuário/senha inválidos).
|
|
|
|
~~~
|
|
[php]
|
|
$connection=new CDbConnection($dsn,$username,$password);
|
|
// estabelece a conexão. Você pode utilizar um try... catch tratar exceções
|
|
$connection->active=true;
|
|
......
|
|
$connection->active=false; // fecha a conexão
|
|
~~~
|
|
|
|
O formato do DSN depende do driver PDO em uso. Geralmente, o DSN é formado pelo nome
|
|
do driver PDO, seguido por ":", seguido pela sintaxe de conexão específica do driver.
|
|
Veja [PDO documentation](http://www.php.net/manual/en/pdo.construct.php) para mais
|
|
informações. Abaixo temos uma lista dos formatos de DSN mais utilizados:
|
|
|
|
- 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`
|
|
|
|
Como a classe [CDbConnection] estende a classe [CApplicationComponent], podemos
|
|
utiliza-la como um [componente da aplicação](/doc/guide/basics.application#application-component).
|
|
Para isso, configure um componente chamado `db` (ou qualquer outro nome) na
|
|
[configuração da aplicação](/doc/guide/basics.application#application-configuration),
|
|
como no código a seguir:
|
|
|
|
~~~
|
|
[php]
|
|
array(
|
|
......
|
|
'components'=>array(
|
|
......
|
|
'db'=>array(
|
|
'class'=>'CDbConnection',
|
|
'connectionString'=>'mysql:host=localhost;dbname=testdb',
|
|
'username'=>'root',
|
|
'password'=>'password',
|
|
'emulatePrepare'=>true, // necessário em algumas instalações do MySQL
|
|
),
|
|
),
|
|
)
|
|
~~~
|
|
|
|
Podemos então acessar a conexão com o banco via `Yii::app()->db`, que é
|
|
ativada automaticamente, a não ser que se configure a propriedade
|
|
[CDbConnection::autoConnect] para false. Dessa maneira, uma única conexão com o
|
|
banco de dados pode ser compartilhada entre diversas partes de seu código.
|
|
|
|
Executando Instruções SQL
|
|
-------------------------
|
|
|
|
Uma vez que a conexão com o banco de dados tenha sido estabelecida, comandos SQL
|
|
podem ser executados utilizando-se a classe [CDbCommand]. Você pode criar uma
|
|
instância de [CDbCommand] utilizando o método [CDbConnection::createCommand()],
|
|
com a instrução SQL desejada:
|
|
|
|
~~~
|
|
[php]
|
|
$connection=Yii::app()->db; // Aqui, estamos assumind que você configurou um conexão com o banco de dados
|
|
// Caso contrario, você deverá cria-la explicitamente:
|
|
// $connection=new CDbConnection($dsn,$username,$password);
|
|
$command=$connection->createCommand($sql);
|
|
// se necessário, a instrução SQL pode ser alterada da seguinte maneira:
|
|
// $command->text=$newSQL;
|
|
~~~
|
|
|
|
Uma instrução SQL pode ser executada via [CDbCommand] de duas maneiras:
|
|
|
|
- [execute()|CDbCommand::execute]: executa uma instrução SQL que não retorna
|
|
resultados, tal como `INSERT`, `UPDATE` e `DELETE`. Em caso de sucesso, retorna
|
|
a quantidade de registros afetados pela consulta.
|
|
|
|
- [query()|CDbCommand::query]: executa uma instrução que retorna registros,
|
|
tal como `SELECT`. Em caso de sucesso, retorna uma instância de [CDbDataReader],
|
|
que pode ser utilizada para acessar os registros encontrados. Por conveniência,
|
|
um conjunto de métodos `queryXXX()` também está implementado, e retornam
|
|
diretamente o resultado da consulta.
|
|
|
|
Uma exceção será disparada caso ocorram erros durante a execução de instruções
|
|
SQL.
|
|
|
|
~~~
|
|
[php]
|
|
$rowCount=$command->execute(); // executa uma consulta que não retorna resultados
|
|
$dataReader=$command->query(); // executa uma consulta SQL
|
|
$rows=$command->queryAll(); // consulta e retorna todos os resultados encontrados
|
|
$row=$command->queryRow(); // consulta e retorna apenas o primeiro registro do resultado
|
|
$column=$command->queryColumn(); // consulta e retorna a primeira coluna do resultado
|
|
$value=$command->queryScalar(); // consulta e retorna o primeiro campo do primeiro registro
|
|
~~~
|
|
|
|
Obtendo Resultados de Consultas
|
|
-------------------------------
|
|
|
|
Depois que o método [CDbCommand::query()] gerar uma instância de [CDbDataReader],
|
|
você pode recuperar os registros do resultado através do método [CDbDataReader::read()],
|
|
repetidamente. Você também pode utilizar um [CDbDataReader] dentro de um loop
|
|
`foreach`, para recuperar os registros, um a um:
|
|
|
|
~~~
|
|
[php]
|
|
$dataReader=$command->query();
|
|
// executando read() repetidamente, até que ele retorne false
|
|
while(($row=$dataReader->read())!==false) { ... }
|
|
// utilizando foreach para "navegar" por cada registro encontrado
|
|
foreach($dataReader as $row) { ... }
|
|
// recuperando todos os registros de uma vez, em um vetor
|
|
$rows=$dataReader->readAll();
|
|
~~~
|
|
|
|
> Note|Nota: Diferente do método [query()|CDbCommand::query], todos os métodos
|
|
`queryXXX()`, retornam os dados diretamente. Por exemplo, o método
|
|
[queryRow()|CDbCommand::queryRow] retorna um vetor representando o primeiro
|
|
registro do resultado da consulta.
|
|
|
|
Utilizando Transações (Transactions)
|
|
------------------------------------
|
|
|
|
Quando uma aplicação executa algumas consultas, seja lendo ou gravando informações
|
|
no banco de dados, é importante garantir que todas as consultas tenham sido executadas.
|
|
Nesse caso, uma transação, representada por uma instância de [CDbTransaction],
|
|
pode ser iniciada:
|
|
|
|
- Inicie a transação.
|
|
- Execute as consultas, uma a uma. Todas as atualizações no banco de dados
|
|
não são visíveis aos outros.
|
|
- Encerre a transação. Nesse momento, as atualizações tornam-se visíveis, caso a
|
|
transação tenha encerrado com sucesso.
|
|
- Se uma das consultas falhar, toda a transação é desfeita.
|
|
|
|
O fluxo acima pode ser implementado como no código a seguir:
|
|
|
|
~~~
|
|
[php]
|
|
$transaction=$connection->beginTransaction();
|
|
try
|
|
{
|
|
$connection->createCommand($sql1)->execute();
|
|
$connection->createCommand($sql2)->execute();
|
|
//.... outras execuções de comandos SQL
|
|
$transaction->commit();
|
|
}
|
|
catch(Exception $e) // uma exceção é disparada caso uma das consultas falhe
|
|
{
|
|
$transaction->rollBack();
|
|
}
|
|
~~~
|
|
|
|
Vinculando (Binding) Parâmetros
|
|
-------------------------------
|
|
|
|
Para evitar ataques de [SQL injection](http://en.wikipedia.org/wiki/SQL_injection)
|
|
e aumentar a performance ao executar instruções SQL repetidamente, você pode
|
|
"preparar" um comando SQL com marcadores opcionais que serão substituídos pelos
|
|
valores reais, durante o processo de vinculação de parâmetros.
|
|
|
|
Os marcadores de parâmetros podem ser nomeados (representados por tokens únicos)
|
|
ou anônimos (representados por interrogações). Execute o método [CDbCommand::bindParam()]
|
|
ou [CDbCommand::bindValue()] para substituir esses marcadores pelos parâmetros reais.
|
|
Eles não precisam estar entre aspas: o próprio driver do banco de dados faz isso para você.
|
|
A vinculação de parâmetros deve ser realizada antes da instrução SQL ser executada.
|
|
|
|
~~~
|
|
[php]
|
|
// uma consulta com dois marcadores ":username" e ":email"
|
|
$sql="INSERT INTO tbl_users(username, email) VALUES(:username,:email)";
|
|
$command=$connection->createCommand($sql);
|
|
// substitui o marcador ":username" com o valor atual de $username
|
|
$command->bindParam(":username",$username,PDO::PARAM_STR);
|
|
// substitui o marcador ":email" com o valor atual de $email
|
|
$command->bindParam(":email",$email,PDO::PARAM_STR);
|
|
$command->execute();
|
|
// insere um novo registro com um novo conjunto de parâmetros
|
|
$command->bindParam(":username",$username2,PDO::PARAM_STR);
|
|
$command->bindParam(":email",$email2,PDO::PARAM_STR);
|
|
$command->execute();
|
|
~~~
|
|
|
|
Os métodos [bindParam()|CDbCommand::bindParam] e
|
|
[bindValue()|CDbCommand::bindValue] são similares. A única diferença entre eles
|
|
é que o primeiro vincula um parâmetro utilizando uma referência para a variável
|
|
enquanto o outro utilizar um valor. Para parâmetros que representem uma grande
|
|
quantidade de dados em memória, o primeiro é mais recomendado, devido a uma melhor
|
|
performance.
|
|
|
|
Para mais detalhes sobre a vinculação de parâmetros, veja a
|
|
[documentação do PHP](http://www.php.net/manual/en/pdostatement.bindparam.php).
|
|
|
|
Vinculando Colunas
|
|
------------------
|
|
|
|
Ao recuperar os resultados de uma consulta, você também pode vincular colunas à
|
|
variáveis, de forma que elas sejam automaticamente preenchidas cada vez que um
|
|
novo registro é recuperado:
|
|
|
|
~~~
|
|
[php]
|
|
$sql="SELECT username, email FROM tbl_users";
|
|
$dataReader=$connection->createCommand($sql)->query();
|
|
// vincula a 1ª coluna (username) à variável $username
|
|
$dataReader->bindColumn(1,$username);
|
|
// vincula a 2ª coluna (email) à variável $email
|
|
$dataReader->bindColumn(2,$email);
|
|
while($dataReader->read()!==false)
|
|
{
|
|
// $username e $email contém o nome de usuário e a senha do registro atual
|
|
}
|
|
~~~
|
|
|
|
Utilizando Prefixos de Tabelas
|
|
------------------------------
|
|
|
|
A partir da versão 1.1.0, o Yii framework possui suporte integrado para a utilização
|
|
de prefixos em nomes de tabela. Um prefixo é uma string que será anexada ao início dos
|
|
nomes das tabelas do banco de dados conectado. Normalmente, eles são utilizados em
|
|
ambientes de hospedagem compartilhada, onde múltiplas aplicações utilizam um único
|
|
banco de dados e é necessário diferenciar as tabelas de cada aplicação. Por exemplo,
|
|
uma aplicação pode utilizar o prefixo `tbl_`, enquanto outra utiliza `yii_`.
|
|
|
|
Para utiliza-los, você deverá configurar a propriedade
|
|
[CDbConnection::tablePrefix] com o prefixo desejado. Feito isso, em suas consultas
|
|
SQL você deverá utilizar `{{NomeDaTabela}}`, onde `NomeDaTabela` é o nome da tabela
|
|
sem o prefixo. Por exemplo, se um banco de dados contém uma tabela chamada `tbl_user`
|
|
e `tbl_` é o prefixo configurado, então você pode utilizar o seguinte código para
|
|
realizar consultas nessa tabela:
|
|
|
|
~~~
|
|
[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>
|