【Phoenix1.4】（前編）パスワード認証＆リレーションあり「ブログチュートリアル」

（この記事は、**「[fukuoka.ex Elixir／Phoenix Advent Calendar 2018](https://qiita.com/advent-calendar/2018/fukuokaex)」**の2日目です）
昨日は[zacky1972](https://qiita.com/zacky1972)さんの[「ZEAM開発ログ2018年総集編その1: Elixir 研究構想についてふりかえる(前編)」](https://qiita.com/zacky1972/items/c9865f59259303d5f53e)です！こちらもぜひぜひ！

学生エンジニア**Koyo**です！
紅葉の季節ですね～

今までCakePHP, RailsからGoやVueまで色んな言語（フレームワーク）を触ってきました
フルスタックのフレームワークには大体「ブログチュートリアル」がありますよね

Elixirのフレームワークである[Phoenix](https://hexdocs.pm/phoenix/up_and_running.html)は、公式ドキュメントにはいくつかチュートリアルはあるのですが、日本語の情報が少ないです＞＜
（結構苦労して笑）先日パスワード認証＆リレーションありの「ブログチュートリアル」を自分でやってみたので、その過程をシェアしたいと思います

|> 後編はこちら
[【Phoenix1.4】（後編）パスワード認証＆リレーションあり「ブログチュートリアル」](https://qiita.com/koyo-miyamura/items/8b17893fb99b4ac71cec)

#|> 対象読者
**|> Elixir, Phoenixをインストールして少し動かしてみたけど、もうすこしステップアップしたい方**

特にfukuoka.ex代表のpiacereさんのコラムは入門に最適なので、まだやってない方は是非全6回やってみてください！

|> [Excelから関数型言語マスター1回目：行の「並べ替え」と「絞り込み」](https://qiita.com/piacere_ex/items/6714e1440e3f25fb46a1)

また、今回はつい先日リリースされた`Phoenix 1.4`でやってみようかなと思うので、以下のコラムも見てみるといいと思います！（1.3の場合は`Routes.user_path(...)`という記述の`Routes.`をすべて除けば動作すると思われます）

|> [Phoenix 1.4正式版① インストール編](https://qiita.com/piacere_ex/items/b63417540378ab9cbd18)

# |> ブログチュートリアル
## |> 筆者の実行環境
* Windows10
* WSL(Windows Subsystem for Linux)
* Erlang/OTP 21 
* Elixir 1.7.2 (compiled with Erlang/OTP 20)
* Phoenix1.4

動作未確認ですが他の環境でも（Macとか）でもおそらく大丈夫だと思います！

## |> プロジェクトを作成
`mix phx.new ex_blog` でex_blogという名前でプロジェクトを作成しましょう
※今回はwebpack使わないからといって`--no-webpack`つけたりすると、deleteメソッドが動かない罠にハマるので気を付けましょう（[筆者はハマった笑](https://twitter.com/KoyoMiyamura/status/1068668170200698881)）

以下のプロンプトが出たら`Y`で依存関係をインストールしましょう
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/932b7668-c49c-d724-0135-ee2f5089baa6.png)

以下の指示に従ってDBを生成しましょう

```bash
cd ex_blog
mix ecto.create
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/6939a44d-c11a-1a58-7107-e0a1ebea301c.png)

`iex -S mix phx.server`として以下が表示されればOKです！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/51f4bca5-df96-9442-1fb8-e1472570cb43.png)

ここで上手くいかない場合はPhoenixのインストールやDBの設定が上手くいっていない場合が考えられるので、以下を参考に見直してみましょう
[Excelから関数型言語マスター3回目：WebにDBデータ表示【PostgreSQL or MySQL編】](https://qiita.com/piacere_ex/items/a7558adc6856e3577dc6)

※ちなみに筆者はPostgreSQL起動し忘れていたので`sudo service postgresql start`しました笑

## |> Userリソースの追加
Accountsという名前でコンテキストを生成し、その中にUserリソースを追加します
Userはname, email, passwordカラムを持ち、emailは一意である（ユニーク制約）としましょう
`mix phx.gen.html Accounts User users name:string email:string:unique password:string`

ちなみにRails触っていた人はコンテキストの概念がよく分からないと思いますが、モデルのロジック相当と捉えるとしっくりくるかと思います（合ってるか分かりませんが筆者はそう解釈しています）
似たようなロジックは同じコンテキスト内にまとめていくとよいでしょう

以下のようにファイルがたくさんできます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/a50cd069-9e3e-69d9-9ca5-1c019bb99a45.png)

指示に従って`router.ex`に`resources "/users", UserController`を追加

```elixir:router.ex
defmodule ExBlogWeb.Router do
  use ExBlogWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", ExBlogWeb do
    pipe_through :browser

    get "/", PageController, :index
    resources "/users", UserController # ここ追加！
  end

  # Other scopes may use custom stacks.
  # scope "/api", ExBlogWeb do
  #   pipe_through :api
  # end
end

```

これで以下のようにRESTfulなルーティングが追加されますbb
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/76ebb666-3a28-6152-7643-b2bb9516f164.png)

## |> Articleリソースの追加
title, content属性を持つArticleリソースを作成しましょう
リレーションはこんな感じ

* User has many Articles
* Article belongs to user


以下のように書くとuser_idを外部キーとしてBlogコンテキストのArticleリソースを作成できます！
`mix phx.gen.html Blog Article articles user_id:references:users title:string content:string`

今回Articleリソースは、userリソースが属するAccountコンテキストと同じグループではないと考えられるので、新しくBlogコンテキストを作ってその中にいれてみました
例えばArticleに対するコメント機能などを作成する場合は、commentリソースはBlogコンテキストに属すると思います

`resources "/articles", ArticleController`をroute.exに追加しましょう
その後`mix ecto.migrate`してみましょう
※エラーになる場合は`mix ecto.reset`してみましょう

## |> DB周りの設定
### |> Migrationファイルをいじる
priv/repo 以下にタイムスタンプつきの`create_users.exs`ファイルができていると思います

```elixir:20181130....._create_users.exs
defmodule ExBlog.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :name, :string
      add :email, :string
      add :password, :string

      timestamps()
    end

    create unique_index(:users, [:email])
  end
end
```

DBレベルでnullを許容したくないので以下のように書き換えます

```elixir
defmodule ExBlog.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :name, :string, null: false # ここ追加
      add :email, :string, null: false # ここ追加
      add :password, :string, null: false # ここ追加

      timestamps()
    end

    create unique_index(:users, [:email])
  end
end
```

同様にarticleの方も

```elixir:20181130..._create_articles.exs
defmodule ExBlog.Repo.Migrations.CreateArticles do
  use Ecto.Migration

  def change do
    create table(:articles) do
      add :title, :string
      add :content, :string
      add :user_id, references(:users, on_delete: :nothing)

      timestamps()
    end

    create index(:articles, [:user_id])
  end
end
```

userリソースが削除されたら関連するArticleも削除してほしいので、以下のように書き換えます

```elixir
defmodule ExBlog.Repo.Migrations.CreateArticles do
  use Ecto.Migration

   def change do
    create table(:articles) do
      add :title, :string, null: false ## ここ変更
      add :content, :string
      add :user_id, references(:users, on_delete: :delete_all), null: false ## ここ変更

      timestamps()
    end

    create index(:articles, [:user_id])
  end
end
```

このあたりの設定は、Phoenixが依存している[EctoSQL](https://hexdocs.pm/ecto_sql/Ecto.Migration.html#references/2)に詳しく載っているので、カスタマイズしてみたい方は試してみてください！

最後に`mix ecto.reset`してmigrationを再設定しましょう

### |> Schemaファイルをいじる
自動生成されたSchemaファイルは以下のようになっていると思います

```elixir:ex_blog/lib/ex_blog/accounts/user.ex
defmodule ExBlog.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset


  schema "users" do
    field :email, :string
    field :name, :string
    field :password, :string

    timestamps()
  end

  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:name, :email, :password])
    |> validate_required([:name, :email, :password])
    |> unique_constraint(:email)
  end
end
```

Ecto（PhoexnixのORM, Railsで言えばActionRecord）でリレーションたどれるように設定していきます

```elixir
defmodule ExBlog.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset
  alias ExBlog.Blog.Article # ここ追加

  schema "users" do
    field :email, :string
    field :name, :string
    field :password, :string

    has_many :articles, Article # ここ追加
    timestamps()
  end

  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:name, :email, :password])
    |> validate_required([:name, :email, :password])
    |> unique_constraint(:email)
  end
end
```

Articleスキーマも同様に

```elixir:ex_blog/lib/ex_blog/blog/article.ex
defmodule ExBlog.Blog.Article do
  use Ecto.Schema
  import Ecto.Changeset


  schema "articles" do
    field :content, :string
    field :title, :string
    field :user_id, :id

    timestamps()
  end

  @doc false
  def changeset(article, attrs) do
    article
    |> cast(attrs, [:title, :content])
    |> validate_required([:title, :content])
  end
end
```

以下のように変更します

```elixir
defmodule ExBlog.Blog.Article do
  use Ecto.Schema
  import Ecto.Changeset
  alias ExBlog.Accounts.User # ここ追加

  schema "articles" do
    field :content, :string
    field :title, :string

    belongs_to :user, User # ここ追加
    timestamps()
  end

  @doc false
  def changeset(article, attrs) do
    article
    |> cast(attrs, [:title, :content])
    |> validate_required([:title, :content])
  end
end

```

### |> ここまでで画面の確認
さて、ここまででリレーションの土台ができました
これでArticleを投稿したときにログインしたユーザの`user_id`をカラムに追加するように設定したりできます

`iex -S mix phx.server`してlocalhost:4000にアクセスしてみましょう
`localhost:4000/users`
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/bc5b9eee-00b7-acb2-a56b-e7460162f308.png)

`localhost:4000/users/new`
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/5d70d0d2-4328-54d2-e73c-3e62a33643ea.png)

ここでパスワード入力する際にパスワードがガッツリ表示されています
また、indexやshowにパスワードが表示されるようになっています
これはあまり好ましくないので以下を参考に修正します

[Phoenixのphx.gen.htmlでpasswordフィールドを生成するときは忘れずにtemplateを変更しよう！](https://qiita.com/koyo-miyamura/items/2e5dad2e90bb2fce7945)

```diff:lib/ex_blog_web/templates/user/index.html.eex

<h1>Listing Users</h1>

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
-      <th>Password</th>

      <th></th>
    </tr>
  </thead>
  <tbody>
<%= for user <- @users do %>
    <tr>
      <td><%= user.name %></td>
      <td><%= user.email %></td>
-      <td><%= user.password %></td>

      <td>
        <%= link "Show", to: Routes.user_path(@conn, :show, user) %>
        <%= link "Edit", to: Routes.user_path(@conn, :edit, user) %>
        <%= link "Delete", to: Routes.user_path(@conn, :delete, user), method: :delete, data: [confirm: "Are you sure?"] %>
      </td>
    </tr>
<% end %>
  </tbody>
</table>

<span><%= link "New User", to: Routes.user_path(@conn, :new) %></span>

```

```diff:lib/ex_blog_web/templates/user/show.html.eex
<h1>Show User</h1>

<ul>

  <li>
    <strong>Name:</strong>
    <%= @user.name %>
  </li>

  <li>
    <strong>Email:</strong>
    <%= @user.email %>
  </li>

-  <li>
-    <strong>Password:</strong>
-    <%= @user.password %>
-  </li>

</ul>

<span><%= link "Edit", to: Routes.user_path(@conn, :edit, @user) %></span>
<span><%= link "Back", to: Routes.user_path(@conn, :index) %></span>

```

```diff:lib/ex_blog_web/templates/user/form.html.eex
<%= form_for @changeset, @action, fn f -> %>
  <%= if @changeset.action do %>
    <div class="alert alert-danger">
      <p>Oops, something went wrong! Please check the errors below.</p>
    </div>
  <% end %>

  <%= label f, :name %>
  <%= text_input f, :name %>
  <%= error_tag f, :name %>

  <%= label f, :email %>
  <%= text_input f, :email %>
  <%= error_tag f, :email %>

  <%= label f, :password %>
-  <%= text_input f, :password %>
+  <%= password_input f, :password %>
  <%= error_tag f, :password %>

  <div>
    <%= submit "Save" %>
  </div>
<% end %>
```

これで以下のように安全にパスワード入力ができます！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/03c2b620-a9de-73b6-ffcc-3236edf4ca34.png)


次は実際にリレーションを反映するためにログイン機能を実装しましょう

## |> Guardianでパスワード認証の実装
パスワード認証などを行うにはセッションを扱う必要があります
以下の分かりやすい記事のようにPhoenixのSession機能を使って実装する方法もありますが今回は[Guardian](https://hexdocs.pm/guardian/introduction-overview.html)というライブラリを使ってみます

* [Phoenixのログイン管理のためのSessionの使い方](https://qiita.com/sand/items/a88f1c39f3a6f7a01f99)

Guardianについては公式ドキュメントのほかに以下が参考になりました

* [Session Authentication Example For Phoenix 1.3 Using Guardian 1.0-beta](https://medium.com/@tylerpachal/session-authentication-example-for-phoenix-1-3-using-guardian-1-0-beta-a228c78478e6)
* [Elixir + Phoenix Frameworkにおけるユーザ認証](http://kray.jp/blog/elixir-phoenix-user-authentication/)

### |> Guardianと必要なライブラリの導入
以下を`mix.exs`に書いて、`mix deps.get`します

```elixir:mix.exs
      {:guardian, "~> 1.1"},
      {:comeonin, "~> 4.1"},
      {:bcrypt_elixir, "~> 1.1"}
```

次に`mix guardian.gen.secret`を実行して出てきた文字列を使って以下のように設定します

```diff:config/config.exs
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
#
# This configuration file is loaded before any dependency and
# is restricted to this project.

# General application configuration
use Mix.Config

config :ex_blog,
  ecto_repos: [ExBlog.Repo]

# Configures the endpoint
config :ex_blog, ExBlogWeb.Endpoint,
...

+ config :ex_blog, ExBlog.Accounts.Guardian,
+   issuer: "ex_blog",
+   secret_key: # mix guardian.gen.secret の結果を貼り付け

# Use Jason for JSON parsing in Phoenix
config :phoenix, :json_library, Jason

# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.eｚnv()}.exs"
```

#### |>ちょっと余談
ちなみに学習用は構いませんが本番環境でsecret_keyを直書きするのは良くないので、`.bashrc`に`export EX_BLOG_GUARDIAN_KEY=.....`などで環境変数をセットしておいて、以下のように書く方法もあります

```elixir
secret_key: "${EX_BLOG_GUARDIAN_KEY}"
```

このとき`System.get_env`を使う方法と`${}`を使う方法2通りあるんですが、System.get_envだと（後述するGigalixirなどで必要になる）`Distillery`などでコンパイルするときに、コンパイル時の実行時の環境変数で固定されてしまうので後から環境変数を注入できず不便です
`${}`だと現在の環境変数読んでくれるみたいなのでこっちがいいかもです

https://twitter.com/KoyoMiyamura/status/1056952578745892864
https://twitter.com/KoyoMiyamura/status/1056948597957156864

## |> Guardianを使ったログイン機能の実装
### |> ルーティング
まず最終的なルーティングを以下のようにします
２つのパイプライン:authと:ensure_authを定義します
:authはセッションを取得したりする機能を使うために必要で、これはログインしているユーザを判別するために必要なので全ページに適用します
:ensure_authはログイン済みかどうかを判定してくれて、userのcreate/new(ユーザ登録）以外のuserリソースとarticleリソースはログイン済みのみ閲覧可能にしたいので以下のように適用させます
またログイン/ログアウト用にSessionコントローラを新しく定義します

```diff:lib/ex_blog_web/router.ex
defmodule ExBlogWeb.Router do
  use ExBlogWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers
  end

  pipeline :api do
    plug :accepts, ["json"]
  end
  
+ pipeline :auth do
+   plug ExBlog.Accounts.Pipeline
+ end
+ pipeline :ensure_auth do
+   plug Guardian.Plug.EnsureAuthenticated
+ end

  scope "/", ExBlogWeb do
-    pipe_through :browser
+    pipe_through [:browser, :auth]

+     get "/", PageController, :index
+     get "/signin", UserController, :new
+     post "/signin", UserController, :create
+     get "/login", SessionController, :new
+     post "/login", SessionController, :create
+     delete "/logout", SessionController, :delete
+   end
-    resources "/users", UserController
+   scope "/", ExBlogWeb do
+     pipe_through [:browser, :auth, :ensure_auth]
+     resources "/users", UserController, except: [:new, :create]
    resources "/articles", ArticleController
  end

  # Other scopes may use custom stacks.
  # scope "/api", ExBlogWeb do
  #   pipe_through :api
  # end
end
```

### |> パイプラインの実装
先ほど紹介した参考記事やGuardianの公式を参考に実装していきます

```elixir:lib/ex_blog/accounts/guardian.ex
defmodule ExBlog.Accounts.Guardian do
  use Guardian, otp_app: :ex_blog
  alias ExBlog.Accounts
  def subject_for_token(user, _claims) do
    {:ok, to_string(user.id)}
  end
  def resource_from_claims(claims) do
    user = claims["sub"]
    |> Accounts.get_user!
    {:ok, user}
    # If something goes wrong here return {:error, reason}
  end
end
```

```elixir:lib/ex_blog/accounts/pipeline.ex
defmodule ExBlog.Accounts.Pipeline do
  use Guardian.Plug.Pipeline,
    otp_app: :ex_blog,
    error_handler: ExBlog.Accounts.ErrorHandler,
    module: ExBlog.Accounts.Guardian
  # If there is a session token, validate it
  plug Guardian.Plug.VerifySession, claims: %{"typ" => "access"}
  # If there is an authorization header, validate it
  plug Guardian.Plug.VerifyHeader, claims: %{"typ" => "access"}
  # Load the user if either of the verifications worked
  plug Guardian.Plug.LoadResource, allow_blank: true
end
```

```elixir:lib/ex_blog/accounts/error_handler.ex
defmodule ExBlog.Accounts.ErrorHandler do
  import Plug.Conn
  def auth_error(conn, {type, _reason}, _opts) do
    body = to_string(type)
    conn
    |> put_resp_content_type("text/plain")
    |> send_resp(401, body)
  end
end
```


### |> Accountsリソースにログイン機能の実装
コントローラで使いたいので、コンテキストにログイン機能を追加します
authenticate_userはログインemailとパスワードを受け取ってログイン可能かを判定します

check_passwordの定義がすごくElixirらしいですね！
第一引数がnilの場合とそうでない場合で処理を分けるためにパターンマッチを使って分けています
これにより一つの関数は小さく保ちつつ複雑な処理を記述することを可能にします

```diff:lib/ex_blog/accounts/accounts.ex
  alias ExBlog.Accounts.User
+  alias Comeonin.Bcrypt
...

+  def authenticate_user(email, plain_text_password) do
+    query = from u in User, where: u.email == ^email
+    Repo.one(query)
+    |> check_password(plain_text_password)
+  end
+  defp check_password(nil, _), do: {:error, "Incorrect username or password"}
+  defp check_password(user, plain_text_password) do
+    case Bcrypt.checkpw(plain_text_password, user.password) do
+      true -> {:ok, user}
+      false -> {:error, "Incorrect username or password"}
+    end
+  end
+ end	
end
```

### |> userスキーマに保存時にパスワードのハッシュ化と簡単なバリデーションの実装
userスキーマにパスワード保存時のハッシュ化処理を追加します
同名関数を定義して、パターンマッチするかどうかで処理を分岐させるやりかたはいかにもElixirらしいスタイルですね！

```diff:lib/ex_blog/accounts/user.ex
defmodule ExBlog.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset
  alias ExBlog.Blog.Article
+  alias Comeonin.Bcrypt

  schema "users" do
    field :email, :string
    field :name, :string
    field :password, :string

    has_many :articles, Article # ここ追加
    timestamps()
  end

  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:name, :email, :password])
    |> validate_required([:name, :email, :password])
    |> unique_constraint(:email)
+    |> validate_format(:email, ~r/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i)
+    |> validate_length(:password, min: 5)
+    |> put_pass_hash()
  end
+
+  defp put_pass_hash(%Ecto.Changeset{valid?: true, changes: %{password: password}} = changeset) do
+    change(changeset, password: Bcrypt.hashpwsalt(password))
+  end
+  defp put_pass_hash(changeset), do: changeset
end
```

layoutのViewファイルを変更して、eex中でcurrent_userという関数で現在ログイン中のユーザを取得できるようにします

```diff:lib/ex_blog_web/views/layout_view.ex
defmodule ExBlogWeb.LayoutView do
  use ExBlogWeb, :view
+  alias ExBlog.Accounts.Guardian
+  def current_user(conn) do
+    Guardian.Plug.current_resource(conn)
+  end
end

```

### |> 細かい部分の変更
user登録完了後にuser/showではなくpage/indexに飛ぶようにします（好みなので必須では無いです）

```diff:lib/ex_blog_web/controllers/user_controller.ex
  def create(conn, %{"user" => user_params}) do
    case Accounts.create_user(user_params) do
-      {:ok, user} ->
+      {:ok, _user} ->
        conn
        |> put_flash(:info, "User created successfully.")
-        |> redirect(to: Routes.user_path(conn, :show, user))
+        |> redirect(to: Routes.page_path(conn, :index))

      {:error, %Ecto.Changeset{} = changeset} ->
        render(conn, "new.html", changeset: changeset)
    end
  end
```

### |> sessionコントローラ/ビューの実装

```elixir:lib/ex_blog_web/controllers/session_controller.ex
defmodule ExBlogWeb.SessionController do
  use ExBlogWeb, :controller
  alias ExBlog.Accounts
  alias ExBlog.Accounts.User
  alias ExBlog.Accounts.Guardian
   def new(conn, _params) do
    changeset = Accounts.change_user(%User{})
    render(conn, "new.html", changeset: changeset)
  end
   def create(conn, %{"user" => %{"email" => email, "password" => password}}) do
    Accounts.authenticate_user(email, password)
    |> login_reply(conn)
  end
   defp login_reply({:error, error}, conn) do
    conn
    |> put_flash(:error, error)
    |> redirect(to: Routes.session_path(conn, :new))
  end
  defp login_reply({:ok, user}, conn) do
    conn
    |> put_flash(:info, "Welcome back!")
    |> Guardian.Plug.sign_in(user)
    |> redirect(to: "/")
  end
   def delete(conn, _) do
    conn
    |> Guardian.Plug.sign_out()
    |> put_flash(:info, "Logout successfully.")
    |> redirect(to: Routes.page_path(conn, :index))
  end
end
```

Contorllerには必ず対応するViewファイルが必要なので作成しましょう（ないとエラーになる）

Railsやったことある人はViewってerbテンプレートの置き場所だと感じるのですが、Phoenixの場合はコントローラから渡ってきた変数をビュー用に整形したり、表示用のhelper関数を定義する場所として使われるようです
Railsでいうhelperの役割と、コントローラ/テンプレート間の仲立ちをする層というイメージみたいです


ちなみにJSON APIを作る場合はここでレスポンスデータの定義をします

```elixir:lib/ex_blog_web/views/session_view.ex
defmodule ExBlogWeb.SessionView do
  use ExBlogWeb, :view
end
```

ログイン画面の実装

```elixir:lib/ex_blog_web/templates/session/new.html.eex
<h2>Login Page</h2>
<%= form_for @changeset, Routes.session_path(@conn, :create), fn f -> %>
   <div class="form-group">
    <%= label f, :email, class: "control-label" %>
    <%= text_input f, :email, class: "form-control" %>
    <%= error_tag f, :email %>
  </div>
  <div class="form-group">
    <%= label f, :password, class: "control-label" %>
    <%= password_input f, :password, class: "form-control" %>
    <%= error_tag f, :password %>
  </div>
  <div class="form-group">
    <%= submit "Submit", class: "btn btn-primary" %>
  </div>
<% end %>
```

ヘッダー部分にLogoutリンクを追加し、userがログインしている場合のみ表示するようにしてみましょう

```diff:lib/ex_blog_web/templates/layout/app.html.eex
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>ExBlog · Phoenix Framework</title>
    <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
  </head>
  <body>
    <header>
      <section class="container">
        <nav role="navigation">
          <ul>
-            <li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
+            <%= if current_user(@conn) do %>
+              <li><%= link "Logout", to: Routes.session_path(@conn, :delete), method: :delete %></li>
+            <% end %>
          </ul>
        </nav>
        <a href="http://phoenixframework.org/" class="phx-logo">
          <img src="<%= Routes.static_path(@conn, "/images/phoenix.png") %>" alt="Phoenix Framework Logo"/>
        </a>
      </section>
    </header>
    <main role="main" class="container">
      <p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
      <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
      <%= render @view_module, @view_template, assigns %>
    </main>
    <script type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>
  </body>
</html>

```

### |> ここまでで画面表示
さて、`iex -S mix phx.server`して`localhost:4000`を開いてみましょう
ログインしていない場合に、ログインが必要なリソースを表示させると以下のようにエラーページが表示されます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/3a9f99f0-02f2-e319-edc0-fdc7fdd7eeab.png)

`localhost:4000/signin`にアクセスし、適当なユーザを作成してみましょう
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/18ab1671-fa3c-3265-d855-f37a87d4bbaa.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/485be943-5530-a637-80d9-299d4d763881.png)

今回はユーザ登録完了後にログインを自動で行う実装ではないので、ログインを行う必要があります
（UX的にはユーザ登録後にログインした方がよさげ）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/721521d0-4f82-86b3-538b-810b72c9bbba.png)

ログインするとヘッダー部分にLogoutのリンクができていますね！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/8c6178d2-1d55-d920-9c30-65abef1e16cf.png)

`localhost:4000/users`にアクセスするとログイン前は見ることが出来なかったページが見れるようになっています
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/02e0f220-af5a-6932-98c1-64596a1cc3b7.png)

`localhost:4000/articles`も
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/d90173e5-8050-9ee0-ff31-4977327df108.png)

ヘッダのLogoutリンクをクリックすると
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/6adaf38f-7845-4112-c9d2-3474a76f6983.png)

フラッシュが表示されます
もう一度`localhost:4000/users`や`localhost:4000/articles`にアクセスしてみましょう
![image.png](https://qiita-image-store.s3.amazonaws.com/0/201242/c19208a7-02a1-a6e4-dc3e-34b5eccb7da2.png)

ちゃんとアクセスできなくなっています！すごい！笑

## |> ※続きはElixir Advent Calendarで！
若干睡眠不足で執筆していたので、今見返したらめちゃ長いですねこの記事・・・！
後半は分割・修正して[Elixir Advent Calandar](https://qiita.com/advent-calendar/2018/elixir) の方にしようと思います

|> 後編はこちら
[【Phoenix1.4】（後編）パスワード認証＆リレーションあり「ブログチュートリアル」](https://qiita.com/koyo-miyamura/items/8b17893fb99b4ac71cec)

## |> 次回
次回は[sym_num](https://qiita.com/sym_num)さんの[はじめての並行処理 -Queensパズルを題材にして-](https://qiita.com/sym_num/items/f9ea9d4672f804ced94f)です！こちらも是非どうぞ～