Associações muitos-para-muitos requerem a criação de uma tabela intermediária para poderem ser implementadas. Tomemos como exemplo as entidades USERS e GROUPS.

USERS(id, nome_completo, email)
GROUPS(id, nome, descricao)
GROUP_USERS(id_usuario, id_grupo)

Um usuario pode participar de vários grupos e um grupo pode conter vários usuários. Para registrar isso no BD, a tabela GROUP_USERS deve ser criada.

No rails, para implementar essa relação, as classes model ficariam assim:

____________________________________

class User < ActiveRecord::Base
has_and_belongs_to_many :groups
end

class Groups < ActiveRecord::Base
has_and_belongs_to_many :users
end

____________________________________

Como seguimos a convenção do Rails para o nome da tabela que associa usuarios a grupos, o Rails já entende que a tabela GROUP_USERS tem essa função.

Isso funciona bem no Rails para associações muitos-para-muitos em que somente se deseja armazenar a associação. E se quiséssemos armazenar a data que o usuários entrou em um grupo ?

Supondo as tabelas no BD

USERS(id, nome_completo, email)
GROUPS(id, nome, descricao)
MEMBERSHIPS(id_usuario, id_grupo, data_entrada)

Faríamos da seguinte maneira no Rails:

____________________________________

class Membership < ActiveRecord::Base
belongs_to :groups
belongs_to :users
end

class User < ActiveRecord::Base
has_many :memberships, :dependent => true
end

class Group < ActiveRecord::Base
has_many :memberships, :dependent => true
end

____________________________________

Legal, agora se quisermos, por exemplo, acessar os usuários de um grupo, faríamos:

@users= []
Group.find_by_name(”ITA”).memberships.each do |s|
@users << s.user
end

Funciona!!! Mas a partir do Rails 1.1 foi introduzido uma maneira mais elegante, utilizando Through Associations.

____________________________________

class Membership < ActiveRecord::Base
belongs_to :groups
belongs_to :users
end

class User < ActiveRecord::Base
has_many :memberships, :dependent => true
has_many :groups, :through => :memberships
end

class Group < ActiveRecord::Base
has_many :memberships, :dependent => true
has_many :users, :through => :memberships
end

____________________________________

Agora, para acessar os usuários de um grupo, escreveríamos:

@users = Group.find_by_name(”ITA”).users

Bem mais simples e fácil de entender!

Abaixo estão algumas referências sobre o assunto.

http://wiki.rubyonrails.org/rails/pages/ThroughAssociations

http://www.infused.org/2005/12/06/has-many-through-association/

http://blog.hasmanythrough.com/2006/04/20/many-to-many-dance-off