スポンサーリンク

2015年7月13日

[Elixir+Phoenix]Create login form

Goal

ログイン(サインイン)用のフォームを作成する。
Authenticationの処理を作成する。

Dev-Environment

OS: Windows8.1
Erlang: Eshell V6.4, OTP-Version 17.5
Elixir: v1.0.4
Phoenix Framework: v0.13.1
PostgreSQL: postgres (PostgreSQL) 9.4.4

Wait a minute

第八章は、ログインフォームとAuthenticationから始める。
難しい部分は後に回し、まずは表示部分から作成する。

Index

  1. Session Controller
  2. Login Form
  3. Authentication
  4. Extra

1. Session Controller

Rails Tutorialの例に倣い、
Sessionのコントローラを作成する。
各処理をRESTfulに対応させます。
  • サインインページ: new
  • サインイン: create
  • サインアウト: delete
それぞれのルーティングを追加。
ファイル: web/router.ex
scope "/", SampleApp do
  ...
  get "/signin", SessionController, :new
  post "/session", SessionController, :create
  delete "/signout", SessionController, :delete
end
ルーティングを確認。
>mix phoenix.routes
...
session_path  GET     /signin         SampleApp.SessionController.new/2
session_path  POST    /session        SampleApp.SessionController.create/2
session_path  DELETE  /signout        SampleApp.SessionController.delete/2
Sessionコントローラを作成。
ファイル: web/controllers/session_controller.ex
defmodule SampleApp.SessionController do
  use SampleApp.Web, :controller

  plug :action

  def new(conn, _params) do
    render conn, "login_form.html"
  end

  def create(conn, _params) do
    redirect(conn, to: static_pages_path(conn, :home))
  end

  def delete(conn, _params) do
    redirect(conn, to: static_pages_path(conn, :home))
  end
end
続いて、ビューと仮のテンプレートも作成。
ファイル: web/views/session_view.ex
defmodule SampleApp.SessionView do
  use SampleApp.Web, :view
end
ファイル: web/templates/session/login_form.html.eex
<div class="jumbotron">
  <h2>Sign in!!</h2>
</div>
実行して画面を確認する。
URL Example: http://localhost:4000/signin
Caution:
create、deleteアクションは、この段階では実行エラーが発生する。

2. Login Form

次はログインフォームを作成していく。
経験ある方は、
どのようなテンプレートになるか想像が付いているのではないだろうか?
ファイル: web/templates/session/login_form.html.eex
<h1>Sign in!!</h1>

<%= form_for @conn, session_path(@conn, :create), [name: :login_params], fn f -> %>
  <%= if f.errors != [] do %>
    <div class="alert alert-danger">
      <p>Oops, something went wrong! Please check the errors below:</p>
      <ul>
        <%= for {attr, message} <- f.errors do %>
          <li><%= humanize(attr) %> <%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="form-group">
    <label>Email</label>
    <%= text_input f, :email, class: "form-control" %>
  </div>

  <div class="form-group">
    <label>Password</label>
    <%= text_input f, :password, class: "form-control" %>
  </div>

  <div class="form-group">
    <%= submit "Submit", class: "btn btn-primary" %>
  </div>
<% end %>
画面を確認するとフォーム画面が表示されている。
Description:
この時点でSubmitのボタンを押下すると、
createアクションが実行され/homeへリダイレクトする。

3. Authentication

ログイン(サインイン)と切っても切り離せない認証を作成する。
集中力も切れてくる頃合いだろうが、もう少しお付き合い頂ければと思う。
まず行うのは、認証で使うEmailでDBからデータを取得できるようにすることだ。
自分で作成しないと、Repoでid以外からDBデータを取得できない。
(方法を知っている方がいましたらご教授頂ければ嬉しいです)
Ecto.Queryのインポートと関数を追加する。
ファイル: web/models/user.ex
import Ecto.Query
def find_user_from_email(email) do
  query = from user in SampleApp.User,
          where: user.email == ^email,
          select: user
  SampleApp.Repo.all(query) |> List.first
end
ファイル: web/controllers/session_controller.ex
認証を行う関数を定義する。
createアクションで認証の処理を行う。
ログインを確認する関数。
def login(email, password) do
  user = SampleApp.User.find_user_from_email(email)
  case authentication(user, password) do
    true -> {:ok, user}
    _    -> :error
  end
end
認証を行う関数。
def authentication(user, password) do
  case user do
    nil -> false
    _   -> Safetybox.is_decrypted(password, user.password_digest)
  end
end
createアクションでの処理。
成功と失敗で処理を分けている。
def create(conn, %{"login_params" => %{"email" => email, "password" => password}}) do
  case login(email, password) do
    {:ok, user} ->
      conn
      |> put_flash(:info, "User login is success!!")
      |> redirect(to: static_pages_path(conn, :home))
    :error ->
      conn
      |> put_flash(:error, "User login is failed!! email or password is incorrect.")
      |> redirect(to: session_path(conn, :new))
  end
end
Authenticationの処理だが、後の内容で再登場する予定である。
Plugに組み込んだ認証処理を行うため。
Sessionとの絡みがあるので今回は暫定的な処理として作成している。

4. Extra

以前レイアウトのヘッダーで、
sign inのリンクを作成していたことを覚えているだろうか?
忘れないうちにリンク先を#から書き換えます。
ファイル: web/templates/layout/header.html.eex
変更前:
<li><a href=#>Sign in</a></li>
変更後:
<li><a href="<%= session_path(@conn, :new) %>">Sign in</a></li>
それと、密かに用意していたUserControllerのauthenticationメソッドを削除します。
(不要でした申し訳ないです・・・しかもそのまま使えないバグのおまけ付き・・・笑えない)
ファイル: web/controllers/user_controller.ex
def authentication(email, password) do
  user = Repo.get(SampleApp.User, email)
  Safetybox.is_decrypted(password, user.password_digest)
end

Speaking to oneself

今回はここまでです。
次は、Phoenix - Guide Sessionsを参考にSessionについて学びます。
PhoenixでSessionを扱うためにはPlugを学ぶのが良いのですが、
機能を利用することを優先します。
正直な話、Plugは機能が多いので紹介するのは中々大変です。
機能の多さはドキュメントを見て頂ければ分かるかと(笑)
なので、Plugについて遊ぶ記事はまた別で作成します。
Plugを理解できれば、できる幅が広がると思います。

Bibliography

人気の投稿