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
Bernardo Pádua respondeu em 11 Abr 2007 às 16:09 #
O Active record é foda! Não entendi a função do :dependent=>true, o que ele especifica? Sem ele não funcionaria do mesmo jeito?
Bernardo Pádua respondeu em 06 Jun 2007 às 11:40 #
Dois detalhes importantes:
- o :dependent=>true está ‘deprecated’, o correto agora é usar :dependent=>:destroy, que significa que se o ‘pai’ ( o que contem o has_many ou has_one) for destruido o filho também será destruído. Existe também outras opções para :dependent.
- Infelizmente ainda não é possível usar ‘has_many :through’ aninhado, que possibilitaria você navegar facilmente na hierarquia do seu banco de dados. Por exemplo, ainda não é possível:
has_many :memberships
has_many :users, :through=>:memberships
has_many :posts, :through=>:users
Existe um patch para o Rails que implementa isto, mas ainda não foi incorporado nem ao Rails ‘Edge’, que é a versão em desenvolvimento do Rails. Provavelmente até o final do ano já estará incorporado.