Hello, and thanks for checking in. If you haven’t read the previous parts, you can find them here:

First of all, before we start working on the todos themself, we should allow users to delete their account (whether we like it or not, but also for learning purposes). Letting a user delete their own account is easy:

# web/controllers/user_controller.ex ......

def delete(conn, %{“id” => id}) do

user = Repo.get(User, id) cond do

user == Guardian.Plug.current_resource(conn) ->

case Repo.delete(user) do

{:ok, user} ->

conn

|> Guardian.Plug.sign_out

|> put_flash(:info, “Account deleted”)

|> redirect(to: page_path(conn, :index))

{:error, _} ->

conn

|> render(“show.html”, user: user)

end

end

end

......

First, we get the user’s id from the conn struct. We use the id to fetch the correct user from the database. For security purposes, we check if the current user is, in fact the user we got from the database. If so, we log the user off, redirect the user to the front page, and delete the user. If anything else gets pattern matched against our case, we’ll simply render the user :show template again.

Let’s add the button for deleting accounts in the user show template:

Sweet! This button link invokes our newly created :delete action in our user controller, and deletes the account.

We should also make it easier for our users to access their own personal information page, the :show action. Let’s open up the partial where our sign in and sign out buttons are written:

Cool! We change our current_user into a link, which invokes the :show action in our user controller. When a user clicks on their own email address, they get redirected to their personal information page.

Creating todos

Users are now able to sign up, log in and out, change their personal information, and delete their account. Pretty sweet, eh? I guess it’s time to start looking at how we can implement our todo items for this application.

If you’re familiar with Ruby on Rails, you’ve probably used the built in scaffold command that comes shipped with the framework. Believe it or not, Phoenix actually comes with a similar command. As with generating models and controllers, we can generate both, plus full templates and tests in one go. Let’s try it out:

~/todo$ mix phoenix.gen.html Todo todos title:string completed:boolean user_id:references:users

* creating web/controllers/todo_controller.ex

* creating web/templates/todo/edit.html.eex

* creating web/templates/todo/form.html.eex

* creating web/templates/todo/index.html.eex

* creating web/templates/todo/new.html.eex

* creating web/templates/todo/show.html.eex

* creating web/views/todo_view.ex

* creating test/controllers/todo_controller_test.exs

* creating priv/repo/migrations/20160606185749_create_todo.exs

* creating web/models/todo.ex

* creating test/models/todo_test.exs Add the resource to your browser scope in web/router.ex: resources "/todos", TodoController Remember to update your repository by running migrations: $ mix ecto.migrate



As we can see above, this command created a bunch of files for us. If we open up any of these files, we can see all the new, fresh scaffolding code, ready to be edited! Also, as told above, we need to add our todo resource to our routes. We’ll put them under the same scope as the users for now:

# web/router.ex ......

scope “/”, Todo do

pipe_through [:browser, :browser_auth]

resources “/users”, UserController, only: [:show, :index, :update,

:delete]

resources “/todos”, TodoController

end

......

Sweet. By putting it in this scope, we also ensure that our visitors are logged in before using the todo part of our application. Let’s migrate it into the database:

~/todo$ mix ecto.migrate

21:11:36.364 [info] == Running Todo.Repo.Migrations.CreateTodo.change/0 forward

21:11:36.364 [info] create table todos

21:11:36.409 [info] create index todos_user_id_index

21:11:36.413 [info] == Migrated in 0.4s

Now, when users are logged in, it would be stupid to redirect them to the user controller :index action. This is a todo application, so the visitors who signs up to our application probably want to add their own todo items right away. Let’s change the path for redirection in the session controller:

# web/controllers/session_controller.ex ......

def create(conn, %{“session” => %{“email” => user,

“password” => pass}}) do

case Todo.Auth.login_by_email_and_pass(conn, user, pass,

repo: Repo) do

{:ok, conn} ->

logged_in_user = Guardian.Plug.current_resource(conn)

conn

|> put_flash(:info, “Innlogget”)

|> redirect(to: todo_path(conn, :index)) #New redirect

{:error, _reason, conn} ->

conn

|> put_flash(:error, “Feil brukernavn/passord”)

|> render(“new.html”)

end

end

......

And also the user controller:

#web/controllers/user_controller.ex ......

def create(conn, %{“user” => user_params}) do

changeset = User.registration_changeset(%User{}, user_params)

case Repo.insert(changeset) do

{:ok, user} ->

conn

|> Todo.Auth.login(user)

|> put_flash(:info, “User created!”)

|> redirect(to: todo_path(conn, :index)) #New redirect

{:error, changeset} ->

conn

|> render(“new.html”, changeset: changeset)

end

end

......

Perfect. If we try to sign up or log in, we get redirected to our todo items. Try adding a couple todos to your list:

Awesome, we can add items to our list! However, if we log out and log into another account, the same items we just added are still on display, and we don’t have the user’s id in our User column. This is because the items doesn’t belong to any users.

# web/models/todo.ex ......

schema “todos” do

field :title, :string

field :completed, :boolean, default: false

belongs_to :user, Todo.User timestamps

end

......

If we take a look at our todo schema, we can see that our todo model is already set up to belong to a user. The first thing we need to do, is to make sure the relationship between user and todo goes both ways:

# web/models/user.ex ......

schema “users” do

field :email, :string

field :password, :string, virtual: true

field :password_hash, :string

has_many :todos, Todo.Todo # new line timestamps

end

......

Now, lets open up our todo controller, and make sure each todo item is referenced to the correct user when created:

# web/controllers/todo_controller.ex ......

def new(conn, _params) do

changeset = Guardian.Plug.current_resource(conn)

|> build_assoc(:todos)

|> Todo.changeset() render(conn, “new.html”, changeset: changeset)

end def create(conn, %{“todo” => todo_params}) do

changeset = Guardian.Plug.current_resource(conn)

|> build_assoc(:todos)

|> Todo.changeset(todo_params) case Repo.insert(changeset) do

{:ok, _todo} ->

conn

|> put_flash(:info, “Todo created successfully.”)

|> redirect(to: todo_path(conn, :index))

{:error, changeset} ->

render(conn, “new.html”, changeset: changeset)

end

end

......

build_assoc(struct, assoc, attributes \\ %{})

Builds a struct from the given assoc in model. If the relationship is a has_one or has_many and the key is set in the given model, the key will automatically be set in the built association

/ Ecto Documentation

First, we make the changeset ready for the :new action, and once created, we store the correct user’s id as a reference to the todo item.

We can see that the proper user id is stored with the todo, in the User column. Looks like we’ve got a healthy user and todo relationship!

Now, we shouldn’t really have every users todo items available to us, as they are private and only belongs to the user who created them. Lets make a private function to only retrieve our own todos:

# web/controllers/todo_controller.ex ......

defp my_todos(user) do

assoc(user, :todos)

end

......

def assoc(model_or_models, assoc) Builds a query for the association in the given model or models. Examples

In the example below, we get all comments associated to the given post:

┃ post = Repo.get Post, 1

┃ Repo.all assoc(post, :comments)

assoc/2 can also receive a list of posts, as long as the posts are not empty:

┃ posts = Repo.all from p in Post, where: is_nil(p.published_at)

┃ Repo.all assoc(posts, :comments) / Ecto Documentation

Cool. Let’s use this function in the index and show action of our todo controller :

# web/controllers/todo_controller.ex ......

def index(conn, _params) do

user = Guardian.Plug.current_resource(conn)

todos = Repo.all(my_todos(user))

render(conn, “index.html”, todos: todos)

end def show(conn, %{“id” => id}) do

user = Guardian.Plug.current_resource(conn)

todo = Repo.get!(my_todos(user), id)

render(conn, “show.html”, todo: todo)

end

......

This ensures us that only the user’s todo items are fetched from the database, and the only items available for us to display:

We’re going to round off for now. Next time, we’re going to discuss how we could mark a todo item as complete, and move it away from our main todo list. We are also going to have a look at our design.

You can find the fifth part here.

If you have any questions, please ask below.

And as always thank you for checking in, and have a wonderful day!

Until next time

Stephan Bakkelund Valois