Couldn't find Subscriber with 'id'= - Rails Association - ruby-on-rails

Currently I have a Subscriber model and Comments model that belongs to Subscriber. Right now I need to link two models together so that my Subscriber has many comments on it. what I want is so that if I write this in the console I'll get my answer -> Subscriber.find(1).comments.first right now that returns nil because it doesn't know how to find the id of the subscriber that is leaving the comment. How can I give the application the proper code so that I can link the two? I'll post code for clarity.
CONTROLLER:
class CommentsController < ApplicationController
def new
#comment = Comment.new
end
def create
#subscriber = Subscriber.find(params[:subscriber_id])
#comment = #subscriber.comments.build(comments_params)
if #comment.save
flash[:notice] = "Thank you!"
redirect_to subscribers_search_path(:comments)
else
render "new"
end
end
private
def comments_params
params.require(:comment).permit(:fav_drink, :subscriber_id)
end
end
As you can see I'm trying to find the :subscriber_id when I create the comment. That is where my problem is. How can I connect it
ERROR:
MODELS:
class Comment < ActiveRecord::Base
belongs_to :subscriber
end
class Subscriber < ActiveRecord::Base
has_many :comments
end
Another aspect I should be clear about is that I have no current subscriber because this app is used for checking in a customer so the app does not log the user in it just checks them in with their phone number. Let me know if you need more info.
VIEWS:
<h1>new</h1>
<%= form_for #comment do |form| %>
<div class="form-group">
<p>
<%= form.label :fav_drink %>
<%= form.text_field :fav_drink %>
<%= form.hidden_field :subscriber_id %>
</p>
<%= form.submit "Send", class: "btn btn-primary" %>
</div>
<% end %>

Method 1
You subscriber id is inside the comment hash in your params. So you need to find subscriber like this
#subscriber = Subscriber.find(params[:comment][:subscriber_id])
#If you're taking this approach, you need to remove :subscriber id from your comment_params
Lik this
def comment_params
params.require(:comment).permit(:fav_drink)
end
##subscriber.comments.build will take care of the subscriber_id field for you, so its pointless rewriting it
Method 2
Or you directly create your comment.
#comment = Comments.new(comments_params)
#notice this already has, the subscriber_id, so we don't need to find
#subscriber and then do build on it
Let me know if that helps

Related

gem devise how to add comment created user email?

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

Parameter in find method

I'm following a guide to Ruby on Rails and there is something I don't understand. I have this model called Comment which belongs_to another two models, called User and Book.
This model's controller, Comments has the following create action:
def create
book = Book.find(params[:comment][:book_id])
comment = book.comments.build(comment_params)
comment.user = current_user
if comment.save
redirect_to comment.book
end
end
comment_params is just this:
def comment_params
params.require(:comment).permit(:body)
end
This create action is called when clicking the "Submit" button from this form, which is a partial called _comments located in the books view folder and rendered in the books' show action:
<%= form_for #book.comments.build do |f| %>
<%= f.hidden_field :book_id, value: #book.id %>
<%= f.text_area :body %>
<%= f.submit "Submit" %>
<% end %>
#book is indeed defined in books#show.
I don't understand why I have to pass the [:comment] parameter to the Book.find method in order to find the book. I thought just the [:book_id] would suffice.
You're not actually passing the :comment parameter but rather accessing the :book_id that's nested in the :comment hash. Your params look something like:
{
:comment => {
:book_id => 1
}
}
If you simply passed params[:book_id] you would get back nil.
If book_id is a field in comments table you don't need to retrieve the book. Just do
def create
comment = Comment.new(comment_params)
comment.user = current_user
if comment.save
redirect_to comment.book
end
end
Also, if the User model has a comments association, and in this action you are sure that a current_user is set, you can do
def create
comment = current_user.comments.build(comment_params)
if comment.save
redirect_to comment.book
end
end

Passing the value from a form_for to another user in Rails

So I want to add a feature in my app that allows a logged in user (first user) to go to another users page (second user) and send them a message. I want this message to be saved to a variable associated with the second user. Currently, when the first user sends the message it is being saved within their own user model instead of being passed to the second user. Please see my code below:
Controller
def create
#feedback = current_user.feedbacks.create(feedback_params)
##sender = #feedback.sender
#receiver = #feedback.receiver
if #feedback.save!
flash[:success] = "Feedback sent!"
redirect_to root_url
else
render 'new'
end
end
def new
#feedback = Feedback.new
#feedback_receiver= #feedback.receiver
end
def view
end
def destroy
end
def index
end
private
def feedback_params
params.require(:feedback).permit(:content, :receiver_id, :user_id)
end
end
Form_for
<% provide(:title, "Give feedback") %>
<h1> Give feedback </h1>
<div class="row">
<div class= "col-md-6 col-md-offset-3">
<%= form_for(#feedback) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "How can I improve myself?", object: f.object %>
<%= f.hidden_field(:receiver_id, #feedback.receiver_id) %>
</div>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
As per the line mentioned below as quoted from post:
Currently, when the first user sends the message it is being saved
within their own user model instead of being passed to the second
user.
Seems like you want to store the messages corresponding to reciever(second user) as well.
In such a case modify your feedbacks table to have a sender_id as well. Currently you are only having only user_id (assuming based on the line current_user.feedbacks.build)
Thus there will be two associations in user.rb to feedbacks.
has_many :feebacks_sent, class_name: "Feedback", foreign_key: :sender_id
has_many :feedbacks_recieved, class_name: "Feedback", foreign_key: :reciever_id
So, now when first_user sends messages to second_user then feedbacks table will have following entries apart from the one you need as per application requirement:
reciever_id: "One to which the message is being sent"
sender_id: "One who is sending the message"
message: "Text which is being sent"
Also modify the feedback params as now you be needing to permit as the receiver_id
def feedback_params
params.require(:feedback).permit(:content,:receiver_id)
end
Hope it answers the question.

Allowing user to favorite a post

I'm working on allowing a user to favorite a post.
I've created a model called favorite.
belongs_to :user
belongs_to :post
it stores the user_id and post_id.
I've also created a FavoritesController
class FavoritesController < ApplicationController
def create
#post = Post.find(params[:post_id])
current_user.favorite(#post)
end
def destroy
#post = Post.find(params[:id])
current_user.unfavorite(#post)
end
end
the form I have on my Posts#index is:
<%= form_for current_user.favorites.build do |favorite| %>
<%= hidden_field_tag :post_id, f.id %>
<%= favorite.button do %>
<i class="fa fa-star-o"></i>
<% end %>
<% end %>
my user model looks like this:
# Favorites a post.
def favorite(post)
favorite.create(post_id: post)
end
# Unfavorites a post.
def unfavorite(post)
favorite.find_by(post_id: post).destroy
end
when I try to click on favorite I get:
wrong number of arguments (0 for 1)
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"HjiANQUqTQVEqy0yzfLFMlnC8RsTiY5kVlvIUnD5OSIaSYSi4ELSuC95vRMIBA/6W+KvzCWMMXQ==",
"post_id"=>"7",
What am I doing wrong here? Also is there a better way to do this?
If you're just trying to get a 'voting/favoriting' system working, I suggest using a gem like https://github.com/ryanto/acts_as_votable or something of the like.

Best practice in creating belongs_to object

Let's say we have the following situation:
class User < ActiveRecord::Base
has_many :tickets
end
class Ticket < ActiveRecord::Base
belongs_to :user
end
For simplicity let's say Ticket has only some text field description and integer user_id. If we open User's views/users/show.html.erb view and inside User controller we have this code which finds correct user which is selected:
def show
#user = User.find(params[:id])
end`
Now inside that show.html.erb view we also have small code snipped which creates user's ticket. Would this be a good practice in creating it?
views/users/show.html.erb
<%= simple_form_for Ticket.new do |f| %>
<%= f.hidden_field :user_id, :value => #user.id %>
<%= f.text_area :description %>
<%= f.submit "Add" %>
<% end %>
controller/tickets_controller.rb
def create
#ticket = Ticket.new(ticket_params)
#user = User.find(ticket_params[:user_id])
#ticket.save
end
def ticket_params
params.require(:ticket).permit(:user_id, :description)
end
So, when we create a ticket for user, ticket's description and his user_id (hidden field inside view) are passed to tickets_controller.rb where new Ticket is created.
Is this a good practice in creating a new object which belongs to some other object? I am still learning so I would like to make this clear :) Thank you.
You should be able to do something like this in your form:
<%= f.association :user, :as => :hidden, :value => #user.id %>
This will pass user_id through your controller to your model and automatically make an association. You no longer need the #user= line in your controller.
Don't forget that the user could modify the form on their end and send any id they want. :)
See https://github.com/plataformatec/simple_form#associations for more info.
How about getting the user from the controller using current_user so that you protect yourself from anyone that would manipulate the value of the user_id in the form. Also I think this way is much cleaner
views/users/show.html.erb
<%= simple_form_for Ticket.new do |f| %>
<%= f.text_area :description %>
<%= f.submit "Add" %>
<% end %>
controller/tickets_controller.rb
def create
#ticket = Ticket.new(ticket_params)
#ticket.user = current_user
#ticket.save
end
def ticket_params
params.require(:ticket).permit(:user_id, :description)
end

Resources