mergulhao.info
Por Sylvestre Mergulhão
Sylvestre Mergulhão é programador web e fundador da HE:labs.
Veja também minhas palestras no Speaker Deck e me acompanhe no twitter @smergulhao.
Associações com ActiveRecord
Postado em 18/05/2007
Vou contar um pouco da minha experiência com o ActiveRecord do Rails. Trata-se do has and belongs to many - hbtm - e do has many through, inserido no Rails 1.1.
As associações hbtm são resultado de relações muitos para muitos - ou n para n - entre duas tabelas do banco de dados. Por exemplo, imagine um livro. Esse livro pode ser enquadrado em diversas categorias simultaneamente. Reciprocamente, uma categoria pode possuir diversos livros enquadrados. Nesse caso teríamos duas classes de modelo:
class Book < ActiveRecord::Base
has_and_belongs_to_many :categories
end
e
class Category < ActiveRecord::Base
has_and_belongs_to_many :books
end
E temos o modelo de dados:
p{text-align: center}.
A tabela books_categories não precisa de modelo. Ela é mapeada automaticamente pelo has_and_belongs_to_many. Para adicionar uma categoria a um livro:
book.categories << Category.find(1)
book.save
E para recuperar todos os livros de uma categoria pode-se fazer:
books = Category.find(1).books
Dentro da variável books estarão todos os livros(um array de livros) que pertencem a categoria com id igual a 1. O mesmo vale no sentido contrário:
categories = Book.find(1).categories
A variável categories será um array das categorias a qual o livro com id igual a 1 pertence. Simples não?
Mas e se o relacionamento em si possuir atributos também? No nosso caso, isso significa a tabela books_categories possuir atributos que pertencem ao relacionamento, e não a categoria ou ao livro isoladamente. Esse caso não é tratado pelo has and belongs to many. É aí que entra o has many through.
Alterando o modelo de dados para:
p{text-align: center}.
Temos agora a substituição da tabela books_categories pela tabela comments, que provê as mesmas informações da anterior e mais um comentário. Então agora cada livro possui um comentário que por sua vez está associado a uma categoria. Esse comentário poderia ser usado, por exemplo, para explicar por que a pessoa que cadastrou o livro incluiu-o naquela categoria. Os modelos ficam agora:
class Book < ActiveRecord::Base
has_many :comments
has_many :categories, :through => :comments
end
class Comment < ActiveRecord::Base
belongs_to :book
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :books, :through => :comments
end
Agora podemos fazer coisas como:
category = Category.find(1)
comment = Comment.new :content => "meu comentario", :category => category
book.comments << comment
book.save
Ou apenas:
book.categories << Category.find(1)
E o comentário virá em branco. Pode-se também:
books = Category.find(1).books
Que retornará todos os livros pertencentes a uma categoria. Podemos também:
categories = Books.find(1).categories
Onde teremos todas as categorias de um livro. Além disso:
comments = Books.find(1).comments
Onde teremos todos os comentários de um livro. Lembrando que cada objeto comment ainda possui a categoria associada a ele.
Para testar as coisas e garantir que o código que eu escrevi aqui estava ok, no fim eu resolvi testá-lo. E usei migração para geração da base de dados, como forma fácil de migrar do primeiro modelo para o segundo. Então esse poderá ser o tema do próximo post.
Por favor, se encontrar qualquer erro nas informações ou no código, não exite em informar nos comentários para que eu possa corrigir.