Matthew Berman
Matthew Berman

Reputation: 8631

How do I pull a list of models (events) through a relationship with another model (Users)?

This is a bit complicated and I'm not sure how to implement it. I have a User model and a Relationship model. Users are able to "follow" each other (just like twitter). The relationship model is all setup properly and works great.

Next, I have an Event model. Each user has_and_belongs_to_many events (many to many association between users and events). Users "attend" events.

What I would like to do is pull a list of all events that are

  1. being attended by the current_user
  2. are being attended by users that current_user is following.

If possible, I would like to have this list accessible via the User model so I can say current_user.event_feed and it will list all events as mentioned above.

Here are my models:

class Event < ActiveRecord::Base
  attr_accessible :name, 
                  :description, 
                  :event_date, 
                  :location, 
                  :owner_id,
                  :category,
                  :photo

  CATEGORIES = ['Music', 'Outdoors', 'Party']

  has_and_belongs_to_many :users

and relationship model:

class Relationship < ActiveRecord::Base
  attr_accessible :followed_id

  belongs_to :follower, :class_name => "User"
  belongs_to :followed, :class_name => "User"

  validates :follower_id, :presence => true
  validates :followed_id, :presence => true
end

and user model:

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  attr_accessible :email, :password, :password_confirmation, :remember_me
  attr_accessor :password
  attr_accessible :name, :email, :password, :password_confirmation, :time_zone

  has_and_belongs_to_many :events

  has_many :relationships, :dependent => :destroy,
                             :foreign_key => "follower_id"
  has_many :reverse_relationships, :dependent => :destroy,
                                   :foreign_key => "followed_id",
                                   :class_name => "Relationship"
  has_many :following, :through => :relationships, 
                       :source  => :followed
  has_many :followers, :through => :reverse_relationships,
                       :source  => :follower

Thanks!

Upvotes: 0

Views: 311

Answers (3)

Adam Eberlin
Adam Eberlin

Reputation: 14205

This is rails 3 only, but quite elegant (untested, hopefully my memory of habtm relationships is ok).

class User < ActiveRecord::Base
  # ...

  def event_feed
    ids = self.followers.collect(&:id) << self.id
    Event.includes(:users).where(["`users`.id IN (#{ids.join(',')})"])
  end

  # ...
end

Upvotes: 1

brycemcd
brycemcd

Reputation: 4513

1) being attended by the current_user and This can be achieved simply by calling current_user.events

2) are being attended by users that current_user is following. This is a little trickier. You want to end up with a flattened list of other user's events: current_user.following.collect { |friend| friend.events }.flatten #=> returns an array of followers' events

Since you want to display all events in a single list (from what I could gather), I think a presenter class would be useful:

class EventFeed
    attr_accessor :event, :display_name

   def initialize(event, name)
     self.event = event
     self.name  =  name
   end
end

And now, adding them together to get to current_user.event_feed

class User
   def event_feed; []; end
end

And gluing it all together:

current_user.events.each { |e| current_user.event_feed << EventFeed.new(e, 'YOU') }
current_user.following.each do |friend| 
   friend.events.each { |e| current_user.event_feed << EventFeed.new(e, friend.name) }
end

current_user.event_feed #=> an array of EventFeed objects where you can display "You are going to #{event.name}"

Of course this is pseudo code, but it should get you on the right track

Upvotes: 1

nkm
nkm

Reputation: 5914

Event Model:

scope :attended, where("event_date < #{Date.today}")

User Model:

# Returns collection of events that are attended by user and users she follows
def attended events
  attended_events = []
  attended_events << events.attended
  followers.each do |follower|
    attended_events << follower.events.attended
  end
  attended_events
end

Upvotes: 1

Related Questions