0 follower

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), 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.

1. 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).

$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 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. Para isso, configure um componente chamado db (ou qualquer outro nome) na configuração da aplicação, como no código a seguir:

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.

2. 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:

$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(): 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(): 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.

$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

3. 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:

$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();

Nota: Diferente do método query(), todos os métodos queryXXX(), retornam os dados diretamente. Por exemplo, o método queryRow() retorna um vetor representando o primeiro registro do resultado da consulta.

4. 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:

$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();
}

5. Vinculando (Binding) Parâmetros

Para evitar ataques de 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.

// 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() e 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.

6. 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:

$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
}

7. 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:

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

Found a typo or you think this page needs improvement?
Edit it on github !