26 de setembro de 2012  //  6 Comentários  //  Categorias: Artigos, CakePHP, Tutoriais

Pesquisando em relações HABTM no CakePHP



Recebo muitas dúvidas por e-mail perguntando como fazer o plugin Filter Results pesquisar em relações HABTM. Então, estou reciclando este post para mostrar como fazer isso de forma simples e sem mistérios, sem ou com o plugin. =)

O manual do CakePHP – Cookbook – tem as principais explicações para trabalhar com qualquer tipo de consulta. Portanto, não vou explorar aqui todas elas, mas sim as que o ele não contempla. Se você utiliza o CakePHP a pouco tempo, recomendo a leitura do mesmo.

Situação

Para nossos exemplos vamos supor o seguinte relacionamento (utilizando os convenções do CakePHP):

Ou seja, um Post pode ter muitas Categorias e vice-versa.
Temos então no banco de dados 3 tabelas:

Problema

Vamos supor a seguinte situação: Desejamos selecionar todos os Posts de uma Category específica.

De uma forma ou de outra você conseguirá fazer isso. Porém, pode não ser a maneira correta. Por exemplo, se você utilizar o Containable, provavelmente o cake irá fazer 2 ou mais selects no banco de dados e depois fazer um “merge” nos resultados.

Sendo direto ao ponto, o problema de utilizar HABTM são os excessos de consultas realizadas pelo CakePHP no banco de dados. Então, veremos algumas maneiras de obter uma query equivalente a esta:

Soluções

Para conseguirmos a query que desejamos, precisamos que o model Post tenha relacionamentos/associações hasOne para as tabelas: categories_posts e categories.

Por convenção, sempre que o CakePHP for juntar uma tabela com nomes compostos, apenas o último nome fica no singular. Por exemplo:

categories_posts -> CategoriesPost
pedidos_produtos -> PedidosProduto
itens_pedidos_produtos -> ItensPedidosProduto
grupos_usuarios_permissoes -> GruposUsuariosPermissao

1. Joins

Utilizar Joins é a primeira coisa que você deve aprender no CakePHP. Ele te salva em várias ocasiões, porém perde pela falta da versatilidade comparado com os outros recursos que ainda mostrarei.

Utilizando-o, nosso código ficará assim:

Resultado:

2. BindModel

Utilizando o BindModel, vamos criar quantas associações dinâmicas forem necessárias para a nossa pesquisa:

Resultado:

3. Containable

Esta é a melhor solução. Este behaviour mudou completamente a minha maneira de montar as associações das tabelas no CakePHP.

Antes de tudo, você deve habilitar o behaviros Containable no model desejado ou no app_model:

Basicamente, vamos criar “hacks” de associações nos models desejados e chama-las em nossas consultas. Portanto, vamos acrescentar estas associações hasOne em nosso model Post.

Agora com os “hacks” devidamente criados no model, vamos fazer nossa pesquisa de forma muito simples:

Resultado:

Conclusão

Claro que os exemplos aqui citados foram simplórios, apenas para você ter uma ideia de como e qual solução utilizar. Entretanto, se conselho fosse bom se vendia, porém, insisto que você utilize sempre containable.

Você pode conforme for criando as associações HABTM da sua aplicação, criar também os “hacks” hasOne necessários. Além disso, pode também configurar o Recursive de todos os models como -1 e eliminar todas as consultas indesejadas realizadas pelo CakePHP de forma “automágica”.

Qualquer dúvida é só deixar um comentário. ^^’
Até!

Quer copiar esse post no seu site? Você pode!

Segundo a licença da Creative Commons 3.0 (CC BY SA 3.0) você pode copiar e distribuir esse conteúdo desde que faça menção ao autor original, para isso é só copiar esse código no final do artigo quando for publicá-lo em seu site:

<p>Artigo originalmente publicado em <em>26 de setembro de 2012</em> por <strong><a href="http://pedroelsner.com/" title="Pedro Elsner, Profissional de TI - São Paulo">Pedro Elsner</a></strong>: <a href="http://pedroelsner.com/2012/09/pesquisando-em-associacoes-habtm-no-cakephp/" title="Pesquisando em relações HABTM no CakePHP">Pesquisando em relações HABTM no CakePHP</a></p>
A não menção ao autor original da obra implicará em cópia e/ou distribuição ilegal de propriedade intelectual, o que é crime segundo a Lei n.º 9.610.
  • Manoel

    isso que chamo de um post salva vidas rs =)

  • Manoel

    Muito bom o post! Agora estou com um problema aqui que não consigo resolver sobre o hasAndBelongsToMany. Tenho uma tabela Inscricoes (id, contato_id), Contatos(id, nome, estado_id, municipio_id), Categorias(id, categoria), contatos_categorias(id_contato, id_categoria). Preciso fazer um find em Inscricoes que me traga os dados até o nome das categorias da pessoa. Como faço? O find em contatos me traz um array que me diz as categorias, mas um find em inscricoes só vai até as informacoes do contato e nao traz a lista de categorias.

  • Pedro Escobar

    Pedro,
    parabéns pelo post, estou começando no Cake e achei muito interessante. Porém estou com um probleminha, ainda não entendi como adiciono um filter (select), relacionando uma tabela com joins nessa relação hasAndBelongsToMany. Obrigado

  • Thanksssssssssssss!

  • salvou nossas vidas, parabéns pelo artigo.
    Eu apanhava demais pra executar finds() em relações HABTM, depois disso minha vida mudou….rs

  • Que bom =] Abraços