Multiple connections using activerecord in rails
I am creating a small Twitter style microblogging service where users can follow other users and get a feed of their posts
I have the following models:
class Follow < ActiveRecord::Base
belongs_to :follower, :class_name => "User"
belongs_to :followee, :class_name => "User"
end
class User < ActiveRecord::Base
has_many :follows, :foreign_key => 'follower_id',
:class_name => 'Follow'
has_many :followers, :through => :follows
has_many :followed, :foreign_key => 'followee_id',
:class_name => 'Follow'
has_many :followees, :through => :followed
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :user
end
To get the feed for the current user, I want to execute the following SQL query:
SELECT * FROM follows JOIN users JOIN messages WHERE follows.follower_id = current_user.id AND follows.followee_id = users.id AND users.id = messages.user_id;
What is the correct ActiveRecord way to do this?
source to share
Not sure what you are looking for, but here is my suggestion:
I am assuming you have other targets for this Follow class, otherwise I cannot see its purpose.
The "correct way" (ie my totally subjective way) of doing this would actually be something like this:
class User < ActiveRecord::Base
has_and_belongs_to_many :followers, :foreign_key => 'followed_id',
:class_name => 'User', :association_foreign_key => 'follower_id',
:include => [:messages]
has_and_belongs_to_many :follows, :foreign_key => 'follower_id',
:class_name => 'User', :association_foreign_key => 'followed_id'
has_many :messages
end
class Message < ActiveRecord::Base
belongs_to :user
end
Then create the following table:
create_table :users_users, :id => false do |t|
t.integer :followed_id
t.integer :follower_id
end
And you installed:
followed = User.find :first
follower = User.find :last
followed.followers << follower
followed.followers.first.messages
followed.followers.first.followers.first.messages # etc...
But from what I do you want to display all posts from all subscribers at the same time.
This can be achieved by adding
has_and_belongs_to_many :followed_messages, :foreign_key => 'follower_id',
:class_name => 'Message', :association_foreign_key => 'followed_id'
for the User class, but I don't know how correct that would be. Or could be achieved with extension of the association, but there I really cannot give examples. Business>
Update:
By changing: class_name it will associate it with Message.id
, didn't think about it, so it won't be correct this way.
So the only "good" option is to go through the User class like in the first example. The only other options I see are either association extensions (which I cannot give you) or perhaps using a finder statement.
has_many :followed_messages, :class_name => 'Message',
:finder_sql => 'select * from messages where user_id in(select followed_id from users_users where follower_id = #{id})'
You will probably have to tweak this sql statement to get everything to work, but at least you should get an image :)
source to share
Keijro's organization will perform better, although if you need a Follow table, then you can execute the requested SQL query like this:
Follow.all(:joins => { :messages, :users }, :conditions => { "follows.follower_id" => current_user.id, "follows.followee_id" => "users.id", "users.id" => "messages.user_id"} )
source to share