Relacionamento muitos-para-muitos com ActiveRecord no Rails - Nando Vieira
Relacionamento muitos-para-muitos com ActiveRecord no Rails
Esta semana recebi dois e-mails com a mesma dúvida: como funciona o relacionamento muitos-para-muitos no ActiveRecord. Aparentemente, este é um assunto que muitas pessoas tem dúvidas, mas cuja resposta é bastante simples, até. Você pode fazer tal relacionamento de duas maneiras diferentes, como veremos neste artigo.
Utilizando o método hasandbelongstomany
O método hasandbelongstomany possui uma convenção para se nomear a tabela que persistirá os relacionamentos. Você deve colocar ambas as tabelas relacionadas no nome da tabela de relacionamentos, ordenados alfabeticamente e separados por underscore. Se você tem as tabelas posts e categories, o nome de sua tabela de relacionamentos será categories_posts. Os campos também possuem uma convenção, que é colocar o nome da tabela no singular, adicionando o sufixo _id. Neste caso, teríamos um arquivo de migração como este:
class AddPostsAndCategories < ActiveRecord::Migration
create_table :categories_posts do |t|
t.references :category, :post
drop_table :categories_posts
Para relacionar ambos os modelos, precisaríamos colocar o seguinte método no modelo Post:
Já no modelo Category, o relacionamento seria o seguinte:
Muito simples! Apenas com estas poucas linhas você já pode usar diversos métodos, adicionados pelo próprio ActiveRecord.
# creating relationship through post
post.categories << category
# creating relationship through category
# getting all posts
categories.posts.each do |post|
# getting all categories from a post
post.categories.each do |cat|
puts cat.title
# getting all the post ids
category.post_ids
O método has_and_belongs_to_many possui uma grande desvantagem. Você não pode adicionar campos extras aos relacionamentos. No entanto, o método has_many permite que você faça isso.
Ao contrário do método has_and_belongs_to_many, você pode ter quantos campos adicionais precisar se fizer o relacionamento com o método has_many. Isso é extremamente útil e bastante utilizado por plugins que lidam com ActiveRecord. A convenção para este tipo de relacionamento é um pouco diferente. Sua tabela pode ter um nome específico que identifica o tipo de relacionamento que está sendo feito, já que ele terá um modelo próprio, que fará o papel intermediário entre as tabelas relacionadas.
Crie um modelo chamado Categorization. Ele deve ter os seguintes campos:
t.references :post, :category
Agora, você precisa criar relacionamentos entre este modelo e os demais (Post e Category). No seu modelo Categorization, adicione os seguintes relacionamentos:
E em cada um dos outros modelos faça o relacionamento contrário, utilizando o método has_many.
E aqui entra realmente a parte importante: você irá usar um relacionamento chamado de has_many through:, que fará uma consulta SQL, unindo os resultados em uma única consulta. No seu modelo Post, adicione mais um relacionamento.
Faça a mesma coisa no modelo Category.
Pronto! Seu relacionamento já foi criado. Para testar, faça algo como isto usando o console.
# get post categories
post.categories.map(&:title).to_sentence
# get posts by categories
category.posts.map(&:title).to_sentence
Para criar uma nova associação, você precisa fazer isso através do relacionamento categorizations; caso contrário, você receberá um erro dizendo que não possui um campo para o id.
post.categorizations.create(category: category)
# through category
category.categorizations.create(post: post)
É isso! Se você tem alguma dúvida, envie um e-mail. Se for um assunto que me interessa, ou que interessa a mais pessoas, eu posso escrever algo aqui!