Creating Users

Now that things are working smoothly and safely, let’s integrate that new code with our web layer through the public API we expose through the Accounts context. We’ll need to replace Accounts.create_user with a function that performs proper account registration with passwords.

Since our UserController currently calls the Accounts.create_user function, we hope to get by with minimal changes. Our approach works fine but internally we are only using the base User.changeset which doesn’t yet include our password hashing.

At this point, we could change the Accounts.create_user function to use the registration_changeset. Instead, we will explicitly build a new function to manage registration details. We’ll maintain create_user to expose the details any other API will need to build users. So often, applications must create accounts to satisfy a number of different use cases, such as seeding example data, imports, or sending user invitations. For these cases, we want a workflow in our Accounts context that allows us to create a user without the full ceremony that end users use.

We will write a new Accounts.register_user function which wraps up all the details of end-user registration, while maintaining our API for simply creating a new user in the system. While we are at it, let’s add a function to expose the registration changeset in the User schema. Open up your Accounts context and add these functions:

 def​ change_registration(%User{} = user, params) ​do
  User.registration_changeset(user, params)
 end
 
 def​ register_user(attrs \ %{}) ​do
  %User{}
  |> User.registration_changeset(attrs)
  |> Repo.insert()
 end

Any view that requires the user passwords will need to use a specific changeset for registration. This new function isn’t very exciting, but as we grow our application, our purpose-built functions will pay dividends by simplifying interactions with our controller. For example, as soon as we’re ready to send welcome emails when a new user registers, we have a perfect place for this new code to live.

Next, we need to make a tiny change in the UserController. The create action must now call our new register_user function and the new action must pull in our registration changeset, like this:

 def​ new(conn, _params) ​do
  changeset = Accounts.change_registration(%User{}, %{})
  render(conn, ​"​​new.html"​, ​changeset:​ changeset)
 end
 
 def​ create(conn, %{​"​​user"​ => user_params}) ​do
 case​ Accounts.register_user(user_params) ​do
  {​:ok​, user} ->
  conn
  |> put_flash(​:info​, ​"​​#{​user.name​}​​ created!"​)
  |> redirect(​to:​ Routes.user_path(conn, ​:index​))
 
  {​:error​, %Ecto.Changeset{} = changeset} ->
  render(conn, ​"​​new.html"​, ​changeset:​ changeset)
 end
 end

In create we use pattern matching to pick off the user_params and pass them to our new Accounts.register_user function. If our registration was successful, we redirect as before. If not, we simply render the new template again, with the changeset, which now has the errors from our failed validations.

Finally, we need to make one small change to our new user registration form to accept the user password. Open up lib/rumbl_web/templates/user/new.html.eex and add the following code above the submit button:

 <div>
  <%= password_input f, ​:password​, ​placeholder:​ ​"​​Password"​ %>
  <%= error_tag f, ​:password​ %>
 </div>

Now, we’re ready to load the registration form at http://localhost:4000/users/new:

images/src/authentication/registration_form.png

You should be smiling now. Like plug pipelines, validations are a pipeline of functions that transform the changeset. Each validation is a step that transforms the changeset, explicitly tracking the changes and their validity. The actual change happens only when we call the repository in the context.

Now we should be able to visit http://localhost:4000/users/new and create new users with our registration changeset. We have a problem, though. Newly registered users are not automatically logged in, and users still can’t log in or log out at will.

We need to create an authentication service and make it available throughout our system. You’ve used plugs created by others, but for this job it’s time you learn to create your own. We’ll implement authentication as a plug. That way we can add it to a pipeline in our router so other controllers can use it as needed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.181.36