%{"user" => user_params}, what does it do exactly?

I see this in a lot of function calls in Elixir / Phoenix controllers, but just am not sure what I am looking at exactly. I assume its a pattern match of some kind, but don’t see how or why the string “user” is involved and what it is doing exactly.

Example: (This is from generated 1.6 authentication code I believe)

def create(conn, %{"user" => user_params}) do
  case Accounts.register_user(user_params) do
    (try and register the user)
  end
end

Since all I see in router.ex is this black magic, I can’t tell how the function is getting called.

post "/users/register", UserRegistrationController, :create

And I also don’t see anything called “user” in the heex template. (will post if needed)

Thanks for the help!

You can take a look at your form and find any input name. It would be something like name="user[email]". That when translataded into a map for phoenix would be %{“user” => %{“email” => “value”}`.

What makes it being encapsulated? When you use any phoenix form helper such as form_for or <.form> you pass a for struct or atom name. If it’s an atom it’s going to encapsulate under that name, if it’s a ModelName it’s going to make a camel case model_name version for it.

When you submit the form, phoenix sends that action the payload as a Map so that’s why you see that format and the pattern matching on the function params is just phoenix telling “this is what I expect, if it’s something else I know it’s not going to work”

2 Likes

Two things to unpack here:

  1. Yes, stuff like %{"user" => user_params} is basically deconstructing a function argument:
map = %{"user" => user_params, "password" => "shhh_secret", "marketing_emails" => "true"}

do_stuff_with_params(map)

# ...

def do_stuff_with_params(%{"user" => user_params}) do
  IO.puts("User_params: #{inspect(user_params)}")
end
  1. As also explained by @lubien, the hierarchy of the maps your controllers receive from Phoenix is well-defined and can be easily found in the docs. But long story short, having this in your HTML:
<input type="checkbox" name="marketing[consent]">

…will lead to Phoenix giving you this as an argument to your controller’s function:

%{"marketing" => %{"consent" => value}}
4 Likes

Thanks for the explanation - that makes a lot of sense.

You can take a look at your form and find any input name. It would be something like name="user[email]"

I can’t find anything like that in my registration form, but this is old code that I am looking through and it’s possible I changed it (and broke it), and if so you just helped me fix it, which I definitely appreciate. Here is what my form looks like:

<.form let={f} for={@changeset} action={Routes.user_path(@conn, :do_register)}>
  <%= if @changeset.action do %>
    <div class="alert alert-danger">
       <p>Oops, something went wrong! Please check the errors below.</p>
    </div>
  <% end %>

  <%= label f, "Username" %>
  <%= text_input f, :name, required: true %>
  <%= error_tag f, :name %>

  <%= label f, :email %>
  <%= email_input f, :email, required: true %>
  <%= error_tag f, :email %>

  <%= label f, :password %>
  <%= password_input f, :password, required: true %>
  <%= error_tag f, :password %>

  <div>
    <%= submit "Register" %>
  </div>
</.form>

Thanks, that’s a very good explanation!

You should find it by inspecting the html source of your form in the browser.

To add to what @patrickdm said, the for={@changeset} is how it figures out what to call the map key (It’s a changeset for a User so it creates a "user" key).

1 Like

Thanks! I doubt I would have figured that out on my own.