AnthonyGalli.com
AnthonyGalli.com

Reputation: 2866

How to fix ActiveRecord::RecordNotFound when liking comment?

When a user tries to like a comment he is confronted with this error:

ActiveRecord::RecordNotFound (Couldn't find Comment with 'id'=3 [WHERE "comments"."user_id" = ?]):
  app/controllers/comments_controller.rb:58:in `set_comment'

I keep tinkering with one of the lines in the controller, but I'm not sure if that's the answer.

comments_controller

class CommentsController < ApplicationController
  before_action :set_commentable, only: [:index, :new, :create]
  before_action :set_comment, only: [:edit, :update, :destroy, :like]
  before_action :correct_user, only: [:edit, :update, :destroy]

  def index
    @comments = @commentable.comments
  end

  def new
    @comment = @commentable.comments.new
  end

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

  def edit
  end

  def update
    if @comment.update_attributes(comment_params)
      redirect_to :back, notice: "Comment was updated."
    else
      render :edit
    end
  end

  def destroy
    @comment.destroy
    redirect_to @comment.commentable, notice: "Comment destroyed."
  end

  def like
    @comment = Comment.find(params[:id]) # I've been tinkering with this line.
    @comment_like = current_user.comment_likes.build(comment: @comment)
    if @comment_like.save
      @comment.increment!(:likes)
      flash[:success] = 'Thanks for liking!'
    else
      flash[:error] = 'Too many likes'
    end  
    redirect_to(:back)
  end

  private

  def set_commentable
    @commentable = find_commentable
  end

  def set_comment
    @comment = current_user.comments.find(params[:id])
  end

  def correct_user
    @comment = current_user.comments.find_by(id: params[:id])
    redirect_to root_url, notice: "Not authorized to edit this comment" if @comment.nil?
  end

  def find_commentable
    if params[:goal_id]
      Goal.find(params[:goal_id])
    elsif params[:habit_id]
      Habit.find(params[:habit_id])
    elsif params[:valuation_id]
      Valuation.find(params[:valuation_id])
    elsif params[:stat_id]
      Stat.find(params[:stat_id])
    end
  end

  def comment_params
    params[:comment][:user_id] = current_user.id
    params.require(:comment).permit(:content, :commentable, :user_id, :like)
  end
end

comment_like.rb

class CommentLike < ActiveRecord::Base
  belongs_to :user
  belongs_to :comment
  belongs_to :habit
  belongs_to :stat
  belongs_to :valuation
  belongs_to :goal
  validates :user, uniqueness: { scope: :comment }
  belongs_to :liker, class_name: 'User', foreign_key: :user_id
  belongs_to :liked_comment, class_name: 'Comment', foreign_key: :comment_id
end

comment.rb

class Comment < ActiveRecord::Base
    after_save :create_notification
  has_many :notifications
  has_many :comment_likes   
  has_many :likers, through: :comment_likes, class_name: 'User', source: :liker
  belongs_to :habit
  belongs_to :stat
  belongs_to :valuation
  belongs_to :goal
    belongs_to :user
  validates :user, presence: true

private

  def create_notification
    author = 
      if goal
        goal.user
      #elsif comment_like
      # comment_like.user
      elsif habit
       habit.user
      elsif stat
        stat.user
      elsif valuation
        valuation.user
      end
    notifications.create(
      comment:      self,
      likes:        likes,
      habit:        habit,
      stat:         stat,
      goal:         goal,
      valuation:    valuation,
      user:         author,      
      read:         false
    )
  end
end

schema

  create_table "comment_likes", force: true do |t|
    t.integer  "user_id"
    t.integer  "comment_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "comments", force: true do |t|
    t.text     "content"
    t.integer  "goal_id"
    t.integer  "habit_id"
    t.integer  "valuation_id"
    t.integer  "stat_id"
    t.integer  "commentable_id"
    t.string   "commentable_type"
    t.integer  "user_id"
    t.datetime "created_at",       null: false
    t.datetime "updated_at",       null: false
    t.integer  "likes"
  end

  add_index "comments", ["commentable_id", "commentable_type"], name: "index_comments_on_commentable_id_and_commentable_type"
  add_index "comments", ["commentable_type", "commentable_id"], name: "index_comments_on_commentable_type_and_commentable_id"
  add_index "comments", ["user_id"], name: "index_comments_on_user_id"

Everything else works, like the ability to comment and like a user's own comment, but not when a user tries to like someone else's comment.

Upvotes: 0

Views: 66

Answers (2)

Aetherus
Aetherus

Reputation: 8898

This error is caused by

before_action :set_comment, only: [:edit, :update, :destroy, :like]

Remove :like will solve the issue.

Other lines of code of your original post are fine.

Because the Comment which current_user likes DOES NOT BELONG TO current_user, current_user.comments.find(params[:id]) will raise ActiveRecord::RecordNotFound.

Upvotes: 1

Adib Saad
Adib Saad

Reputation: 572

As per the documentation here, an ActiveRecord::RecordNotFound exception will be thrown if the record is not found.

The error message states app/controllers/comments_controller.rb:58:in 'set_comment'. set_comment seems to be getting called because you tell your controller to call it as a before_action

before_action :set_comment, only: [:edit, :update, :destroy, :like]

set_comment does the following

@comment = current_user.comments.find(params[:id])

It's only looking within the scope of the current_user's comments, which explains why a user can like his own comments but not anyone else's. You need to change it to

@comment = Comment.find(params[:id])

Now it'll look for all comments with that id. Also, this means you can remove the first line of the like action since it does the same thing as set_comment which is redundant (you're querying your db twice).

Upvotes: 1

Related Questions