Reputation: 2874
I have a few models that I would like users to be able to subscribe or unsubscribe to i.e. opt-in or out of receiving notifications when certain events happen.
For example, I have a Video model which users can comment on and review. If a user subscribes to a video they would receive notifications (similar to Github's) when something happens e.g. comments, reviews, status changes, etc.
I'm wondering how to set this up in terms of models and their relationships to one another.
For example, would I create a generic NotificationSubscription model like so?:
class NotificationSubscription
belongs_to :subscribable, polymorphic: true
belongs_to :subscriber, class_name: "User", foreign_key: "user_id"
end
class User
has_many :notification_subscriptions, dependant: :destroy
end
Then in the the subscribable models:
class Video
has_many :notification_subscriptons, as: :subscribable, dependant: :destroy
has_many :notification_subscribers, through: :notification_subscriptons, source: :subscriber
end
or with a concern:
module Subscribable
extend ActiveSupport::Concern
included do
has_many :notification_subscriptions, as: :subscribable, dependent: :destroy
has_many :subscribers, through: notification_subscriptions
end
module ClassMethods
end
def subscription_for(subscriber)
subscriber.try(:notification_subscriptions).try(:find_by, subscribable: self)
end
def subscribe(subscriber)
return false if subscription_for(subscriber).present?
subscription = NotificationSubscription.new(subscribable: self, subscriber: subscriber)
subscription.save
end
def unsubscribe(subscriber)
subscription = subscription_for(subscriber)
return false unless subscription.present?
subscription.destroy
end
end
class Video
include Subscribable
end
and the notification_subscription schema:
class CreateNotificationSubscriptions < ActiveRecord::Migration
def change
create_table :notification_subscriptions do |t|
t.references :user, index: true
t.references :subscribable, polymorphic: true, index: true
t.boolean :subscribed
t.boolean :ignored
t.string :reason
t.timestamps
end
add_index :notifications_subscriptions, [:user_id, :subscribable_id, :subscribable_type], unique: true
end
end
Upvotes: 1
Views: 830
Reputation: 4636
I would use a polymorphic association, but your verbiage is slightly wrong, I think. The example given is:
http://guides.rubyonrails.org/association_basics.html#polymorphic-associations
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
class Employee < ActiveRecord::Base
has_many :pictures, as: :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, as: :imageable
end
It's much simpler than your polymorphic code was making it. It's really a combination of what you have in your first and second examples.
Slightly off-topic, but hopefully this is helpful, I would use ActiveSupport::Notifications
for the events themselves. This way you can fire an event and have a separate object subscribe to that event to send the subscribers (users) notifications out via direct email or by kicking off a Sidekiq job. http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html
Upvotes: 1