Ruby on Rails - Adding comments to post

16,599

As a comment can belong to a single post only, you do not need an association table (post_comments). You just need a simple one-to-many relationship.

Your post comment would be:

class Post < ActiveRecord::Base
  has_many :comments
  ...
end

And comment would be like this:

class Comment < ActiveRecord::Base
  belongs_to :post
  ...
end

Just make sure that you have the necessary post_id column in the comments table (you can check the db/schema.rb file). If that is missing, you can use the following migration to add it:

class AddPostIdToComments < ActiveRecord::Migration
  def change
    add_column :comments, :post_id, :integer
    add_index  :comments, :post_id
  end
end

You also need to make sure you keep somewhere the reference to the post, whenever a user tries to create a comment to a post. You can add this in a hidden field to your comments/new.html.erb template. You could set the hidden field in the new action, in PostsController, after passing it through the URL.

So, in your posts/show.html.erb template you would have:

<%= link_to "Add Comment", new_comment_path(post_id: @post.id) %>

In your new action, in PostsController:

def new
  @comment = Comment.new(post_id: params[:post_id])
end

And finally the hidden field in your form would be:

<%= f.hidden_field :post_id %>

Finally, add the post_id parameter to the list of permitted parameters in CommentsController.

Share:
16,599
Adam 'Sacki' Sackfield
Author by

Adam 'Sacki' Sackfield

Currently studying programming. Starting with CS50, currently on C. Also have a grasp on HTML, CSS and a little JS

Updated on June 04, 2022

Comments

  • Adam 'Sacki' Sackfield
    Adam 'Sacki' Sackfield almost 2 years

    Trying to learn RoR. Currently adding comments to posts by user. So far I have a posts model, comments model and post_comments model (to associate the two). So for in 'rails console' I can run: (say I set p = Post.first and c = Comment.first)

    p.comments << c

    This forms an association so it works in the console. I just can't seem to get the comments to form this association from the UI. So far I am creating the comments at "comments/new" (not sure if this is the issue. Do they need to be created on the "show view" for "post").

    Here are some code snippets

    Controllers comments_controller.rb

    class CommentsController < ApplicationController
    
    def index
      @comment = Comment.all
    end
    
    def new
      @comment = Comment.new
    end
    
    def create
      @comment = Comment.new(commentParams)
      if @comment.save
        flash[:success] = "Comment successfully added"
        redirect_to comments_path(@comment)
      else
        render 'new'
      end
    end
    
    def show
      @comment = Comment.find(params[:id])
    end
    
    private
    
      def commentParams
        params.require(:comment).permit(:comment)
      end
    end
    

    posts_controller

    class PostsController < ApplicationController
    before_action :setPost, only: [:edit, :update, :show, :destroy, :sold]
    before_action :requireUser, except: [:index, :show]
    before_action :requireSameUser, only: [:edit, :update, :destroy, :sold]
    
    def index
      @posts = Post.paginate(page: params[:page], per_page: 20)
    end
    
    def new
      @post = Post.new
    end
    
    def create
      @post = Post.new(postParams)
      @post.user = currentUser
      if @post.save
        flash[:success] = "Post successfully added."
        redirect_to post_path(@post)
      else
        render 'new'
      end
    end
    
    def update
      if @post.update(postParams)
        flash[:success] = "Post successfully updated."
        redirect_to post_path(@post)
      else
        render 'edit'
      end
    end
    
    def show
    end
    
    def edit
    end
    
    def sold
      @post.toggle(:sold)
      @post.save
      redirect_to post_path(@post)
    end
    
    def destroy
      @post.destroy
      flash[:danger] = "Item successfully deleted."
      redirect_to posts_path
    end
    
    private
      def postParams
        params.require(:post).permit(:title, :price, :description,   category_ids:[])
      end
    
      def setPost
        @post = Post.find(params[:id])
      end
    
      def requireSameUser
        if currentUser != @post.user and !currentUser.admin
          flash[:danger] = "You can only edit or delete your own items"
          redirect_to root_path
        end
      end
    end
    

    Models comment.rb

    class Comment < ActiveRecord::Base
      belongs_to :post_comments
      belongs_to :user
      belongs_to :post
    end
    

    post_comment.rb

    class PostComment < ActiveRecord::Base
      belongs_to :post
      belongs_to :comment
    end
    

    post.rb

    class Post < ActiveRecord::Base
      belongs_to :user
      has_many :post_categories
      has_many :categories, through: :post_categories
      has_many :post_comments
      has_many :comments, through: :post_comments  
    
      validates :title, presence: true,
                    length: { minimum: 4, maximum: 20 }
    
      validates :description, presence: true,
                          length: { maximum: 1000 }
      validates :user_id, presence: true
    

    end

    Views posts/show.html.erb

      <p>Comments: <%= render @post.comments %></p>
    

    This renders the partial below

    comments/_comment.html.erb <%= link_to comment.name, comment_path(comment) %>  

    Finally is the new comment page as it is.

    comments/new.html.erb

    <h1>New Comment</h1>
    
    <%= render 'shared/errors', obj: @comment %>
    
    <div class="row">
    <div class="col-xs-12">
      <%= form_for(@comment, :html => {class: "form-horizontal", role: "form"}) do |f| %>
    
      <div class="form-group">
        <div class="control-label col-sm-2">
          <%= f.label :comment %>
        </div>
    
        <div class="col-sm-8">
          <%= f.text_area :comment, rows: 3, class: "form-control", placeholder: "Please enter a comment", autofocus: true %>
        </div>
      </div>
    
      <div class="form-group">
        <div class="center col-sm-offset-1 col-sm-10">
          <%= f.submit class: "btn btn-primary btn-lg" %>
        </div>
      </div>
    <% end %>
    

    Any help would be greatly received.

    Update

    Log

    Started GET "/posts/2" for ::1 at 2016-01-15 12:39:55 +0000
    Processing by PostsController#show as HTML
    Parameters: {"id"=>"2"}
    [1m[36mPost Load (0.1ms)[0m  [1mSELECT  "posts".* FROM "posts" WHERE    "posts"."id" = ? LIMIT 1[0m  [["id", 2]]
    [1m[35mUser Load (0.1ms)[0m  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
    [1m[36m (0.1ms)[0m  [1mSELECT COUNT(*) FROM "posts" WHERE "posts"."user_id" = ?[0m  [["user_id", 1]]
    [1m[35mCategory Exists (0.1ms)[0m  SELECT  1 AS one FROM "categories"    INNER JOIN "post_categories" ON "categories"."id" =  "post_categories"."category_id" WHERE "post_categories"."post_id" = ?  LIMIT 1  [["post_id", 2]]
    [1m[36mCategory Load (0.0ms)[0m  [1mSELECT "categories".* FROM "categories" INNER JOIN "post_categories" ON "categories"."id" = "post_categories"."category_id" WHERE "post_categories"."post_id" = ?[0m  [["post_id", 2]]
    Rendered categories/_category.html.erb (0.2ms)
    [1m[35mComment Load (0.1ms)[0m  SELECT "comments".* FROM "comments"  WHERE "comments"."post_id" = ?  [["post_id", 2]]
    Rendered comments/_comment.html.erb (0.1ms)
    Rendered posts/show.html.erb within layouts/application (6.5ms)
    [1m[36mCategory Load (0.1ms)[0m  [1mSELECT "categories".* FROM "categories"[0m
    Rendered layouts/_navigation.html.erb (0.9ms)
    Rendered layouts/_messages.html.erb (0.1ms)
    Rendered layouts/_footer.html.erb (0.1ms)
    Completed 200 OK in 52ms (Views: 50.3ms | ActiveRecord: 0.5ms)