How can i fix my commenting system from creating multiple comments? - Rails - ruby-on-rails

The problem is in replying, each time i submit a reply it a new reply and comment is created.
Here's my comments controller
def new
#comment = Comment.new
end
def create
#post = Post.find(params[:post_id])
if params[:parent_id].present?
#comment = Comment.find(params[:parent_id]).replies.new.(comment_params)
elsif params[:parent_id].blank?
#comment = #post.comments.new(comment_params)
#comment.user = current_user
end
if #comment.save
flash[:success] = 'Your comment was successfully added!'
redirect_to :back
else
render 'new'
end
end
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
if #comment.destroy
redirect_to :back
end
end
private
def comment_params
params.require(:comment).permit(:body, :parent_id)
end
end
and
and in my _comment.html.erb
<%= render comment.replies if comment.replies.any? %>
<%= form_for [comment.post, comment.replies.new] do |f| %>
<%= f.hidden_field :parent_id, :value => comment.id %>
<%= f.text_field :body %>
<%= f.submit 'reply', name: 'reply' %>
<% end %>
in my model
belongs_to :parent, class_name: "Comment"
has_many :replies, class_name: "Comment", foreign_key: :parent_id, dependent: :destroy
When the reply is submitted the a params[:parent_id] is passed.
The problem
If-Else statement is not working, I need it to know if a comment or a reply is submitted.
If it did work I don't know how to get the comment I'm creating the reply in.
Hope everything is clear, thanks.

Based on your form parent_id is coming under comment params, so it should be something like this
#post = Post.find(params[:post_id])
if params[:comment][:parent_id].present?
#comment = Comment.find(params[:parent_id]).replies.new.(comment_params)
elsif params[:comment][:parent_id].blank?
#comment = #post.comments.new(comment_params)
#comment.user = current_user
end
Hope it helps!

Related

Rails strong params not passing to object, can't create comment due to missing valid attributes

I'm trying to create a comment on a video show page. When I submit the form, rails gives me a flash notice: "User must exist, Video must exist". Not sure why my strong params aren't going through to the create method.
comments_controller.rb
def create
#user = current_user
#video = Video.find(params[:video_id])
#comment = Comment.new(comment_params)
#post = #video.post
if #comment.save
flash[:notice] = "Comment successfully created"
redirect_to post_video_path(#post, #video)
else
#errors = #comment.errors.full_messages.join(', ')
flash[:notice] = #errors
render :'videos/show'
end
private
def comment_params
params.require(:comment).permit(
:body,
:user,
:video
)
end
models/comment.rb
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :video
validates :body, presence: true
end
models/video.rb
class Video < ActiveRecord::Base
belongs_to :user
belongs_to :post
has_many :comments
end
views/videos/show.html.erb
<%= #video.title %>
<%= content_tag(:iframe, nil, src: "//www.youtube.com/embed/#{#video.embed_id}") %>
<%= link_to "Delete Video", post_video_path(#post, #video), method: :delete %>
<%= link_to('Back', user_post_path(#user, #post)) %>
<h3>Comments</h3>
<%= form_for [#video, #comment] do |f| %>
<%= f.label(:body, "Comment") %>
<%= f.text_area(:body) %>
<%= f.submit("Submit Comment") %>
<% end %>
<% unless #comments.nil? %>
<% #comments.each do |comment| %>
<%= comment.body %>
<%= comment.user %>
<% end %>
<% end %>
I tried adding this to the create method...
#comment.user = current_user
#comment.video = #video
That allowed the comment to save but instead of displaying the comment.body, it displayed the comment object. It still doesn't explain why the strong params aren't being passed.
This is probably a nested params issue and how you have the strong params defined. Check this answer for more information.
If you need to see what is in the params, insert a pry statement into the controller and inspect it there.
Good luck!
There are multiple things you should look at. I have made multiple changes to your code. Go through them.
In your videos/show.html.erb
<%= form_for Comment.new do |f| %>
<%= f.label(:body, "Comment") %>
<%= f.text_area(:body) %>
<%= f.hidden_field :video_id, :value => #video.id %>
<%= f.submit("Submit Comment") %>
<% end %>
Send video_id using hidden_field. Do not send current user id for security reasons. If you take current user id from form end user can easily edit your form in html and pass someone else's user id and this will be one of the easiest and major vulnerability.
In comments_controller.rb
def create
#comment = Comment.new(comment_params)
#comment.user = current_user # we are making sure that current_user is set to comment.
if #comment.save
flash[:notice] = "Comment successfully created"
redirect_to post_video_path(#comment.video.post, #comment.video)
else
#errors = #comment.errors.full_messages.join(', ')
flash[:notice] = #errors
render :'videos/show'
end
end
private
def comment_params
params.require(:comment).permit(:body, :user, :video_id)
# We are permitting video_id instead of video
end
I got this to work but I'm not sure if this addresses the security concerns that #Dinesh raised.
comments_controller.rb
def create
#user = current_user
#video = Video.find(params[:video_id])
#comment = Comment.new(comment_params)
#comment.user = #user
#comment.video = #video
#post = #video.post
if current_user == #video.user || current_user.admin
if #comment.save
flash[:notice] = "Comment successfully created"
redirect_to post_video_path(#post, #video)
else
#errors = #comment.errors.full_messages.join(", ")
flash[:notice] = #errors
render :"videos/show"
end
else
flash[:notice] = "Only OP or admin may comment"
render :"videos/show"
end
end
and
private
def comment_params
params.require(:comment).permit(
:body,
)
end

Having a comment obtain a community_id as well

I've tried figuring out the issue behind not being able to save the community_id I hope someone can help me with.
MODELS
User
has_one :profile
has_many :communities, dependent: :destroy
has_many :comment, dependent: :destroy
Community
extend FriendlyId
friendly_id :title, use: [:slugged, :finders]
has_many :comments
belongs_to :user
Comment
belongs_to :user
belongs_to :community
belongs_to :profile
CONTROLLERS
Communities
def index
#communities = Community.all
#comment = Comment.new
#comments = Comment.all
end
def create
#community = Community.new(community_params)
#community.user_id = session[:user_id]
if #community.save
flash[:notice] = "Post Created"
else
flash[:alert] = "Error post not created"
end
redirect_to "/"
end
def new
#community = Community.new
end
def edit
#community = Community.friendly.find(params[:id])
end
def show
#comment = Comment.new
#comments = Comment.all
#community = Community.friendly.find(params[:id])
#current_user = User.find(session[:user_id])
end
def update
#community = Community.friendly.find(params[:id])
if #community.update(params[:comment])
flash[:notice] = "post updated"
else
flash[:alert] = "post not updated"
end
end
def destroy
#community = Community.friendly.find(params[:id])
if #community.destroy
flash[:notice] = "Post was thrown away"
else
flash[:alert] = "Post was not deleted"
end
redirect_to "/"
end
private
def community_params
params.require(:community).permit(:user_id, :title, :bio)
end
Comments
def index
#comments = Comment.all
end
def create
#comment = Comment.new(comment_params)
#comment.user_id = session[:user_id]
if #comment.save && #comment.community_id
flash[:notice] = "Comment has been posted"
else
flash[:alert] = #comment.errors.full_messages
end
redirect_to :back
end
def new
#comment = Comment.new
end
def show
#comment = Comment.find(params[:id])
end
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
redirect_to :back
end
private
def comment_params
params.require(:comment).permit(:text, :user_id, :community_id, :profile_id)
end
VIEWS
comments/_new
<%= form_for #comment do |f| %>
<%= f.hidden_field :community_id %>
<%= f.text_area :text, placeholder: "Enter New Comment Here ...", :cols => 50, :rows => 3, :class => 'text_field_message', :id => 'new_comment' %>
<%= f.submit :class => 'new_comment_button' %>
<% end %>
Whenever I check my console I notice the user_id gets registered with the comment but community_id is nil. Could someone point me in the right direction or help me understand where I made an error. Thank you for all your help.
Firstly,
#app/models/user.rb
has_many :comments, dependent: :destroy #-> this should be plural.
You have no value in your community_id input attribute:
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def new
#comment = Comment.new
end
end
#app/views/comments/new.html.erb
<%= form_for #comment do |f| %>
<%= f.hidden_field :community_id, params[:community_id] %>
<% end %>
I am assuming you're passing your :community_id parameter through the routes (IE url.com/communities/:community_id/comments/new)
I would do the following:
#config/routes.rb
resources :communities do
resources :comments
end
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
def new
#comment = Comment.new
end
def create
#comment = Comment.new comment_params
#comment.save
end
private
def comment_params
params.require(:comment).permit(:text).merge(user_id: current_user.id, community_id: params[:community_id])
end
end
This will give you the ability to use the following view:
#app/views/comments/new.html.erb
<%= form_for #comment do |f| %>
<%= f.text_area :text, placeholder: "Enter New Comment Here ...", :cols => 50, :rows => 3, :class => 'text_field_message', :id => 'new_comment' %>
<%= f.submit :class => 'new_comment_button' %>
<% end %>

'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path while creating comments Rails

Alright guys, as one of my projects at Bloc I am creating a reddit clone. For this particular assignment, I am to create a feature where users can comment on each post (witch is already nested within each topic). However, I am coming across this error when I try to view a post that is associated with a topic.
ArgumentError in Posts#show
'nil' is not an ActiveModel-compatible object. It must implement :to_partial_path.
So far I have created comments controller, made the foreign key connection between comments and users, made the comments routes nested inside of the posts routes, created both a form partial for comment submission and a comment partial which is in post/show, and created a CommentPolicy in order to authorize users to create new comments.
I have checked my database, and comments do exist because I added them to my seed file. I suspect my error lies in my forms and my references to my partials, but I am a bit stumped. Any help here would be greatly appreciated. Thanks!
My code:
user.rb
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable
has_many :posts
has_many :comments
mount_uploader :avatar, AvatarUploader
def admin?
role == 'admin'
end
def moderator?
role == 'moderator'
end
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user
end
respective routes within routes.rb
resources :topics do
resources :posts, except: [:index] do
resources :comments, only: [:create]
end
end
comments_controller:
class CommentsController < ApplicationController
def index
end
def create
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:post_id])
#comments = #post.comments
#comment = current_user.comments.build(params[:comment])
#comment.post = #post
#new_comment = Comment.new
authorize #comment
if #comment.save
flash[:notice] = "Comment was saved"
else
flash[:error] = "There was an error saving this comment. Please try again."
end
end
end
posts_controller:
class PostsController < ApplicationController
def show
#post = Post.find(params[:id])
#topic = Topic.find(params[:topic_id])
#comment = Comment.find(params[:id])
authorize #comment
end
def new
#topic = Topic.find(params[:topic_id])
#post = Post.new
authorize #post
end
def create
#topic = Topic.find(params[:topic_id])
#post = Post.new(post_params)
#post.user = current_user
#post.topic = #topic
authorize #post
if #post.save
flash[:notice] = "Post was saved."
redirect_to [#topic, #post]
else
flash[:error] = "There was an error saving this post. Please try again."
render :new
end
end
def edit
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:id])
authorize #post
end
def update
#topic = Topic.find(params[:topic_id])
#post = Post.find(params[:id])
authorize #post
if #post.update_attributes(post_params)
flash[:notice] = "Post was updated."
redirect_to [#topic, #post]
else
flash[:error] = "There was an error saving the post. Please try again."
render :edit
end
end
private
def post_params
params.require(:post).permit(:title, :body, :image)
end
end
comments/_form.html.erb
<%= form for [topic, post, comment] do |f| %>
<div class="form-group">
<%= f.label :body %>
<%= f.text_area :body, rows: 8, class: 'form-control', placeholder: "Enter comment here" %>
</div>
<div class="form-group">
<%= f.submit "Add comment", class: "btn btn-success" %>
</div>
<% end %>
comments/_comment.html.rb
<%= comment.body %>
posts/show.html.erb
<h1><%= #post.markdown_title %></h1>
<div class="row">
<div class="col-md-8">
<small>
<%= image_tag(#post.user.avatar.tiny.url) if #post.user.avatar? %>
submitted <%= time_ago_in_words(#post.created_at) %> ago by
<%= #post.user.name %>
</small>
<p><%= #post.markdown_body %></p>
<small>
<%= image_tag #post.image_url%>
</small>
</div>
<div class="col-md-4">
<% if policy(#post).edit? %>
<%= link_to "Edit", edit_topic_post_path(#topic, #post), class: 'btn btn-success' %>
<% end %>
</div>
<%= render #comments %>
<% if policy(#comment).create? %>
<h4>New Comment</h4>
<%= render partial: 'comments/form', locals: { topic: #topic, post: #post, comment: #comment } %>
<% end %>
comment_policy
class CommentPolicy < ApplicationPolicy
def new
user.present?
end
def create
user.present?
end
end
comments within seed file
#Create Comments
100.times do
Comment.create!(
user: users.sample, # we have not yet associated Users with Comments
post: posts.sample,
body: Faker:: Lorem.paragraph
)
end
comments = Comment.all
The believe the error is in this line
<%= render #comments %>
in your posts/show.html.erb
You don't have #comments defined in show method of posts_controller. Try putting #comments = #post.comments in show method.
def show
#post = Post.find(params[:id])
#topic = Topic.find(params[:topic_id])
#comment = Comment.find(params[:id])
authorize #comment
#comments = #post.comments
end

Rails is looking for Update when it should be Create

I'm trying to make a comment form on my Collection show page. I'm a bit rusty with Rails and I'm not sure why this form isn't attempting to Create a comment and is instead returning the error
The action 'update' could not be found for CommentsController
The comments system works in console.
Here is my form
<%= form_for [#commentable, #comment] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.text_area :content %>
<%= f.submit "Comment", class: "btn btn-large btn" %>
<% end %>
My comment model
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
belongs_to :user
end
My collection model
class Collection < ActiveRecord::Base
has_many :comments, as: :commentable
end
My comments_controller
class CommentsController < ApplicationController
def create
#comment = #commentable.comments.new(comment_params)
if #comment.save
flash[:success] = 'Comment posted!'
redirect_to #commentable
else
flash[:notice] = "Error creating comment: #{#comment.errors}"
redirect_to #commentable
end
end
private
def comment_params
params.require(:comment).permit(:content, :commentable_type, :commentable_id, :user_id)
end
end
My collections_controller show action
def show
#collection = Collection.find(params[:id])
#commentable = #collection
#comments = #commentable.comments
#comment = Comment.new if user_signed_in?
end
You're not using #commentable in your form or controller, which may be causing your problem:
<%= form_for [#commentable, #comment] do |f| %>
And your controller action should look like this:
def create
#comment = #commentable.comments.new(comment_params)
Update: Then to load commentable based on resource:
before_filter: load_commentable
def load_commentable
resource, id = request.path.split('/')[1, 2]
#commentable = resource.singularize.classify.constantize.find(id)
end
Method courtesy Ryan Bates: http://railscasts.com/episodes/154-polymorphic-association-revised
Can you show the controller action that renders that forms? I think the problem is that #comment is already persisted/created so it tries to update it.
UPDATE:
I just noticed your show action. Yes the problem is that #comment is already persisted. Just change it to:
#comment = Comment.new if user_signed_in?

Creating an object from the show action of another object in Rails

So I have a fairly typical blog application with posts and comments.
Each comment belongs to one post
A post can have many comments.
Basically I want to add a form for comments to the show action for posts, without having post_id under attr_accessible in the comment model.
In my posts controller I have:
def show
#post = Post.find(params[:id])
#poster = "#{current_user.name} #{current_user.surname} (#{current_user.email})"
#comment = #post.comments.build( poster: #poster )
end
I'm not entirely sure what I should be doing in the comments controller (I'm not confident that the code above is right either if I'm honest). At the moment I have:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:post])
if #comment.save
redirect_to #post, notice: "Comment posted"
else
redirect_to #post, error: "Error!"
end
end
My routes:
resources :comments
resources :posts do
resources :comments
end
and finally the form:
<%= form_for #post.comments.build do |f| %>
<%= f.label :content, "WRITE COMMENT" %>
<%= f.text_area :content, rows: 3 %>
<%= f.hidden_field :post_id, value: #post.id %>
<%= f.submit "Post" %>
<% end %>
The problem here is that I have no way of passing my post_id from the show action of the posts controller to the create action of the comments controller. Any help is much appreciated. Thank you in advance!
Your posts controller looks fine... but assuming your routes looks like
resources :posts do
resources :comments
end
then your CommentsController#create should/could look like:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment])
if #comment.save
redirect_to #post, notice: "Comment posted"
else
redirect_to #post, error: "Error!"
end
end
And your form:
<%= form_for [#post, #comment] do |f| %>
<%= f.hidden_field :poster, value: #poster %>
<%= f.label :content, "WRITE COMMENT" %>
<%= f.text_area :content, rows: 3 %>
<%= f.submit "Post" %>
<% end %>
I will assume that your post model has_many comments and comment belongs_to post
than you in your routes file you can do something like this
resources :posts do
resources :comments
end
this will give you a url schem such as
/posts/:post_id/comments , allowing you to always have post_id of the comment parrent
The URL for your show post should be like post/show/(:id).
Now, in the comment form, you can place a hidden field, with value of params[:id].
hidden_field(:post_id, :value => params[:id])
When you submit your form, you can get the value of post_id using the hidden field.
def create
#comment = Comment.new(params[:comment])
#comment.post_id = params[:post_id]
if #comment.save
flash[:notice] = 'Comment posted.'
redirect_to post_path(#comment.post_id)
else
flash[:notice] = "Error!"
redirect_to post_path(#comment.post_id)
end
end

Resources