DAO genérico
1Apresentação
Como você pôde ver rapidamente na introdução, o Temma é capaz de criar DAOs automaticamente.
O caso mais simples é quando você cria um controlador que precisa acessar apenas uma tabela do banco de dados.
Basta então que esse controlador tenha um atributo protegido chamado $_temmaAutoDao, definido com o valor booleano
true.
Se tomarmos o exemplo dado na introdução:
class Article extends \Temma\Web\Controller {
// informa que o DAO deve ser criado automaticamente
protected $_temmaAutoDao = true;
}
Ao fazer isso, o Temma criará um objeto do tipo \Temma\Dao\Dao, que será configurado automaticamente
para facilitar a manipulação dos dados armazenados em uma tabela cujo nome é idêntico ao do controlador (article).
Esse objeto estará disponível através do atributo $this->_dao.
2Configuração avançada
2.1Princípio geral
Por padrão, os DAOs pressupõem várias coisas:
- A conexão com o banco de dados está configurada com uma fonte de dados chamada db.
- Se o cache estiver acessível (configurado com uma fonte de dados chamada cache), ele é usado para acelerar o acesso aos dados.
- A tabela está no banco de dados sobre o qual a conexão é aberta.
- O nome da tabela corresponde ao nome do controlador.
- O campo que contém a chave primária é chamado id.
- Todos os campos da tabela devem ser recuperados no acesso.
- Quando obtemos os campos da tabela, obtemos os nomes como estão no banco de dados.
Para poder configurar o funcionamento do DAO (desativar o cache, especificar um nome de banco de dados ou de tabela diferente, renomear os campos, ...), é recomendável escrever um objeto DAO personalizado, que pode conter as informações específicas de que você precisa.
2.2Configuração específica
No entanto, se você estiver na situação em que um controlador precisa manipular dados configurando finamente
o comportamento do DAO, mas você não quer criar um objeto DAO personalizado, é possível
fornecer os parâmetros diretamente no controlador, preenchendo um array associativo.
Observe que essa técnica continua limitada e não deve, em especial, ser usada no caso de a mesma
tabela ser acessada por vários controladores diferentes.
Aqui está um exemplo de uso com configurações específicas:
class Article extends \Temma\Web\Controller {
protected $_temmaAutoDao = [
'cache' => false,
'base' => 'cms',
'table' => 'content',
'id' => 'cid',
'fields' => [
'cid' => 'contentId',
'title',
'login',
'content' => 'text',
'date' => 'creationDate',
'status',
],
];
}
- Linha 2: Definição do array de configuração do DAO.
- Linha 3: Desativa o cache.
- Linha 4: Definição do nome do banco de dados que contém a tabela.
- Linha 5: Definição do nome da tabela.
- Linha 6: Definição do nome do campo que contém a chave primária.
- Linhas 7 a 14: Definição de um array associativo contendo a lista de campos a serem recuperados do banco de dados. Se os campos precisarem ser renomeados, basta declarar um par associativo cuja chave é o nome do campo na tabela, e o valor associado é o nome pelo qual ele deve ser renomeado.
Os parâmetros são independentes; você não precisa redefinir todos eles.
3Operações básicas
Os objetos \Temma\Dao oferecem 6 métodos básicos:
- count(): Conta o número de elementos em uma tabela.
- get(): Retorna todas as informações sobre um elemento da tabela.
- search(): Busca por registros.
- create(): Adiciona um novo elemento na tabela.
- remove(): Remove um ou mais elementos.
- update(): Atualiza um ou mais registros.
Esses métodos são explicados em detalhe abaixo, mas antes veremos como os critérios de busca e os critérios de ordenação são construídos, os quais podem ser usados em alguns desses métodos.
4Critérios de busca
Os DAOs fornecem um mecanismo para compor facilmente filtros de busca. Para isso, você precisa criar um critério, que pode ser passado como parâmetro de determinados métodos.
O tipo mais simples de critério é um array associativo cujas chaves correspondem a campos da tabela, e cujos valores correspondem aos que serão buscados nas linhas selecionadas.
Alternativamente, você pode criar um critério usando o método criteria(), que cria um objeto do tipo \Temma\Dao\Criteria.
Depois você pode combinar chamadas com os seguintes métodos:
- equal(): um campo tem um determinado valor (é possível fornecer uma lista de valores possíveis)
- different(): um campo não tem um determinado valor (é possível fornecer uma lista de valores possíveis)
- like(): um campo de texto satisfaz uma expressão de busca
- notLike(): um campo de texto não corresponde a uma expressão de busca
- is(): um campo booleano é definido como "true"
- isNot(): um campo booleano é definido como "false"
- lessThan(): o valor de um campo numérico é menor que um determinado valor
- greaterThan(): o valor de um campo numérico é maior que um determinado valor
- lessOrEqualTo(): o valor de um campo numérico é menor ou igual a um determinado valor
- greaterOrEqualTo(): o valor de um campo numérico é maior ou igual a um determinado valor
Existem aliases, para simplificar a escrita:
- has(): equivalente a is()
- hasNot(): equivalente a isNot()
- eq(): equivalente a equal()
- ne(): equivalente a different()
- lt(): equivalente a lessThan()
- gt(): equivalente a greaterThan()
- le(): equivalente a lessOrEqualTo()
- ge(): equivalente a greaterOrEqualTo()
Por padrão, os critérios são combinados usando operadores booleanos "AND", o que leva à criação de um sistema de filtragem de dados:
apenas os dados que atendem a todas as condições são recuperados. É possível combinar todos os critérios de acordo com o operador "OR"
passando a string "or" como parâmetro do método criteria().
Também é possível combinar os critérios com operadores booleanos graças aos métodos and() e
or(), que recebem cada um um novo objeto de critério como parâmetro.
4.1Exemplo de um array associativo
// exclui registros cuja categoria é "article"
// e cuja visibilidade é "hidden"
$criteria = [
'category' => 'article',
'visibility' => 'hidden',
];
$this->_dao->remove($criteria);
4.2Exemplo equal() e is()
// busca registros cujo endereço de email é igual a "tom@tom.com"
// E cujo booleano "free" é verdadeiro
$critera = $this->_dao->criteria()
->equal('email', 'tom@tom.com')
->is('free');
$users = $this->_dao->search($criteria);
- Linha 3: Criação do objeto de critério.
- Linha 4: Adição de um critério de igualdade. O campo email deve ter o valor tom@tom.com.
- Linha 5: Adição de um critério de igualdade em um booleano. O campo free deve estar definido como "true".
- Linha 6: O objeto de critério é usado com o método search() do DAO para recuperar os elementos correspondentes ao critério.
4.3Exemplo greaterThan() e lessThan()
// busca registros com idade maior que 12
// E menor que 20
$criteria = $this->_dao->criteria()
->greaterThan('age', 12)
->lessThan('age', 20);
- Linha 3: Criação do objeto de critério.
- Linha 4: Adição de um critério de comparação. O campo age deve ser estritamente maior que 12.
- Linha 5: Adição de um critério de comparação. O campo age deve ser estritamente menor que 20.
4.4Exemplo or(), like() e different()
// busca registros cujo email é do Gmail
// OU cujo nome não é o de um criador do Google
$criteria = $this->_dao->criteria('or')
->like('email', '%@gmail.com')
->different('name', ['Sergey', 'Larry']);
- Linha 3: Criação do objeto de critério, especificando que os critérios serão associados por operadores "OR" (e não "AND" como por padrão).
- Linha 4: Adição de um critério de comparação. O campo email deve terminar com a string "@gmail.com".
- Linha 5: Adição de um critério de comparação. O campo name não deve conter o valor "Sergey" ou "Larry".
4.5Exemplo de lógica booleana
// busca registros onde:
// o email é "john@john.com" ou "bob@bob.com",
// E cuja idade é menor ou igual a 12
// OU estritamente maior que 24
$criteria = $this->_dao->criteria()
->equal('email', ['john@john.com', 'bob@bob.com'])
->and(
$this->_dao->criteria('or')
->lessOrEqualTo('age', 12)
->greaterThan('age', 24)
);
- Linha 5: Criação do objeto de critério.
- Linha 6: Adição de um critério de igualdade, fornecendo uma lista de valores.
- Linha 7: Adição de um operador booleano "AND", que contém um subconjunto de critérios.
- Linha 8: Criação do subconjunto de critérios. Especificamos que os diferentes critérios desse conjunto serão ligados por operadores "OR".
- Linha 9: Adição de um critério de comparação. O campo age deve conter um valor menor ou igual a 12.
- Linha 10: Adição de um critério de comparação. O campo age deve ser estritamente maior que 24.
5Critérios de ordenação
O método search() pode retornar vários itens, que você talvez queira ordenar de uma certa forma.
É possível ordenar de 3 maneiras diferentes:
- Fornecendo o nome de um campo, o que realizará uma ordenação crescente sobre os valores desse campo. Se o nome do campo começar com um hífen, será realizada uma ordenação decrescente.
- Transmitindo um array contendo os nomes dos campos sobre os quais a ordenação deve ser feita. Por padrão, a ordenação é crescente (do menor para o maior valor), mas é possível especificar uma ordenação decrescente colocando um hífen na frente do nome do campo. Também é possível usar um array associativo cuja chave é o nome do campo e o valor é a string desc.
- Fornecendo o valor booleano false, para obter uma ordenação aleatória.
// ordenação por data de nascimento, crescente
$sort = 'birthday';
// ordenação por data de nascimento, decrescente
$sort = '-birthday';
// ordenação por data de nascimento (crescente)
// e número de pontos (decrescente)
$sort = ['birthday', '-points'];
// equivalente ao anterior
$sort = [
'birthday',
'points' => 'desc'
];
// equivalente ao anterior
$sort = [
'birthday' => 'asc',
'points' => 'desc'
];
// ordenação aleatória
$sort = false;
6Métodos
6.1count()
count(null|array|\Temma\Dao\Criteria $criteria=null) : int
Se este método for chamado sem parâmetros, ele retorna o número total de elementos na tabela.
Se for chamado com um objeto de critério ou array como parâmetro, ele retorna o número de elementos que correspondem aos critérios.
Exemplo de uso:
// obtém o número total de elementos na tabela
$cnt = $this->_dao->count();
// obtém o número de usuários chamados "Bob" (array)
$cnt = $this->_dao->count(['name' => 'Bob']);
// obtém o número de usuários chamados "Bob" (objeto)
$cnt = $this->_dao->count(
$this->_dao->criteria()->equal('name', 'Bob')
);
6.2get()
get(int|string|array|\Temma\Dao\Criteria $id) : array
Este método retorna todas as informações sobre um registro na tabela, cujo identificador de chave primária é passado como parâmetro. Os dados são retornados em um array associativo, que lista pares cuja chave é o nome do campo.
Um critério de busca pode ser fornecido como parâmetro em vez de uma chave primária. Se esse critério recuperar vários registros, apenas o primeiro será retornado.
Se uma lista de campos foi fornecida na criação do DAO, apenas os campos em questão são retornados. Se essa lista incluía a renomeação dos campos, as chaves do array associativo são modificadas de acordo.
Exemplo de uso:
// obtém as informações do artigo com o identificador 12
$data = $this->_dao->get(12);
// exibe o título
print($data['title']);
6.3search()
search(null|array|\Temma\Dao\Criteria $criteria=null, null|false|string|array $sort=null,
?int $limitOffset=null, ?int $limit=null) : array
Este método é usado para recuperar dados de várias linhas da tabela. Ele retorna uma lista cujo cada elemento é
um array associativo (cujo conteúdo é idêntico ao que o método get() retorna, veja acima).
O primeiro parâmetro é um critério de busca, conforme explicado anteriormente nesta página. Se ele não for fornecido, ou for passado como null,
o método pegará todas as linhas da tabela.
O segundo parâmetro contém as opções de ordenação, conforme explicado anteriormente nesta página.
O terceiro parâmetro pode conter o número do primeiro elemento a retornar (começando em zero).
O quarto parâmetro pode conter o número de elementos a retornar.
Exemplo de uso:
// busca os 5 artigos mais recentes,
// entre todos os escritos desde 1º de janeiro de 2000
$articles = $this->_dao->search(
$this->_dao->criteria()->greaterThan('date', '2011-01-01'),
'-date',
null,
5
);
// exibe os títulos de todos os artigos recuperados
foreach ($articles as $article)
print($article['title']);
// busca 3 artigos aleatórios
$articles = $this->_dao->search(null, false, null, 3);
6.4create()
create(array $data, mixed $safeData=null) : int
Este método adiciona uma nova linha à tabela.
Um array associativo deve ser fornecido como parâmetro, contendo pares chave/valor correspondentes a cada campo da tabela.
O método retorna o identificador de chave primária do item recém-criado.
Exemplo de uso:
// criação do novo artigo
$id = $this->_dao->create([
'title' => 'Título de teste',
'login' => 'Bob',
'date' => date('c'),
'content' => 'Texto...',
]);
// exibe o identificador do novo artigo
print($id);
O método pode receber um segundo parâmetro opcional, usado para evitar deadlocks causados por inserções que geram uma duplicação de chave.
O parâmetro pode receber como valor:
- null: (valor padrão) A consulta gera um erro se houver duplicação de chave.
- true: Todos os campos listados no primeiro parâmetro serão atualizados, usando os valores fornecidos.
- O nome de um campo: Este campo será atualizado. Se esse campo estiver listado no primeiro parâmetro, o valor associado será usado; caso contrário, o campo manterá o valor que possui no banco de dados.
- Um array associativo contendo os campos que serão atualizados. As chaves são os nomes dos campos, e os valores associados serão usados para atualizá-los.
6.5remove()
remove(null|int|string|array|\Temma\Dao\Criteria $criteria=null) : int
Este método é usado para excluir registros da tabela.
Se for chamado sem parâmetros, apaga todos os elementos.
Se um identificador numérico for passado como parâmetro, ele será usado como a chave primária do elemento a ser excluído.
Se um critério de busca for passado como parâmetro, ele é usado para escolher os elementos que serão excluídos.
Este método retorna o número de linhas excluídas.
Exemplo de uso:
// exclui o artigo com o identificador 12
$this->_dao->remove(12);
// exclui todos os artigos escritos por Bob (array)
$this->_dao->remove(['login' => 'Bob']);
// exclui todos os artigos escritos por Bob (objeto)
$this->_dao->remove(
$this->_dao->criteria()->equal('login', 'Bob')
);
6.6update()
update(null|int|string|array|\Temma\Dao\Criteria $criteria, array $data=[],
null|false|string|array $sort=null, ?int $limit=null) : int
Com este método, você pode modificar os dados salvos na tabela.
Se o primeiro parâmetro for definido como null, todos os elementos da tabela serão modificados.
Se um identificador numérico ou uma string for passado como parâmetro, ele será usado como a chave primária do elemento modificado.
Se um critério de busca for passado como parâmetro, ele será usado para selecionar os elementos que serão modificados.
O segundo parâmetro deve conter um array associativo, contendo pares chave/valor correspondentes aos campos a serem atualizados (com uma declaração idêntica à do método create()).
O terceiro parâmetro (opcional) contém as opções de ordenação, conforme explicado anteriormente nesta página. Ele só é útil em conjunto com o quarto parâmetro.
O quarto parâmetro (opcional) é usado para limitar o número de linhas que serão atualizadas. Se definido como null (valor padrão), todas as linhas que correspondem aos critérios serão modificadas.
Este método retorna o número de linhas modificadas.
Exemplo de uso:
// renomeia "Bob" para "Robert" em todos os seus artigos (array)
$this->_dao->update(
['login' => 'Bob'],
['login' => 'Robert']
);
// renomeia "Bob" para "Robert" em todos os seus artigos (objeto)
$this->_dao->update(
$this->_dao->criteria()->equal('login', 'Bob'),
['login' => 'Robert']
);
7Gerenciamento de cache
Usar o cache acelera as leituras no banco de dados. Infelizmente, isso pode ter um efeito indesejável: todas as consultas idênticas retornarão resultados idênticos durante o tempo de cache. Isso pode ser problemático quando você executa consultas que gravam de forma idêntica, mas precisam retornar resultados diferentes (por exemplo, seleções cujos resultados são ordenados aleatoriamente, ou se ocasionalmente você precisar obter dados atualizados).
Portanto, é possível desativar temporariamente o uso do cache usando o método disableCache(). A reativação é feita usando o método enableCache(). Esses dois métodos retornam a instância do seu DAO, a menos que recebam um parâmetro, o qual será então retornado.
Exemplo de uso:
// desativa o cache
$this->_dao->disableCache();
// busca
$result = $this->_dao->search();
// reativa o cache
$this->_dao->enableCache();
// igual ao anterior
$result = $this->_dao->disableCache()->search();
$this->_dao->enableCache();
// resultado idêntico aos anteriores
// ordem de execução:
// 1. disableCache()
// 2. search(), que é escrito como parâmetro de enableCache()
// 3. enableCache()
// no final, o resultado do search() é recuperado,
// porque ele é retornado por enableCache()
$result = $this->_dao
->disableCache()
->enableCache($this->_dao->search());