1

So I'm writing a quick async username validation check, and following the tutorial on PhoenixFrameworks website, I can do it like so:

def validateUsername(conn, %{"username" => user}) do
    IO.inspect(conn)
    query = from u in User,
      where: u.username == ^user,
      select: [u.username]

    case Repo.all(query) do
      [[username]] -> 
        conn
        |> json(%{ success: false, error: "Username has been taken" })
      [] ->
        conn
        |> json(%{ success: true })
      nil -> 
        conn
        |> json(%{ success: true })
      _ ->
        conn
        |> json(%{ success: false, error: "Internal Server Error"})
    end

But this doesn't quite make sense to me, as I've never dealt with a functional programming language, and I know the data binding with = in elixir works differently. But in my head I feel like it should be reversed like:

def validateUsername(conn, %{user => "username"})

Or something like that so my main question is

How does the %{"username" => user}) populate the user variable with the relevant information?

Datsik
  • 14,453
  • 14
  • 80
  • 121

2 Answers2

2

How does the %{"username" => user}) populate the user variable with the relevant information?

That's just how pattern matching in Elixir works. The key should match the key in the map, and the value should be a pattern which gets matched with the value of that key in the map. If it's a normal variable, it just gets assigned the value.

Also, assuming your database has a unique constraint on username in the users table, I would rewrite your original code to use Repo.get_by/2:

def validateUsername(conn, %{"username" = user}) do
  case Repo.get_by(User, username: user) do
    nil -> 
      conn
      |> json(%{ success: true })
    user -> 
      conn
      |> json(%{ success: false, error: "Username has been taken" })
  end
end

or

def validateUsername(conn, %{"username" = user}) do
  if Repo.get_by(User, username: user) do
    conn
    |> json(%{ success: false, error: "Username has been taken" })
  else
    conn
    |> json(%{ success: true })
  end
end
Dogbert
  • 212,659
  • 41
  • 396
  • 397
2

Pattern matching can be thought of as a thing that assigns all variables on the left side of expression if the right side has "the right shape".

{a, b} = {1, 2} # now a = 1, b = 2
%{"username" => user} = %{"username" => "Tomasz", "password" => "secret"} # now user = "Tomasz"
[a, b, c] = [:a, "b", 1] # a = :a, b = :b, c = 1
{a, b} = {1, 2, 3} # error

And it works for nested data too!

{[a, b, 3], {c, 5}} = {[1, 2, 3], {4, 5}}

There is a really comprehensive guide to pattern matching in "Programming Phoenix" which also includes pin operator

Community
  • 1
  • 1
tkowal
  • 9,129
  • 1
  • 27
  • 51