lucis-fluxum
lucis-fluxum

Reputation: 64

Choosing one of two possible associations in a rails model

Newbie to rails here. I'm making an app to manage users, snippets of text, comments on the snippets, and likes.

Each user has many snippets and has many comments. Each snippet belongs to some user, has many comments, and can have many likes. Each comment belongs to some user, belongs to a snippet, and can have many likes.

My issue is with the Like model. A like will belong to some user, and belong to EITHER a snippet or a comment.

My migration looks like this:

class CreateLikes < ActiveRecord::Migration
  def change
    create_table :likes do |t|
      t.references :user
      t.references :snippet # or :comment...?

      t.timestamps
    end
  end
end

My Snippet model:

class Snippet < ActiveRecord::Base
  has_many :comments
  has_many :likes
  belongs_to :user
end

My Comment model:

class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :snippet
  has_many :likes
end

My Like model:

class Like < ActiveRecord::Base
  belongs_to: :user
  # belongs to either :snippet or :comment, depending on
  # where the like is, and what controller it was submitted to
end

I can't reference both, and I feel like creating a new type of Like for every kind of content in the app would be messy.

How do I decide whether to pick between referencing a snippet or referencing a comment for each like?

Upvotes: 0

Views: 93

Answers (2)

ranendra
ranendra

Reputation: 2512

You need to take a look at Polymorphic Associations in Rails. RailsCasts hava a good episode on this i.e http://railscasts.com/episodes/154-polymorphic-association.

Here, I would create a polymorphic model for Like and make Comments and Snippets likeable.

class CreateLikes < ActiveRecord::Migration
  def change
    create_table :likes do |t|
      t.integer :likeable_id
      t.string :likeable_type
      t.references :user
      t.timestamps
    end
  end
end
class Snippet < ActiveRecord::Base
  has_many :comments
  has_many :likes, as: :likeable
  belongs_to :user
end
class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :snippet
  has_many :likes, as: :likeable
end

class Like < ActiveRecord::Base
  belongs_to: :user
  belongs_to :likeable, :polymorphic => true
  # belongs to either :snippet or :comment, depending on
  # where the like is, and what controller it was submitted to
end

Upvotes: 1

Bart Jedrocha
Bart Jedrocha

Reputation: 11570

What you're looking for is called a Polymorphic Association and easily accomplished with Rails and active record.

You'll need to modify your Like migration slightly to include an additional _type column. The easiest way to do this is in the migration.

def change
  create_table :likes do |t|
    # other columns ...
    t.references :likeable, polymorphic: true
    t.timestamps
  end
end

All this does is create a likeable_id and a likeable_type column which will reference a particular id and class type (in your case, the type will be either "Comment" or "Snippet"). After this you can setup your associations like this

class Like < ActiveRecord::Base
  belongs_to :likeable, polymorphic: true
end

class Snippet < ActiveRecord::Base
  has_many :likes, as: :likeable
end

class Comment < ActiveRecord::Base
  has_many :likes, as: :likeable
end

Upvotes: 5

Related Questions