My Rails app allows users to create "connections" that describes their relationship with other users. Users can comment on other users' blog posts (called "works" here) and, for each comment made on a blog post, I want to show the users' relationship to the author. I'm having trouble creating the instance variable in the works controller.
Here's what I have so far in the show action in the works controller:
class WorksController < ApplicationController
def show
#work = Work.find(params[:id])
#workuser = #work.user_id
#connections = Connection.where(user_id: #workuser, otheruser_id: UNKNOWN).all
#comment = #work.comments.build
#comment.user = current_user
#comments = #work.comments.order("created_at DESC").where(work_id: #work).all
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #work }
end
end
end
I need help with the #connections instance variable, specifically what to assign the otheruser_id: parameter. I know for a fact that this needs to be the user_id of the user who posted a comment. However, I'm stumped as to how to get this id.
Here are the model relationships:
work.rb- belonts_to :user, has_many :comments
user.rb- has_many :works, has_many :comments, has_many :connections
connection.rb- belongs_to :user
Please let me know if I can provide any other information. Any help will be greatly appreciated!
Thanks!!
EDIT: Simplified version of the view code that populates the comments (the user, the relationship to the author, and the comment content):
<% #comments.each do |comment| %>
<%= link_to comment.user.full_name, comment.user if comment.user %>,
<%= #connections.description %>
<%= #comment.content %>
<% end %>
Ill update yo on the instance variable once you answer my comment. But Bachan's answer should do it if its two way.
EDIT:
After what you said about one way relationships I think you should not create #connections instance variable.
Instead define a method in the user.rb model like this:
def get_connection otheruser
Connection.where(:user_id=>self.id,:otheruser_id=>otheruser.id).first
end
Then in the view.....
So you wanna display all the comments like:
Commentator Name
Connection between commentator and work author
comment content
Alright to do that you can do this:
<% #comments.each do |comment| %>
<%= link_to comment.user.full_name, comment.user if comment.user %>
<%= #work.user.get_connection(comment.user).description unless #work.user.get_connection(comment.user).nil? %>
<%= comment.content %>
<% end %>
Controller:
class WorksController < ApplicationController
def show
#work = Work.find(params[:id])
#workuser = #work.user_id
#comment = #work.comments.build
#comment.user = current_user
#comments = #work.comments.order("created_at DESC")
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #work }
end
end
end
Please have a try with
#connections = Connection.where("user_id = ? OR otheruser_id = ?", #workuser, #workuser)
Related
I am using gem devise for creating users profile
Each user can create a comment. I need to add the user name beside each comment something like this <%= #comment.user.name %>
in user.rb
has_many :comments, dependent: :destroy
in comment.rb
belongs_to :users
in comment controller
before_action :find_comment ,only:[:show,:update,:edit,:destroy]
def new
#user =User.find(params[:id])
#comment = #user.comments.build
end
def create
#user =User.find(params[:id])
#comment = #user.comments.build(comment_params)
#comment.user = current_user
if #comment.save
redirect_to doctor_path(:id => #user.id)
end
end
private
def find_comment
#comment = Comment.find(params[:id])
end
def comment_params
params.require(:comment).permit(:text)
end
user controller
def show
#user = User.find(params[:id])
end
user show.html.erb
<% for item in #user.comments %>
<% if item.text.present? %>
<%= item.text %><br>
<%= #comment.user.name %>
<br><hr>
<% end %>
I got this error
undefined method `user' for nil:NilClass
You could do it the other way around, in your show method:
#comments = Comment.all
in your show view:
<% #comments.each do |comment| %>
<%= comment.text %>
<%= comment.user.name %>
<% end %>
Since your question is not really clear I'll specifiy that if you want to show just the comments posted by the user:
def show
user_id = User.find(params[:id]).id
#comments = Comment.where(user_id: user_id)
end
Just some quick rules to start with
A user has many comments, this will be the relationship between the user and a comment that the user has made.
You already have this
A user has many profile comments, this is the relationship between a user and the comments that have been left for that user on their profile
Now you have that distinction things start to be come clearer.
Start by creating a single xref table to act as the go between users and comments that have been left for a profile and call it profile_comments
this profile_comments table needs a user_id and a comment_id of type integer to store the primary keys from user and comments tables, where the user_id is the id of the user that is having a comment left about them on their profile
You can now setup a profile_comment model that with the following relationships
belongs_to comment
belongs_to user
So now you need to change your user model relationships to the following
user.rb
has_many :comments
has_many :profile_comments, dependent: :destroy
comment.rb
belongs_to :user #not users as you have defined in your question
has_many :profile_comments, dependent: :destroy
and the new profile_comment.rb model needs the two belongs_to clauses for comment and user
profile_comment.rb
belongs_to :user
belongs_to :comment
Now when you create a comment you need to assign to to the user, and also to the profile_comment
So now your comments controller needs to setup these relationships so instead of
before_action :find_comment ,only:[:show,:update,:edit,:destroy]
def new
#user =User.find(params[:id])
#comment = #user.comments.build
end
def create
#user =User.find(params[:id])
#comment = #user.comments.build(comment_params)
#comment.user = current_user
if #comment.save
redirect_to doctor_path(:id => #user.id)
end
end
You need something like this
def create
#user =User.find(params[:id])
#comment = current_user.comments.build(comment_params)
#profile_comment = ProfileComment.new
#user.profile_comment < #profile_comment
#comment.profile_comment < #profile_comment
if #comment.save
redirect_to doctor_path(:id => #user.id)
end
end
Your update action will need to also change accordingly
Now in your view instead of
<% for item in #user.comments %>
<% if item.text.present? %>
<%= item.text %><br>
<%= #comment.user.name %>
<br><hr>
<% end %>
<% end %>
You want this, it's a little complex because you need to get from the profile comment to the comment then to the user that created the comment
<% #user.profile_comments.each do | profile_comment |%>
<%comment = profile_comment.comment%>
<% if comment.text.present? %>
<%= comment.text %><br>
<%if comment.user.blank?%>
No user assigned to this comment
<%else%>
<%= comment.user.name #or email or whatever%>
<%end%>
<br><hr>
<% end %>
<% end %>
Although text is a reserved word and is an actual column type so you might want to change the column name text to something else
Any questions on this feel free to get back to me but I won't be around for the next 24 hours. Hope it's clear and helps you understand what has gone wrong with your initial setup
In the controller and the view I have assumed that the current_user is the person making the comment and #user is the person that is being commented on. Just switch that round if I have that wrong
#user.name works on my posts, user profile, and everywhere else on my site. But when I add it to user comments, I get undefined method?
<p class="comment_body">
<h2><%= #user.name %></h2>
<%= comment.body %>
</p>
I tried <%= user.name %> and still an error.
I tried <%= current_user.name %> and it worked. However, I don't want to show the current user's name, I want to see the user's name of who posted the comment.
EDIT:
I forgot to associate user to comment, but I'm not really sure how.
Comments controller
class CommentsController < ApplicationController
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:body))
redirect_to post_path(#post)
end
end
I went into my comments.rb and wrote belongs_to :user and in my user.rb I added has_many :comments.
So, should you set the user and save the comment, shouldn't you? try something like
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment].permit(:body))
#comment.user = current_user
if #comment.save
redirect_to post_path(#post)
else
# something else
end
end
I'm working on an app that allows users to comment on a single "work" (think blog post). The associations in the models are as follows:
class User < ActiveRecord::Base
has_many :works
has_many :comments
class Work < ActiveRecord::Base
belongs_to :user
has_many :comments
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
belongs_to :work
In the comments table, a record has the following fields:
id
content
user_id
created_at
updated_at
work_id
In my Comments controller, I have the following Create action:
def create
#work = Work.find(params[:id])
#comment = #work.comments.create(params[:comment])
#comment.user = current_user
if #comment.save
#flash[:success] = "Post created!"
redirect_to root_url
else
render 'activities'
end
end
I'm trying to associate both the user AND the work to the comment but I get the following error message when I try to create a comment:
Unknown action
The action 'update' could not be found for CommentsController
I'm trying to use the following StackOverflow answer as a guide but the solution is not working for me:
Multiple Foreign Keys for a Single Record in Rails 3?
EDIT:
I have a add comment form on the works#show action:
def show
#work = Work.find(params[:id])
#comment = current_user.comments.create(params[:comment])
#activities = PublicActivity::Activity.order("created_at DESC").where(trackable_type: "Work", trackable_id: #work).all
#comments = #work.comments.order("created_at DESC").where(work_id: #work ).all
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #work }
end
end
The Comment form itself:
<%= form_for(#comment) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Post a comment!" %>
</div>
<%= f.submit "Post", class: "btn btn-small btn-primary" %>
<% end %>
I also have an update method on the Comments controller:
def update
#comment = current_user.comments.find(params[:id])
if #comment.update_attributes(params[:comment])
flash[:success] = "Comment updated"
redirect_to #comment
end
end
The error message says:
The action 'update' could not be found for CommentsController
So, the issue is that your form is trying to call an update action on the CommentsController. This is unrelated to adding both the User and the Work instance as foreign keys. Your code for that seems right.
For sure if you persist the comment to the database during the show action:
#comment = current_user.comments.create(params[:comment])
Then the form helper will build an update form rather than a create form (because the model already exists):
<%= form_for(#comment) do |f| %>
If the desired action is to POST a comment create from the show page then try building the comment in the show action:
#comment = current_user.comments.build(params[:comment])
Members create votes that both belong to them and to another model, Issues. Currently I'm doing this with a hidden form and passing the appropriate parameters. Here's the code on the issues index view:
<%= form_for(#vote) do |f| %>
<%= f.hidden_field "issue_id", :value => issue.id %>
<%= f.hidden_field "member_id", :value => session[:member_id] %>
<%= f.hidden_field "type", :value => :Upvote %>
<%= f.label issue.upvotes_count(issue.id) %>
<%= submit_tag "Up", :class => 'up-vote' %>
<% end %>
This doesn't seem ideal as it leaves issue_id and member_id open to mass assignment. Is there a better way to do this with a button_to tag or something?
Here's the controller code:
class VotesController < ApplicationController
#GET
def new
#vote = Vote.new
end
# POST
def create
#vote = Vote.new(params[:vote])
#vote.member_id = current_member
if #vote.save
redirect_to issues_path
else
redirect_to issues_path, notice: "you must be logged in to vote"
end
end
end
and
class IssuesController < ApplicationController
# GET
def index
#issues = Issue.find(:all)
#vote = Vote.new
end
# GET
def show
#issue = Issue.find(params[:id])
respond_to do |format|
format.html
format.js
end
end
end
Use scope in the controller:
#issue = Issue.find(params[:issue_id])
#vote = #issue.votes.new(params[:vote])
#vote.save
and do not pass member_id and issue_id to hidden fields.
If you have proper nested RESTful routes you should be able to get params[:issue_id] directly.
If issue and member_id are available before you vote.save! in the controller, you can set them manually there.
Normally you get values like member_id from current_user in the controller rather than passing it via form parameters. How you have it currently does expose you to mass-assignment.
Do members have to login before voting? If so, then you don't need to include member_id as a hidden field because you can grab current_user in the controller and this will provide good protection since there wouldn't be any advantage for a member to hack issue_id or type.
I'm trying to add comments to a Post model
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :user #should this be has_one :user instead?
....
How do I set up my Comment new and creation actions to get both current_user as well as the current post?
guides.rubyonrails.org suggested
Controller:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.create(params[:comment])
redirect_to post_path(#post)
end
View
<%= form_for([#post, #post.comments.build]) do |f| %>
...
However this only seems to be aiming to associate with the post and not also the user. How can I set up both associations?
I assume you have a current_user() method somewhere in your controller.
So this should do it:
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment])
#comment.user = current_user
#comment.save
redirect_to post_path(#post)
end
Deradon answered the question well but I prefer to have that logic within the new comment form itself. For example, instead of calling those variables you can have:
app/views/comments/_form.html.erb:
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.hidden_field :post_id, value: #post.id %>
This assumes of course that your new comment form is embedded within the 'post show' page, so that #post is available:
app/views/posts/show.html.erb:
<body>
<%= render #post %>
<%= render 'comments/_form' %>
</body>
This will add post_id and user_id directly into the db for a new comment. Also, don't forget to make an index for those foreign keys so the database has quicker access. If you don't know how, google it!