Allow Comments On Your App

Created by Janika Liiv, @janikaliiv.
Compiled and modified for Rails Girls KL by Faezrah, @fzrhrs.

This guide assumes that you have already built a Rails Girls app by following the extended app guide and you've added user authentication to your app.

1.Create Comment Model

Create a comment model, with the comment body (contents of the comment), reference to the photos table (photo_id) and also reference to the users table (user_id).

$ bin/rails generate model Comment body:text photo_id:integer user_id:integer

This will create a migration file that lets your database know about the new comments table. Run the migration with:

$ bin/rake db:migrate

2.Associating Models

You need to make sure that Rails knows the relation between objects (photos, comments and users). As one photo can have many comments we need to make sure the photo model knows that. Open app/models/photo.rb and after the line:

class Photo < ApplicationRecord

Add:

has_many :comments, dependent: :destroy

Add the same relation in app/models/user.rb.

The comment also has to know that it belongs to a photo and a user. So in app/models/comment.rb, after the first line, add the following:

belongs_to :photo
belongs_to :user

Resource: Learn more about Active Record Associations.

3.Create Comment Controller

Now, we need a controller for comments.

$ bin/rails g controller comments

We will then need to add a route so that Rails knows where we would like to navigate to see comments.

In config/routes.rb, edit it as follows:

resources :photos do
  resources :comments
end

This creates comments as a nested resource within photos.

Resource: Learn more about Rails Routing.

4.Render The Comment Form And Existing Comments

Like Instagram, users may add their comments after seeing the photo, and once they have added their comment, they will be sent back to the photo show page to see their comment.

To do this, we need to add a form for comments in the app/views/photos/show.html.erb, like so:

<% if user_signed_in? %>
  <%= form_for [@photo, @photo.comments.build] do |f| %>
    <div class="form-group">
      <%= f.text_area :body, placeholder: "Add a comment...", class: "form-control" %>
    </div>
    <div class="form-group pull-right">
      <%= f.submit class: "btn btn-default" %>
    </div>
  <% end %>
<% end %>

Your comments_controller.rb should have something like this:

before_action :authenticate_user!

def create
  @photo = Photo.find(params[:photo_id])
  @comment = @photo.comments.create(comment_params)
  @comment.user_id = current_user.id
  if @comment.save
    redirect_to photo_path(@photo)
  else
    redirect_to root_path
  end
end

private
  def comment_params
    params.require(:comment).permit(:body)
  end

To display existing comments for a specific photo, we need to adjust our app/views/photos/show.html.erb. After:

<p><%= @photo.caption %></p>

You can add:

<% @photo.comments.each do |comment| %>
  <p>
    <span style="font-weight: bold;"><%= comment.user.email %></span>
    <span><%= comment.body %></span>
  </p>
<% end %>

That's it.