I am implementig the system that enable user to follow the "followable"( in my case these may be an event, place or other user).
My idea:
Follow model holds the user_id, followable type and followale_id(join table)
class Follow < ActiveRecord::Base
belongs_to :user
belongs_to :followable, polymorphic: true
end
Event
class Event < ActiveRecord::Base
has_many :follows, as: :followable
has_many :users, through: :follows
end
Place
class Place < ActiveRecord::Base
has_many :follows, as: :followable
has_many :users, through: :follows
end
User
class User < ActiveRecord::Base
has_many :follows
has_many :events, through: :follows, source: :followable, source_type: "Event"
has_many :places, through: :follows, source: :followable, source_type: "Place"
has_many :users, through: :follows, source: :followable, source_type: "User"
end
The problem is that the realtion works only in one direction, i can do:
user.follows.create(followable:event1) #follow event1
user.follows.create(followable:place1) #follow place1
user.follows.create(followable:user1) #follow user1
user.follows # displays all following relations user has established
But, i cannot do:
event1.follows #return follow objects(some user - event1 pairs)
event1.users #return all of the users that follow this event
user1.users #return all of the users that user1 follows, the most confusing part..
All of the aboves return nil.
How should i establish the relations to make it work in both directions?
Also, i'd like to hear some remarks on how to improve this idea, beacouse it's the first time i'm playin around with more complex realtions.
Thank you in advance.
Lets start off with the User model as its the trickiest:
class User < ActiveRecord::Base
has_many :follows, source: :user
has_many :follows_as_fallowable,
class_name: 'Follow',
as: :followable
has_many :followers, through: :follows_as_fallowable,
source: :user
# other users the user is following
has_many :followed_users, through: :follows,
source: :followable,
source_type: 'User'
end
Note that we need two different relationships to follows
due to the fact that the user can be in either column depending on if the user is the follower or the object being followed.
We can now do a simple test to check if the relationship is setup properly:
joe = User.create(name: 'Joe')
jill = User.create(name: 'Jill')
joe.followers << jill
jill.followed_users.include?(joe) # true
joe.followers.include?(jill) # true
To then setup a the bi-directional relation between users and a followable model you would do:
class Event < ActiveRecord::Base
has_many :follows, as: :followable
has_many :followers, through: :follows,
source: :user
end
class User < ActiveRecord::Base
# ...
has_many :followed_events, through: :follows,
source: :followable,
source_type: 'Event'
end
The relations in the followed model (Event) are pretty much the same in every model so you can easily extract it to a module for reuse:
# app/models/concerns/followable.rb
module Followable
extend ActiveSupport::Concern
included do
has_many :follows, as: :followable
has_many :followers, through: :follows,
source: :user
end
end
class Event < ActiceRecord::Base
include Followable
end
class Place < ActiceRecord::Base
include Followable
end