1
votes

I am getting the following error from Heroku log when I visit the photos/show.html.erb view:

ActionView::Template::Error (No route matches {:action=>"index", :comment_id=>nil, :controller=>"cflags", :photo_id=>"100"} missing required keys: [:comment_id])

I have very basic Photo and Comment models that work correctly and then I have built a Cflag model that is used to flag comments. I use @photo.comments to list the comments in the photos/show.html.erb view.

Show:

# photos/show.html.erb

<% @photo.comments.each do |comment| %>
  <%= form_for(comment, url: photo_comment_cflags_path(@photo, comment)) do |f| %>
    <%= f.hidden_field :user_id, value: current_user.id %>
    <%= f.submit "Report Inappropiate" %>
  <% end %>
<% end %> 

Cflags Controller:

class CflagsController < ApplicationController
before_action :logged_in_user

def new
end

def create
  @comment = Comment.find(params[:comment_id])
  @cflag = @comment.cflags.build(cflag_params)
    if @cflag.save
      if @comment.cflags_count > 1
        @comment.update_attribute(:approved, false)  
        flash[:success] = "Flag created! Comment ##{@comment.id} has been removed for review. Thank you for your feedback"
        redirect_to :back
      else    
        flash[:success] = "Flag created! Thank you for your feedback"
        redirect_to :back
      end
    else
      redirect_to :back, notice: @cflag.errors.full_messages  
    end    
  end    

  private 
    def cflag_params
      params.require(:cflag).permit(:user_id, :comment_id).merge(user_id: current_user.id)
    end
end

Routes:

resources :photos do
  resources :comments, only: [:create, :edit, :destroy] do
    resources :cflags, only: :create
  end  
end

Rails routes:

    photo_comment_cflags GET        /photos/:photo_id/comments/:comment_id/cflags(.:format)          cflags#index
                         POST       /photos/:photo_id/comments/:comment_id/cflags(.:format)          cflags#create
 new_photo_comment_cflag GET        /photos/:photo_id/comments/:comment_id/cflags/new(.:format)      cflags#new
edit_photo_comment_cflag GET        /photos/:photo_id/comments/:comment_id/cflags/:id/edit(.:format) cflags#edit
     photo_comment_cflag GET        /photos/:photo_id/comments/:comment_id/cflags/:id(.:format)      cflags#show
                         PATCH      /photos/:photo_id/comments/:comment_id/cflags/:id(.:format)      cflags#update
                         PUT        /photos/:photo_id/comments/:comment_id/cflags/:id(.:format)      cflags#update
                         DELETE     /photos/:photo_id/comments/:comment_id/cflags/:id(.:format)      cflags#destroy

Photos Controller:

def show
  @photo = Photo.approved.find(params[:id])
end

Comments Controller:

def create
   @photo = Photo.find(params[:photo_id])
   @comment = @photo.comments.build(comment_params)
   @comment.save
   respond_to do |format|
     format.html { redirect_to :back }
     format.js 
   end 
end   

Cflag Model:

class Cflag < ActiveRecord::Base
  belongs_to :comment, counter_cache: true
  belongs_to :user, counter_cache: true
  validates :user_id, presence: true 
  validates :comment_id, presence: true
  validates :user_id, uniqueness: { 
    scope: [:comment_id],
    message: 'You can only flag a comment once. Thank you for your feedback.'
  }
  default_scope -> { order(created_at: :desc) }
end

Schema:

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

add_index "cflags", ["comment_id"], name: "index_cflags_on_comment_id"
add_index "cflags", ["user_id"], name: "index_cflags_on_user_id"

In Heroku Console

p = Photo.find(100) 
p.comments = => #<ActiveRecord::Associations::CollectionProxy [#<Comment id: 309, content: "hi", photo_id: 100, user_id: 1, created_at: "2016-07-24 04:43:17", updated_at: "2016-07-24 04:43:17", approved: true, cflags_count: nil>
3
Hi, what happens if a comment is already reported in the show page?Sahil
Well I have not yet gotten the page with the form to load but if it did load and a cflag already existed then it would trigger: else redirect_to :back, notice: @cflag.errors.full_messages because of the uniqueness scope in the model: validates :user_id, uniqueness: { scope: [:comment_id],Timmy Von Heiss

3 Answers

1
votes

As per the Rails documentation, form_for method expected arguments are (record, options = {}, &block).

form_for(record, options = {}, &block) public

Creates a form that allows the user to create or update the attributes of a specific model object.

The method can be used in several slightly different ways, depending on how much you wish to rely on Rails to infer automatically from the model how the form should be constructed. For a generic model object, a form can be created by passing form_for a string or symbol representing the object we are concerned with:

So you may want to remove the extra [] you inserted in the form_for method and it should look like this:

<% @photo.comments.each do |comment| %>
  <%= form_for(Cflag.new, url: photo_comment_cflags_path(@photo, comment)) do |f| %>
    <%= f.hidden_field :user_id, value: current_user.id %>
    <%= f.submit "Report Inappropiate" %>
  <% end %>
<% end %> 

I suggest you to try it like this and see if it resolves your current issue.

1
votes

Change the form action to this, Updated:

<% if @photo.comments.any? %>
   <% @photo.comments.each do |comment| %>     
     # you do not have to use comment in form_for, because this form is for creating cflag
     <%= form_for(:cflag, url: photo_comment_cflags_path(@photo, comment), method: "post") do |f| %> 
          <%= f.hidden_field :user_id, value: current_user.id %> 
          <%= f.submit "Report Inappropiate" %> 
     <% end %> 
   <% end %> 
<% end %>
0
votes

Your first argument is array, try this:

<%= form_for(comment, url: photo_comment_cflags_path(@photo, comment)) do |f| %>