Question

Est-il possible d'avoir plusieurs relations has_many :through qui passent par l'autre dans Rails? J'ai reçu la suggestion de le faire comme une solution pour une autre question que j'ai posté, mais ont été incapables de le faire fonctionner.

Les amis sont une association cyclique à travers une table de jointure. L'objectif est de créer un has_many :through pour friends_comments, donc je peux prendre un User et faire quelque chose comme user.friends_comments pour obtenir tous les commentaires de ses amis dans une seule requête.

class User
  has_many :friendships
  has_many :friends, 
           :through => :friendships,
           :conditions => "status = #{Friendship::FULL}"
  has_many :comments
  has_many :friends_comments, :through => :friends, :source => :comments
end

class Friendship < ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
end

Cela semble grande et logique, mais ne fonctionne pas pour moi. Ceci est l'erreur que je reçois dans la partie pertinente lorsque je tente d'accéder aux friends_comments d'un utilisateur:
ERROR: column users.user_id does not exist
: SELECT "comments".* FROM "comments" INNER JOIN "users" ON "comments".user_id = "users".id WHERE (("users".user_id = 1) AND ((status = 2)))

Quand je rentre juste user.friends, qui fonctionne, c'est la requête qu'il exécute:
: SELECT "users".* FROM "users" INNER JOIN "friendships" ON "users".id = "friendships".friend_id WHERE (("friendships".user_id = 1) AND ((status = 2)))

Il semble donc que c'est tout à fait oublier la has_many originale à travers une relation d'amitié, et est ensuite essayer d'utiliser de façon inappropriée la classe utilisateur comme une table de jointure.

Ai-je fait quelque chose de mal, ou est-ce tout simplement pas possible?

Était-ce utile?

La solution

Modifier

Rails 3.1 prend en charge les associations imbriquées. Par exemple:

has_many :tasks
has_many :assigments, :through => :tasks
has_many :users, :through => :assignments

Il n'y a pas besoin de la solution ci-dessous. Reportez-vous à ce screencast pour plus de détails.

Réponse originale

Vous passez une association de has_many :through comme source pour une autre has_many :through association. Je ne pense pas que cela fonctionnera.

  has_many :friends, 
           :through => :friendships,
           :conditions => "status = #{Friendship::FULL}"
  has_many :friends_comments, :through => :friends, :source => :comments

Vous avez trois approches pour résoudre ce problème.

1) une extension d'association

 has_many  :friends, 
           :through => :friendships,
           :conditions => "status = #{Friendship::FULL}" do
     def comments(reload=false)
       @comments = nil if reload 
       @comments ||=Comment.find_all_by_user_id(map(&:id))
     end
 end

Maintenant, vous pouvez obtenir des commentaires les amis comme suit:

user.friends.comments

2) Ajouter une méthode à la classe User.

  def friends_comments(reload=false)
    @friends_comments = nil if reload 
    @friends_comments ||=Comment.find_all_by_user_id(self.friend_ids)
  end

Maintenant, vous pouvez obtenir des commentaires les amis comme suit:

user.friends_comments

3) Si vous voulez que ce soit alors encore plus efficace:

  def friends_comments(reload=false)
    @friends_comments = nil if reload 
    @friends_comments ||=Comment.all( 
             :joins => "JOIN (SELECT friend_id AS user_id 
                              FROM   friendships 
                              WHERE  user_id = #{self.id}
                        ) AS friends ON comments.user_id = friends.user_id")
  end

Maintenant, vous pouvez obtenir des commentaires les amis comme suit:

user.friends_comments

Toutes les méthodes cache les résultats. Si vous voulez recharger les résultats procédez comme suit:

user.friends_comments(true)
user.friends.comments(true)

Ou mieux encore:

user.friends_comments(:reload)
user.friends.comments(:reload)

Autres conseils

Il y a un plugin qui permet de résoudre votre problème, jetez un oeil à ce blog .

Vous installez le plug-in avec

script/plugin install git://github.com/ianwhite/nested_has_many_through.git

Bien que cela ne fonctionne pas dans le passé, il fonctionne très bien dans Rails 3.1 maintenant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top