DEV Community

loading...
Cover image for Polymorphysm 101. Part 1 of 3. Polymorphyc Comments.

Polymorphysm 101. Part 1 of 3. Polymorphyc Comments.

yarotheslav profile image Yaroslav Shmarov Originally published at blog.corsego.com on ・2 min read

My current best approach for creating polymorphic child records based on the best bits from all other approaches.

This example - creating polymorphic Comments table that can be integrated easily into any model.

polymorphic-comments.gif

Step 1. Add polymorphic comments to the application

console

rails g migration create_comments content:text commentable:references{polymorphic}

Enter fullscreen mode Exit fullscreen mode

app/models/comment.rb

class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
  validates :content, presence: true
  def to_s
    content
  end
end 
Enter fullscreen mode Exit fullscreen mode

app/controllers/comments_controller.rb

class CommentsController < ApplicationController
  before_action :set_commentable

  def new
    @comment = Comment.new
  end

  def create
    @comment = @commentable.comments.build(comment_params)
    if @comment.save
      redirect_to @commentable, notice: "Comment created"
    else
      render :new
    end
  end

  def destroy
    @comment = Comment.find(params[:id])
    if @comment.destroy
      redirect_to @commentable, notice: "Comment deleted"
    else
      redirect_to @commentable, alert: "Something went wrong"
    end
  end

  private

  def comment_params
    params.require(:comment).permit(:content)
  end

  def set_commentable
    if params[:user_id].present?
      @commentable = User.find_by_id(params[:user_id])
    elsif params[:lesson_id].present?
      @commentable = Lesson.find_by_id(params[:lesson_id])
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

app/views/comments/_form.html.erb

<%= form_for [@commentable, @comment] do |f| %>
  <% if @comment.errors.any? %>
    <% @comment.errors.each do |error| %>
      <%= error.full_message %>
    <% end %>
  <% end %>

  <%= f.label :content %>
  <%= f.text_area :content %>
  <%= f.submit %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

app/views/comments/_index.html.erb

Comments:
<%= @commentable.comments.count %>
<% commentable.comments.each do |comment| %>
  <%= comment.created_at.strftime('%d-%m-%Y %H:%m') %>
  <%= link_to "Destroy", [@commentable, comment], method: :delete %>
  <%= simple_format(comment.content) %>
  <br>
<% end %>
Enter fullscreen mode Exit fullscreen mode

app/views/comments/new.html.erb

New Comment for:
<%= link_to @commentable, @commentable %>
<%= render 'comments/form' %>
Enter fullscreen mode Exit fullscreen mode

routes.rb

  resources :lessons do
    resources :comments, only: [:new, :create, :destroy]
  end
Enter fullscreen mode Exit fullscreen mode

Step 2. Add polymorphic comments to a model. For example lesson

app/controllers/lessons_controller.rb

  def show
    @commentable = @lesson
    @comment = Comment.new
  end
Enter fullscreen mode Exit fullscreen mode

app/models/lesson.rb

   has_many :comments, as: :commentable, dependent: :destroy
Enter fullscreen mode Exit fullscreen mode

app/views/lessons/show.html.erb - with a link to create a comment

<%= link_to "New Comment", new_lesson_comment_path(@commentable, @comment) %>
<%= render partial: "comments/index", locals: {commentable: @commentable} %>
Enter fullscreen mode Exit fullscreen mode

app/views/lessons/show.html.erb - alternative - with a comments form in the view

<%= render template: "comments/new" %>
<%= render partial: "comments/index", locals: {commentable: @commentable} %>
Enter fullscreen mode Exit fullscreen mode

Notes

In comments_controller you have to update the action set_commentable with each model that you want to make commentable. This is better than creating a separate comments_controller for each commentable model.

To add polymorphic to one more model, repeat step 2 by replacing the word lesson with whatever model you want.

Bonus: Save which user created a comment

1.

rails g migration add_user_references_to_comments user:references
Enter fullscreen mode Exit fullscreen mode
  1. To save current_user who created a comment, in comments_controller create action above line if @comment.save add the line
@comment.user_id = current_user.id

Enter fullscreen mode Exit fullscreen mode

Using simple_form

<%= simple_form_for [@commentable, @comment] do |f| %>
  <%= f.error_notification %>
  <%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>

  <%= f.input :content, label: false, required: true %>
  <%= f.button :submit %>
<% end %>
Enter fullscreen mode Exit fullscreen mode

Discussion (0)

pic
Editor guide