0
votes

I am trying to build a gallery with commenting function. Consider there are 3 classes: Album, Photo, Comment. The relation of these objects are Album has many Photos, Photo has many Comments.

Each Comment is left by registered user, thus in the comment object there are comment_text, user_id, and photo_id (Referring to the photo which the comment belongs).

I am trying to create a comment form under each photo's "show" view. Here is some of the Controller and the Show view:

controllers/photos_controller.rb

class PhotosController < ApplicationController
    ...
    def show
       @photo = Photo.find(params[:id])
       @new_comment = @photo.comments.build if user_signed_in?
       @comment_items = @photo.comments.paginate(page: params[:page])
    end 
    ...
end

views/photos/show.html.erb

...
<%= form_for @new_comment, html: {method: :post} do |c| %>
    <div class="row">
        <div class="span">
            <%= image_tag current_user.avatar.url(:thumb) %>
        </div>
        <div class="span">
            <p><%= c.label "Enter your comment here:" %></p>
            <p><%= c.text_area :comment_text %></p>
            <p><%= c.submit "Submit Comment", class: "btn btn-tiny" %></p>
        </div>
    </div>
<% end %>

So the problem is when I am writing the CommentsController, I do not know how to pass the photo's id (photo_id) from the previous "show" page to the create method. Is it only possible if a pass it thru via a hidden field in the form? Are there any more elegant way in Rails to do so? Besides, a point on security, if I retrieve the photo_id via a hidden field, will someone be able to write a specific "post" request so that it post comments to all photos in the album? (like spammer in the php's forum long time ago..)

Thank you for reading this and thanks in advance for answering!

1
you could nest the comments under the photo? that you would just call the route like new_photo_comment_path @photo, and your comments controller could just reference params[:photo_id]Doon
Hi @Doon, Thanks for answering. Do you mean nested_attributes like: accepts_nested_attributes_for :comments, reject_if: lambda {|a| a[:text].nil? && a[:score].nil?}, allow_destroy: true? I have that already but don't know how to create the path... I tried something like: put 'photos/:photo_id/comment' => 'comment#create', via: :post but got stuck.Quin
no, not accept nested_attributes_for. That would let you create the comments with the photo. you could use it but it isn't really required. I was thinking more nested resources. I'll post an answer.Doon

1 Answers

1
votes

take a look at the rails guides on nested resources http://guides.rubyonrails.org/routing.html#nested-resources

your routes would have something like this.

resources :photos do
  resources :comments
end

your photo controller would stay the same, but your comments controller would need to lookup the photo from the url

class CommentsController < ApplicationController
    before_action :get_photo
    def create
       @photo.comments.create(params.require(:comment).permit(:comment_text))
       ....
    end 
    ...

    def get_photo
      @photo =  Photo.find(params[:photo_id])
    end
end

and the photo view would have

<%= form_for [@photo,Comment.new], html: {method: :post} do |c| %>
    <div class="row">
        <div class="span">
            <%= image_tag current_user.avatar.url(:thumb) %>
        </div>
        <div class="span">
            <p><%= c.label "Enter your comment here:" %></p>
            <p><%= c.text_area :comment_text %></p>
            <p><%= c.submit "Submit Comment", class: "btn btn-tiny" %></p>
        </div>
    </div>
<% end %>

which should invoke the new_photo_comment_path

as for the user_id i would just grabbed that from the user_signed_in helper. Since you already have them logged in, no sense in passing it in from the web.