3.7.6.5 hasAndBelongsToMany (HABTM)

Tudo bem. Neste ponto, você já pode chamar você mesmo de profissional em associações de modelos no CakePHP. Você já está bem versado(experiente) nas três associações que ocupam a maior parte das relações entre objetos.

Vamos resolver o último tipo de relacionamento: hasAndBelongsToMany, ou HABTM. Esta associação é usada quando você tem dois modelos que precisam se juntar, várias vezes, muitas vezes, de muitas formas diferentes.

A principal diferença entre hasMany e HABTM é que a ligação entre os modelos em HABTM não é exclusiva. Por exemplo, estamos prestes a juntar nosso modelo Recipe(Receita) com o modelo Tag usando HABTM. Anexando a tag “Italian” a minha receita “grandma's Gnocci” não “esgota” o registro do Tag. Também posso marcar o meu churrasco Honey Glazed Spaghettio's com a tag “Italian” se eu quiser.

Ligações entre objetos associados com hasMany são exclusivos. Se User hasMany Comment, um comentário é apenas ligado a um usuário específico. Deixou-se para ganhar.

Avancemos. É preciso criar uma tabela extra no banco de dados para manipular associações HABTM. Esta nova tabela deve juntar o nome das tabelas associadas, incluindo os nomes de ambos os modelos envolvidos, em ordem alfabética. O conteúdo da tabela deve ser de pelo menos dois campos, cada um com uma chave estrangeira (que devem ser inteiros) apontando para ambas as chaves primárias dos modelos envolvidos.

HABTM: Requer uma tabela separada que inclue os dois nomes dos modelos.

Nota: O nome da tabela deve estar em ordem alfabética.

Recipe HABTM Tag  => recipes_tags.recipe_id, recipes_tags.tag_id
Cake HABTM Fan    => cakes_fans.cake_id, cakes_fans.fan_id
Foo HABTM Bar     => bars_foos.foo_id, bars_foos.bar_id
  1. HABTM: Requer uma tabela separada que inclue os dois nomes dos modelos.
  2. Nota: O nome da tabela deve estar em ordem alfabética.
  3. Recipe HABTM Tag => recipes_tags.recipe_id, recipes_tags.tag_id
  4. Cake HABTM Fan => cakes_fans.cake_id, cakes_fans.fan_id
  5. Foo HABTM Bar => bars_foos.foo_id, bars_foos.bar_id

Uma vez que esta nova tabela foi criada, podemos definir a associação HABTM nos arquivos dos modelos. Estamos indo saltar diretamente a sintaxe array desta vez:

<?php

class Recipe extends AppModel {
    var $name = 'Recipe';   
    var $hasAndBelongsToMany = array(
        'Tag' =>
            array('className'            => 'Tag',
                'joinTable'              => 'posts_tags',
                'foreignKey'             => 'recipe_id',
                'associationForeignKey'  => 'tag_id',
                'conditions'             => '',
                'order'                  => '',
                'limit'                  => '',
                'uniq'                   => true,
                'finderQuery'              => '',
                'deleteQuery'            => '',
                'insertQuery'             => ''
            )
        );             
}
?>
  1. <?php
  2. class Recipe extends AppModel {
  3. var $name = 'Recipe';
  4. var $hasAndBelongsToMany = array(
  5. 'Tag' =>
  6. array('className' => 'Tag',
  7. 'joinTable' => 'posts_tags',
  8. 'foreignKey' => 'recipe_id',
  9. 'associationForeignKey' => 'tag_id',
  10. 'conditions' => '',
  11. 'order' => '',
  12. 'limit' => '',
  13. 'uniq' => true,
  14. 'finderQuery' => '',
  15. 'deleteQuery' => '',
  16. 'insertQuery' => ''
  17. )
  18. );
  19. }
  20. ?>

As possíveis chaves do array para associações hasAndBelongsToMany são:

  • className: o nome da classe do modelo a ser associado ao modelo atual. Se você estiver definindo um relacionamento “Recipe HABTM Tag” , a chave className deve ser igual “Tag”.
  • joinTable: O nome da tabela usada para ingressar nesta associação (se a atual tabela usada para fazer esse tipo de relacionamento não aderir à convenção citada acima para nomear tabelas HABTM).
  • foreignKey: O nome da chave estrangeira encontrada no outro modelo. Isto é especialmente útil se você precisa definir múltiplos relacionamentos HABTM. O valor padrão para esta chave é o nome do outro modelo no singular, seguida de “_id”.
  • associationForeignKey: O nome da chave estrangeira encontrada no modelo atual. Isto é especialmente útil se você precisa definir múltiplos relacionamentos HABTM. O valor padrão para esta chave é o nome do atual modelo no singular, seguida de '_id'.
  • conditions: Um fragmento SQL utilizado para filtrar registros no modelo relacionado. É uma boa prática a utilização nome do modelo nos fragmentos SQL: “Recipe.status = 1” é sempre melhor do que apenas “status = 1”.
  • fields: A lista de campos a serem recuperados quando os dados do modelo associado são coletados. Retorna todos os campos por padrão.
  • order: Um fragmento SQL que define a classificação para a ordem para o retorno de linhas associadas.
  • limit: O número máximo de linhas associadas que você quer que retorne.
  • offset: O número de linhas associadas para saltar sobre (dadas as atuais condições e ordem), antes de ir buscar e associar.
  • finderQuery, deleteQuery, insertQuery: Uma completa consulta SQL CakePHP que pode-se usar para buscar registros associados. Isto deve ser utilizado em situações que exijam muito resultado personalizado.

Uma vez que esta associação tenha sido definida, operações de busca sobre o modelo Recipe(Receita) também vai buscar registros relacionados em Tag caso existam:

// Exemplo de resultados de uma chamada $this->Recipe->find();

Array
(  
    [Recipe] => Array
        (
            [id] => 2745
            [name] => Chocolate Frosted Sugar Bombs
            [created] => 2007-05-01 10:31:01
            [user_id] => 2346
        )
    [Tag] => Array
        (
            [0] => Array
                (
                    [id] => 123
                    [name] => Breakfast
                )
           [1] => Array
                (
                    [id] => 123
                    [name] => Dessert
                )
           [2] => Array
                (
                    [id] => 123
                    [name] => Heart Disease
                )
        )
)
  1. // Exemplo de resultados de uma chamada $this->Recipe->find();
  2. Array
  3. (
  4. [Recipe] => Array
  5. (
  6. [id] => 2745
  7. [name] => Chocolate Frosted Sugar Bombs
  8. [created] => 2007-05-01 10:31:01
  9. [user_id] => 2346
  10. )
  11. [Tag] => Array
  12. (
  13. [0] => Array
  14. (
  15. [id] => 123
  16. [name] => Breakfast
  17. )
  18. [1] => Array
  19. (
  20. [id] => 123
  21. [name] => Dessert
  22. )
  23. [2] => Array
  24. (
  25. [id] => 123
  26. [name] => Heart Disease
  27. )
  28. )
  29. )

Lembre-se de definir uma associação HABTM no modelo Tag se quiser buscar dados do modelo Recipe, quando se utiliza o modelo Tag para pesquisas.