Jareish
Jareish

Reputation: 782

How to create a polymorphic model

I need to link Comments to a Post. However the Comment could be (user generated) a simple text, (system generated) a link or an (system generated) image.

At first they all shared the same attributes. So I just needed to create a category attribute, and do different stuff with the text attribute based on that category.

example:

class Comment < ActiveRecord::Base
  belongs_to :post
  belongs_to :author, :class_name => "User"

  CATEGORY_POST = "post"
  CATEGORY_IMAGE = "image"
  CATEGORY_LINK = "link"

  validates :text, :author, :category, :post, :presence => true
  validates_inclusion_of :category, :in => [CATEGORY_POST, CATEGORY_IMAGE, CATEGORY_LINK]

  attr_accessible :author, :text, :category, :post

  def is_post?
    self.category == CATEGORY_POST
  end

  def is_link?
    self.category == CATEGORY_LINK
  end

  def is_image?
    self.category == CATEGORY_IMAGE
  end

end

However this wil not suffice now, because I doesn't feel clean to dump every value in a generic "text" property. So I was thinking about create a polymorphic model (and if needed in a factory pattern). But when I googled about polymorphic models, I get examples like a Comment on a Post, but the same Comment on a Page, kind of relations. Is my understanding of polymorphic different (a model that acts different in different situations, compared to a model that acts the same under different scopes)?

So how would I set up this kind of relationship?

I was thinking of (and please correct me)

 Post
    id

 Comment
    id
    post_id
    category (a enum/string or integer)
    type_id (references either PostComment, LinkComment or ImageComment based on category)
    author_id

 PostComment
    id
    text

 LinkComment
    id
    link

 ImageComment
    id
    path

 User (aka Author)
    id
    name

But I have no clue how to setup the model so that I can call post.comments (or author.comments) to get all comments. A nice to have would be that the creation of a comment would be through comment and not link/image/postcomment (comment acting as the factory)

My main question is, how to setup up the activerecord models, so the relations stay intact (a author has comments and a post has comments. Comments being either a Link, Image or Postcomment)

Upvotes: 0

Views: 148

Answers (1)

jvnill
jvnill

Reputation: 29599

I'm going to answer only your main question, the model setup. Given the columns and tables you used in your question, with the exception of Comment, you can use the following setup.

 # comment.rb
 # change category to category_type
 # change type_id to category_id
 class Comment < ActiveRecord::Base
   belongs_to :category, polymorphic: true
   belongs_to :post
   belongs_to :author, class_name: 'User'
 end

 class PostComment < ActiveRecord::Base
   has_one :comment, as: :category
 end

 class LinkComment < ActiveRecord::Base
   has_one :comment, as: :category
 end

 class ImageComment < ActiveRecord::Base
   has_one :comment, as: :category
 end

with that setup, you can do the following.

 >> post = Post.first
 >> comments = post.comments
 >> comments.each do |comment|
      case comment.category_type
      when 'ImageComment'
        puts comment.category.path
      when 'LinkComment'
        puts comment.category.link
      when 'PostComment'
        puts comment.category.text
      end
    end

Upvotes: 1

Related Questions