HUFERSIL.WEBDEVELOPER - Soluções com qualidade - Hugo Ferreira da Silva

Lumine - Tutorial 5 - Trabalhando com joins Ter, 29 de abril de 2008 - as 20h50 - 2 comentários

Hoje vamos ver como trabalhas com joins no Lumine.
Ele torna o trabalho bem mais fácil e humanamente falando mais claro de se entender.
Caso você não tenha feito, faça as etapas anteriores para se familiarizar com o processo:
Vamos fazer um inner join simples, cruzando as entidades de Categoria e Noticia.

Antes de qualquer consulta que vamos realizar que exije o cruzamento de uma ou mais tabelas, temos que lembrar a seguinte regra: qual é a fonte de dados principal que eu desejo extrair os dados?

Neste primeiro exemplo, nós queremos listar as notícias e o nome da categoria das mesmas. Sendo assim, vamos trabalhar com a entidade de Noticia para recuperar os dados desejados.

Vejamos o primeiro exemplo de código:
<?php

// importa as configurações padrão
require_once 'config.php';

// importa as classes de categoria e noticia
Lumine::import('Categoria','Noticia');

// inicia um objeto de categoria
$categoria = new Categoria();

// inicia um objeto de noticia
$noticia = new Noticia();

////////////////////////////////////////////////////////
// agora, vamos fazer a união
////////////////////////////////////////////////////////

// mudamos o alias de noticia
$noticia->alias('n')
// unimos com categoria, colocando o alias c
->join($categoria,'inner','c')
// selecionamos os campos desejados
->select('n.codnoticia, n.titulo, n.data_cadastro, n.categoria, c.nome as nomecategoria')
// ordenamos pela data de cadatro da notícia decrescente
->order('n.data_cadastro desc')
// efetuamos a consulta
->find();

// para cada resultado encontrado
while( $noticia->fetch() )
{
// mostramos o codigo da noticia, titulo e categoria
echo $noticia->codnoticia . ' - ' .
$noticia->titulo . ' - ' .
$noticia->nomecategoria . '<br>';
}
Salve o arquivo com o nome de join.php

O primeor argumetno da chamada ao join, é um objeto instanciado que extende a classe Lumine_Base, que no nosso caso é $categoria. O segundo argumento (inner), indica que vamos fazer um INNER JOIN, ou seja, toda notícia DEVE ter uma categoria. Caso a notícia não esteja vinculada a uma categoria, o registro não será retornado. E no terceiro paramêtro indicamos qual o alias (apelido) da entidade que vamos usar.

Agora vamos a um outro exemplo: vamos supor que você deseja saber quantas notícias há em cada categoria. Logo, o dado principal que queremos é a categoria. Então, ao invés de usarmos a entidade de Noticia como principal, vamos usar a de Categoria.
Vejamos o exemplo:
// mudamos o alias de categoria 
$categoria->alias('c')
// une com noticias no modo LEFT JOIN
->join($noticia, 'left', 'n')
// seleciona o codigo da categoria, nome e a quantidade de notícias
->select('c.codcategoria, c.nome, COUNT(n.codnoticia) AS total_noticias')
// ordena pelo nome da categoria
->order('c.nome ASC')
// agrupa pelo código da categoria
->group('c.codcategoria')
// efetua a consulta
->find();

// para cada resultado encontrado
while( $categoria->fetch() )
{
// mostramos o codigo da categoria, nome da categoria e quantidade de noticias
echo $categoria->codcategoria . ' - ' .
$categoria->nome . ' - ' .
$categoria->total_noticias . '<br>';
}
Salve o arquivo com o nome de categorias.phpi

Neste segundo exemplo, fizemos duas coisas diferentes: ao invés de usar INNER, usamos LEFT. Isto por que o que nos interessa são as categorias e quantas notícias há em cada uma. Ao contrário do INNER, o argumento LEFT faz com que os resultados da consulta principal (categorias) sejam obtidos mesmo sem a presença dos dados secundários (noticia), isto por que uma categoria poderia ter sido recém cadastrada, não tendo nenhuma notícia vinculada.

Em seguida, por estarmos usando uma função interna do banco (count) que exige de agrupamento de resultados, temos que indicar por qual campo desejamos agrupas os registros encontrados. Como desejamos pegar todas as categorias, o melhor a ser feito é agrupar pela chave-primária, pois ela é única e não pode ser nula. Na entidade Categoria, nossa chave primária é o campo codcategoria.

Tá, mas e se eu tiver que cruzar mais entidades?

A quantidade de entidades que podem ser unidas em uma mesma consulta é ilimitada. Você pode unir várias entidades para realizar uma consulta complexa, caso seja essa sua necessidade.

Vou mostrar um exemplo que utilizei em um site de um cliente:
// recupera a franquia atual do sistema (NÃO FAZ PARTE DO PACOTE DO Lumine)
$franquia = $sis->getFranquia();

// instancia as classes necessárias
$imovel = new Imovel;
$tipo = new Tipo_imovel;
$cidade = new Cidade;
$bairro = new Bairro;
$cliente = new Cliente;
$foto = new Clienteimovelfoto;
$cat = new Categoria();

//////////////////////////////////////////////////
// destaques da esquerda (ALTO PADRAO)
////////////////////////////////////////////////
$imovel->alias('i') // muda o alias de imovel
->join($tipo,'inner','t') // une com o tipo de imovel com alias t
->join($cidade,'inner','c') // une com cidade com alias c
->join($bairro,'inner','b') // une com bairro e alias b
->join($cliente->alias('cl') // une com cliente com alias cl E cliente une com...
->join($franquia,'inner','fr') // ... a franquia que ele pertence com alias fr
)
->join($foto,'left','f') // une com a foto do imovel (LEFT) e alias f

// pega todos os dados de imovel
->selectAs()
// seleciona mais alguns dados
->select('b.nome as nomebairro,t.nome as nometipo,c.nome as nomecidade,f.arquivo, fr.permalink as nomefranquia')
// limpa todas as condições WHERE que existirem (se existirem)
->where()
// somente clientes ativos que não tenham expirado e imóveis que estejam ativos
->where('cl.ativo = 1 and cl.dataexpira >= ? and i.ativo = 1', time())
// somente se a franquia está ativa e que o codigo seja da franquia selecionada
->where('fr.ativo = 1 and fr.idfranquia = ?', $franquia->idfranquia)
// somente imoveis marcados como "alto padrão"
->where('i.altopadrao = 1')
// ordena randomicamente (pega a função do banco)
->order( $imovel->_getConnection()->random() )
// limita em 3 registros
->limit( 3 )
// agrupa pelo id do imovel
->group('i.idimovel')
// efetua a consulta
->find();

Este "pequeno" trecho irá gerar a seguinte consulta:

SELECT i.idimovel as idimovel, i.idbairro as bairro, i.idcidade as cidade, i.idtipo as tipo_imovel, i.idlocador as clientelocador, i.idcliente as cliente, i.nome as nome, i.logradouro as logradouro, i.numero as numero, i.descricao as descricao, i.qtd_quarto as qtd_quarto, i.qtd_suite as qtd_suite, i.qtd_sala as qtd_sala, i.qtd_cozinha as qtd_cozinha, i.terreno_largura as terreno_largura, i.terreno_comprimento as terreno_comprimento, i.area_construida as area_construida, i.venda as venda, i.locacao as locacao, i.ativo as ativo, i.destaque as destaque, i.valor_aluguel as valor_aluguel, i.valor_venda as valor_venda, i.data_cadastro as data_cadastro, i.areautil as areautil, i.areatotal as areatotal, i.idcategoria as categoria, i.altopadrao as altopadrao, b.nome as nomebairro, t.nome as nometipo, c.nome as nomecidade, f.arquivo, fr.permalink as nomefranquia
FROM imo_imovel i
INNER JOIN imo_tipo_imovel t ON t.idtipo = i.idtipo
INNER JOIN imo_cidade c ON c.idcidade = i.idcidade
INNER JOIN imo_bairro b ON b.idbairro = i.idbairro
INNER JOIN imo_cliente cl ON cl.idcliente = i.idcliente
INNER JOIN imo_franquia fr ON fr.idfranquia = cl.idfranquia
LEFT JOIN imo_clienteimovelfoto f ON f.idimovel = i.idimovel
WHERE cl.ativo = 1 and cl.dataexpira >= '2008-04-29' and i.ativo = 1 AND fr.ativo = 1 and fr.idfranquia = 1 AND i.altopadrao = 1
GROUP BY i.idimovel
ORDER BY rand()
LIMIT 3

@braços e fiquem com Deus!

Comentários

Por Hugo enviado em 30 de abril de 2008, as 19:56 Sim, você pode realizar uma query manualmente se você quiser.

Ambos métodos _getLink e getLink têm a mesma funcionalidade. Somente declarei o _getLink para no caso se alguem tiver um método interno também chamado getLink (que por padrão, tem a nomenclatura de métodos getters e setters).

O objetivo do getLink é pegar objetos relacionados ao objeto atual em relação aos mapeamentos declarados. Ele fará join somente se necessário.

O join e o getLink tem conceitos diferentes.

O getLink é para "pegar" dados relacionados ao objeto atual. Ou seja, você já tem um objeto e quer pegar os dados relacionados (por exemplo, carreguei uma pessoa com código 1 e agora quero todos os endereços dela);

O join é para efetuar uma consulta unindo duas ou mais tabelas.
Por Cairo Noleto enviado em 30 de abril de 2008, as 18:00 Olá Hugo, é impressão minha, mas agora o framework da a possibilidade de "escrever sql" em objetos?

Outra coisa, andei dando uma olhada, e o método getLink, foi alterado para _getLink, queria saber se ele ainda tem a mesma funcionalidade da versão anterior?

No caso, eu sempre entendi que o getLink era pra fazer uma busca "utilizando join". Então comecei a utilizar o join pra realmente fazer join, mas notei que se utilizo nomes de mapeamento ou nomes de campos na minha tabela igual, ele sobrepõe os nomes.

Então a minha duvida é que se o join vai substituir o getLink ou continuar os dois juntos.

Abraços!

Cairo Noleto
http://caironoleto.wordpress.com

Deixe seu comentário