TheSuper
TheSuper

Reputation: 645

Modeling a Subscription in Rails

So, after thinking on this for a while, I have no idea what the proper way to model this is.

I have a website focused on sharing images. In order to make the users happy, I want them to be able to subscribe to many different collections of images.

So far, there's two types of collections. One is a "creator" relationship, which defines people who worked on a specific image. That looks like this:

class Image < ActiveRecord::Base
   has_many :creations
   has_and_belongs_to_many :locations
   has_many :creators, through: :creations
end

class Creator < ActiveRecord::Base
  has_many :images, ->{uniq}, through: :creations
  has_many :creations
  belongs_to :user
end


class Creation < ActiveRecord::Base
  belongs_to :image
  belongs_to :creator
end

Users may also tag an image with a subjective tag, which is something not objectively present in the image. Typical subjective tags would include "funny" or "sad," that kind of stuff. That's implemented like this:

class SubjectiveTag < ActiveRecord::Base
  # Has a "name" field. The reason why the names of tags are a first-class DB model
  # is so we can display how many times a given image has been tagged with a specific tag
end

class SubjectiveCollection < ActiveRecord::Base
  # Basically, "User X tagged image Y with tag Z"
  belongs_to :subjective_tag
  belongs_to :user
  has_many :images, through: :subjective_collection_member
end

class SubjectiveCollectionMember < ActiveRecord::Base
  belongs_to :subjective_collection
  belongs_to :image
end

I want users to be able to subscribe to both Creators and SubjectiveTags, and to display all images in those collections, sequentially, on the home page when they log in.

What is the best way to do this? Should I have a bunch of different subscription types - for example, one called SubjectiveTagSubscription and one called CreatorSubscription? If I do go this route, what is the most efficient way to retrieve all images in each collection?

Upvotes: 0

Views: 434

Answers (1)

Todd Agulnick
Todd Agulnick

Reputation: 1995

What you want to use is a Polymorphic Association.

In your case, it would look like this:

class Subscription < ActiveRecord::Base
  belongs_to :user
  belongs_to :subscribeable, polymorphic: true
end

The subscriptions table would need to include the following fields:

  • user_id (integer)
  • subscribeable_id (integer)
  • subscribeable_type (string)

This setup will allow a subscription to refer to an instance of any other model, as ActiveRecord will use the subscribeable_type field to record the class name of the thing being subscribed to.

To produce a list of images for the currently logged in user, you could do this:

Subscription.where(user_id: current_user.id).map do |subscription|
  subscription.subscribeable.images.all
end.flatten

If the performance implications of the above approach are intolerable (one query per subscription), you could collapse your two types of subscribeables into a single table via STI (which doesn't seem like a good idea here, as the two tables aren't very similar) or you could go back to your initial suggestion of having two different types of subscription models/tables, querying each one separately for subscriptions.

Upvotes: 1

Related Questions