Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reverse has_many with polymorphism

I have two models: Users and Projects. The idea is that Users can follow both projects AND other users. Naturally, Users and Projects are part of a polymorphic "followable" type. Now, using the user model, I'd like to get three things:

user.followed_users
user.followed_projects
user.followers

The first two work fine; It's the third that I'm having trouble with. This is sort of a reverse lookup where the foreign key becomes the "followable_id" column in the follows table, but no matter how I model it, I can't get the query to run correctly.

User Model

has_many :follows, :dependent => :destroy
has_many :followed_projects, :through => :follows, :source => :followable, :source_type => "Project"
has_many :followed_users, :through => :follows, :source => :followable, :source_type => "User"
has_many :followers, :through => :follows, :as => :followable, :foreign_key => "followable", :source => :user, :class_name => "User"

Follow Model

class Follow < ActiveRecord::Base
  belongs_to :followable, :polymorphic => true
  belongs_to :user
end

My follows table has: user_id followable_id followable_type

Whenever I run the query I get:

SELECT `users`.* FROM `users` INNER JOIN `follows` ON `users`.`id` = `follows`.`user_id` WHERE `follows`.`user_id` = 7

where it should be "followable_id = 7 AND followable_type = 'User", not "user_id = 7"

Any thoughts?

like image 964
Stephen Corwin Avatar asked May 04 '12 23:05

Stephen Corwin


1 Answers

Figured it out. Took a look at a sample project Michael Hartl made and noticed that the correct way to do this is to specify not only a relationship table (in this case follows) but also a reverse relationship table (which I called reverse follows).

    has_many    :follows,
                        :dependent => :destroy

    has_many    :followed_projects,
                        :through => :follows,
                        :source => :followable,
                        :source_type => "Project"

    has_many    :followed_users,
                        :through => :follows,
                        :source => :followable,
                        :source_type => "User"

    has_many    :reverse_follows,
                        :as => :followable,
                        :foreign_key => :followable_id,
                        :class_name => "Follow"

    has_many    :followers,
                        :through => :reverse_follows,
                        :source => :user

Hope this helps some people out down the line!

like image 155
Stephen Corwin Avatar answered Oct 13 '22 02:10

Stephen Corwin