4

At the beginning let me excuse for easy question for those who are experienced but for me it is difficult right now. In the Rails in one of the views/_form.html.erb, I want to change line below (it works):

    <%= f.collection_select :user_id, User.all, :id, :email %>

into hidden field that will hold the id of the user that is logged in. I try to change it into:

    <% f.hidden_field :user_id, :id %>

but it throws an error:

    NoMethodError in Orders#new
    undefined method `merge' for :id:Symbol

Can sb help me to solve that?

7
  • 1
    big recommendation: Never trust users' input. Do a double-check in the controller to make sure the user's id given is matching the current user that is connected (or eventually directly set the user's id in the Controller instead of using a hidden field in a view) Commented Sep 3, 2015 at 20:58
  • 1
    Good point. I do not need to send it in the form. So following your suggestion and code from Rakibul's comment: @current_user = User.find(params[:user_id]), I should update the Controller. Commented Sep 3, 2015 at 21:06
  • So now I have the scenario that I have logged user, and he/she adds a new order. I need to assign this order a proper user_id. How to do that not sending it in a form? Commented Sep 3, 2015 at 21:11
  • You should not trust the parameters, params[:user_id] can be modified manually. You probably have a session between your app and the end-user, in this session is stored the User's id (usually, depending on your user-sessions solution). Use this to set the user_id of the object beeing created/updated. Something like Item.create(params[:item].merge(user_id: current_user.id)) Commented Sep 3, 2015 at 21:12
  • You can actually do: @current_user = User.find(session["user_id]") Commented Sep 3, 2015 at 21:13

3 Answers 3

1

Use this (if you already have current_user method available):

<%= f.hidden_field :user_id, :value => current_user.id %>

If you don't have current_user method implemented, in your corresponding controller, you can have something like this:

@current_user = User.find(params[:user_id])

Then, in your view, you can do:

<%= f.hidden_field :user_id, :value => @current_user.id %>

Update

After the above conversation, if you want to use session to store the user_id, then you can do something like this.

You can create a SessionsHelper module (which you can include in your ApplicationController) where you can define a log_in method:

  # Logs in the given user.
  def log_in(user)
    session[:user_id] = user.id
  end

(You can also put this: session[:user_id] = user.id in the create action where you create an user.)

You can also define a current_user method in this module:

  # Returns the current logged-in user (if any).
  def current_user
    @current_user ||= User.find_by(id: session[:user_id])
  end

Here are some other useful helper methods that you can add in this module:

  # Returns true if the given user is the current user.
  def current_user?(user)
    user == current_user
  end

  # Returns true if the user is logged in, false otherwise.
  def logged_in?
    !current_user.nil?
  end

  # Logs out the current user.
  def log_out
    session.delete(:user_id)
    @current_user = nil
  end

Finally, I would suggest you to take a look at Devise gem which is a very popular authentication solution for Rails application.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for pointing me to Devise. I checked it and thanks to current_user method I have access to user_id whenever I need it :)
Yeah. Devise is useful and has many helper methods that you would need. So why reinventing the wheel? :)
1

This error means that it expects a hash, but you are putting an empty symbol.

You need to send a hash

If you have the current_user method:

<%= f.hidden_field :user_id, :value => current_user.id %>

If you don't, you may use this in your controller

   id = User.find(someid)

And keep the same code in the view.

Look @ the documentation for more info

Comments

1

The first two answers are correct. Here's why

f.hidden_field is a method on the form object you created somewhere either in that partial or in a file/partial that includes it. The documentation linked by Hristo Georgiev is for the hidden_field method from ActionView::Helpers::FormHelper (link). The one you're trying to call is from ActionView::Helpers::FormBuilder (link)

hidden_field works just as you seem to be expecting it to, ie

hidden_field(:user_object, :id)

these are two different methods

f.hidden_field works a bit differently, because it already has access to some of the information used to build the hidden field. It expects a method to call on the form object and an optional hash which it converts to attributes on the hidden field (thus the :value => user.id hash)

Assuming you had the following form

form_for(@user) do |f|
  ...
end

And you wanted the id to be a hidden field, you would put this within that block

f.hidden_field(:id)

That would generate the following HTML

<input type="hidden" id="user_id" name="user[id]" value="1" />

See this line from ActionView

TL;DR

You're calling the FormBuilder#hidden_field method with the arguments expected by FormHelper#hidden_field

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.