Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

has_many :through, :source, :source_type returning empty array

I got some Manager and SoccerTeam model. A manager "owns" many soccer teams; also a manager can comment on soccer teams and can comment on other managers too:

manager.rb

# Soccer teams the manager owns
has_many :soccer_teams, :dependent => :restrict
# Comments the manager has made on soccer teams or other managers
has_many :reviews, :class_name => "Comment", :foreign_key => :author_id, :dependent => :destroy
# Comments the manager has received by other managers
has_many :comments, :as => :commentable, :dependent => :destroy
# Soccer teams that have received a comment by the manager
has_many :observed_teams, :through => :comments, :source => :commentable, :source_type => "SoccerTeam"

soccer_team.rb

# The manager that owns the team
belongs_to :manager
# Comments received by managers
has_many :comments, :as => :commentable, :dependent => :destroy
# Managers that have reviewed the team
has_many :observers, :through => :comments, :source => :author, :class_name => "Manager"

comment.rb

belongs_to :commentable, :polymorphic => true
belongs_to :author, :class_name => Manager

Now, if I have a Manager commenting on a SoccerTeam I expect to find:

  • A Comment object in manager.reviews and in soccer_team.comments
  • A SoccerTeam object in manager.observed_teams
  • A Manager object in soccer_team.observers

While everything works well for the first and the third point, when I call manager.observed_teams I always obtain an empty array. To actually obtain the list of soccer teams a manager has commented on I need to use:

manager.reviews.collect{ |review| Kernel.const_get(review.commentable_type).find(review.commentable_id) if review.commentable_type == "SoccerTeam" }

Which is ugly. I expect the simple manager.observed_teams to work… why it doesn't?

Edit

I went a step further in understanding why it does not work. In fact, the genrated SQL is:

SELECT "soccer_teams".* FROM "soccer_teams" INNER JOIN "comments" ON "soccer_teams".id = "soccer_teams".commentable_id AND "comments".commentable_type = 'SoccerTeam' WHERE (("comments".commentable_id = 1) AND ("comments".commentable_type = 'Manager'))

While I want it to be:

SELECT "soccer_teams".* FROM "soccer_teams" INNER JOIN "comments" ON "soccer_teams".id = "comments".commentable_id AND "comments".commentable_type = 'SoccerTeam' WHERE ("comments".author_id = 1)

So the question is simple: how to obtain that query? (An heuristic attempt with :foreign_key ans :as, as expected, hasn't solved the problem!).

like image 652
Alberto Santini Avatar asked Feb 19 '11 18:02

Alberto Santini


1 Answers

I think you've simply used the wrong association for observed_teams. Instead of

has_many :observed_teams, :through => :comments, 
  :source => :commentable, :source_type => "SoccerTeam"

Try this:

has_many :observed_teams, :through => :reviews,
 :source => :commentable, :source_type => "SoccerTeam"

Also in,

has_many :reviews, :class_name => :comment, 
  :foreign_key => :author_id, :dependent => :destroy

:comment should be 'Comment'

and in

has_many :comments, :as => commentable, :dependent => :destroy

commmentable should be :commmentable

like image 52
zetetic Avatar answered Nov 06 '22 09:11

zetetic