Cor Cinza

A um tempo atras escrevi este artigo que falava sobre como filtrar uma consulta com associações HABTM no CakePHP. O artigo já tem mais de um ano e muita coisa já mudou.

Na verdade, na época a coisa já havia mudado, mas eu não sabia. Foi quando eu encontrei este outro artigo aqui. O artigo ensina como fazer joins em casos específicos utilizando o Model::find().

Vou tentar parafrasear o artigo utilizando a motivação daquele meu artigo anterior.

Então imagine que temos duas tabelas, uma de Posts e outra de Tags e exista uma relação HABTM entre elas. Eu quero encontrar todos os posts com a Tag “design”, como fazer?

Uma vez dentro do controller dos posts eu posso escrever o seguinte código:

Obs.: Este código não foi testado e seu objetivo é apenas o de ilustrar as possibilidades aqui apresentadas.

$posts = $this->Post->find('all', array(
	'joins' => array(
		array(
			'table'      => 'posts_tags',
			'alias'      => 'PostsTag',
			'type'       => 'INNER',
			'conditions' => array('PostsTag.post_id = Post.id')
		),
		array(
			'table'      => 'tags',
			'alias'      => 'Tag',
			'type'       => 'INNER',
			'conditions' => array(
				'Tag.id = PostsTag.tag_id',
				'Tag.tag LIKE' => '%design%'
			)
		)
	)
));

Trazendo um caso da vida real, na Quitanda eu queria mostrar os últimos posts de uma determinada categoria dentro do painel dos usuários com lojas. O nosso blog é feito com Wordpress. Eu teria que criar um arquivo model para a tabela de posts do WP para poder fazer isso, mas fiquei com preguiça já que este seria o único lugar onde isto aconteceria, então ao invés de criar um arquivo de modelo, eu criei ele dentro do próprio controller utilizando a classe ClassRegistry e a função init.

Como minha tabela do Wordpress era diferente da utilizada pela aplicação, primeiro eu defini um novo vetor de configurações no meu arquivo config/database.php. Veja:

var $wp = array(
	'driver'     => 'mysql',
	'persistent' => false,
	'host'       => 'localhost',
	'login'      => 'root',
	'password'   => 'max7max7',
	'database'   => 'qolwp',
	'encoding'   => 'utf8'
);

Depois, já no meu controller, escrevi a seguinte linha:

$this->WpPost = ClassRegistry::init(array(
	'ds'    => 'wp',
	'table' => 'wp_posts',
	'class' => 'model',
	'alias' => 'WpPost'
));

Os parâmetros praticamente se auto explicam mas, a chave ‘ds’ guarda a variável de configuração do banco que vai ser utilizada, no caso ‘wp’, como definido anteriormente. Depois dizemos qual é a tabela a ser utilizada, o tipo de classe a ser registrada e o ‘alias’ para ser utilizado nas consultas.

Depois eu fiz a consulta. Ela ficou meio extensa e é claro que escrevendo isto direto no SQL seria muito mais simples, mas fica aqui a titulo de exemplo. Eu fiz isso mais para experimentar as possibilidades e ver o que acontecia. Olha só:

$posts = $this->WpPost->find('all', array(
        'joins' => array(
                array(
                        'table'      => 'wp_term_relationships',
                        'alias'      => 'WpTermRelationship',
                        'type'       => 'INNER',
                        'conditions' => array('WpPost.ID = WpTermRelationship.object_id')
                ),
                array(
                        'table'      => 'wp_term_taxonomy',
                        'alias'      => 'WpTermTaxonomy',
                        'type'       => 'INNER',
                        'conditions' => array('WpTermRelationship.term_taxonomy_id = WpTermTaxonomy.term_taxonomy_id')
                ),
                array(
                        'table'      => 'wp_terms',
                        'alias'      => 'WpTerm',
                        'type'       => 'INNER',
                        'conditions' => array('WpTermTaxonomy.term_id = WpTerm.term_id')
                )
        ),
        'conditions' => array(
                'WpPost.post_type'        => 'post',
                'WpPost.post_status'      => 'publish',
                'WpTermTaxonomy.taxonomy' => 'category',
                'WpTerm.slug'             => 'painel'
        ),
        'fields' => array(
                'WpPost.post_title',
                'WpPost.post_content',
                'WpPost.guid',
                'WpPost.post_date'
        ),
        'group' => 'WpPost.ID',
        'order' => 'WpPost.post_date DESC',
        'limit' => 5
));

O SQL que esta função vai criar fica assim:

SELECT `WpPost`.`post_title`, `WpPost`.`post_content`, `WpPost`.`guid`, `WpPost`.`post_date` FROM `wp_posts` AS `WpPost` INNER JOIN wp_term_relationships AS `WpTermRelationship` ON (`WpPost`.`ID` = `WpTermRelationship`.`object_id`) INNER JOIN wp_term_taxonomy AS `WpTermTaxonomy` ON (`WpTermRelationship`.`term_taxonomy_id` = `WpTermTaxonomy`.`term_taxonomy_id`) INNER JOIN wp_terms AS `WpTerm` ON (`WpTermTaxonomy`.`term_id` = `WpTerm`.`term_id`) WHERE `WpPost`.`post_type` = 'post' AND `WpPost`.`post_status` = 'publish' AND `WpTermTaxonomy`.`taxonomy` = 'category' AND `WpTerm`.`slug` = 'painel' GROUP BY `WpPost`.`ID` ORDER BY `WpPost`.`post_date` DESC LIMIT 5

Com isto você já pode ver que existem muitas possibilidades. Por exemplo, você pode fazer consultas com LEFT JOIN ao invés de INNER JOIN simplesmente trocando a chave ‘type’. Veja também que nas condições gerais da minha consulta eu pude colocar filtros diretamente relacionados às tabelas que eu dei join. No artigo original existe uma chave ‘foreignKey’ com valor false, mas analisando a API, não achei nenhuma referência a este valor em nenhum dos adaptadores de bancos de dados, então me parece ser algo totalmente dispensavel e a função funciona sem que ela seja declarada.

Acho que é isso. Duvidas, sugestões e agradecimentos são sempre bem vindos! Até a próxima!

Tags: , ,

Comentários /comments

  • Lucas Renan
    Lucas Renan escreveu:
    Postado em July 23, 2009 às 11:57 am

    Eber,

    valeu pelo post, agora eu entendi como fazer, ficou mais simples que a gambi que eu estava fazendo. huauhhuahu
    E parabéns pela evangelização ao cakephp.

    abs.

  • Edluise Costa
    Edluise Costa escreveu:
    Postado em October 14, 2009 às 5:02 am

    Parabéns pelo post.

    Edluise Costa
    http://www.ecadti.com.br

  • Éber
    Éber escreveu:
    Postado em October 14, 2009 às 6:06 am

    Obrigado :)

Comente! /post a comment!