PhoenixLiveViewを使用したmix phx.gen_authを詳しく見てみた【認証メール再送信編】②

# はじめに

この記事ではPhoenixのバージョン1.7.0を使用しています。

```bash
$ mix phx.new -v
Phoenix installer v1.7.0
```
[前回の記事](https://qiita.com/suzuki-t0505/items/5477e9b2cb6c172b778d)に引き続き、今回はアカウントの認証メール再送信を見ていきます。

以下はこのシリーズの目次です。
- [PhoenixLiveViewを使用したmix phx.gen_authを詳しく見てみた【登録〜認証編】①](https://qiita.com/suzuki-t0505/items/5477e9b2cb6c172b778d)
- PhoenixLiveViewを使用したmix phx.gen_authを詳しく見てみた【認証メール再送信編】②
- [PhoenixLiveViewを使用したmix phx.gen_authを詳しく見てみた【ログイン編】③](https://qiita.com/suzuki-t0505/items/207660d202d693502cdd)

## 目次

- [はじめに](#はじめに)
- [生成されたファイルの確認](#生成されたファイルの確認)
    - [認証メール再送信](#認証メール再送信)
- [終わり](#終わり)

## 生成されたファイルの確認

前回はアカウントの登録から認証するまでの生成されたファイルを確認しました。
今回は認証メールを再送信するための機能をまとめたファイルを確認します。

### 認証メール再送信

認証メール再送信をするためのurlは以下です。

[http://localhost:4000/accounts/confirm](http://localhost:4000/accounts/confirm)

![Screenshot from 2023-03-03 17-39-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646507/6ec14888-28ed-d935-1ccf-7f58533c302b.png)

認証メール再送信に該当するソースコードは以下です。

```elixir:auth_sample/lib/auth_sample_web/live/account_confirmation_instructions_live.ex
defmodule AuthSampleWeb.AccountConfirmationInstructionsLive do
  use AuthSampleWeb, :live_view

  alias AuthSample.Accounts

  def render(assigns) do
    ~H"""
    <.header>Resend confirmation instructions</.header>

    <.simple_form for={@form} id="resend_confirmation_form" phx-submit="send_instructions">
      <.input field={@form[:email]} type="email" label="Email" required />
      <:actions>
        <.button phx-disable-with="Sending...">Resend confirmation instructions</.button>
      </:actions>
    </.simple_form>

    <p>
      <.link href={~p"/accounts/register"}>Register</.link>
      |
      <.link href={~p"/accounts/log_in"}>Log in</.link>
    </p>
    """
  end

  def mount(_params, _session, socket) do
    {:ok, assign(socket, form: to_form(%{}, as: "account"))}
  end

  def handle_event("send_instructions", %{"account" => %{"email" => email}}, socket) do
    if account = Accounts.get_account_by_email(email) do
      Accounts.deliver_account_confirmation_instructions(
        account,
        &url(~p"/accounts/confirm/#{&1}")
      )
    end

    info =
      "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."

    {:noreply,
     socket
     |> put_flash(:info, info)
     |> redirect(to: ~p"/")}
  end
end
```

うえから順に確認します。

まずは`mount`を確認します。

```elixir:auth_sample/lib/auth_sample_web/live/account_confirmation_instructions_live.ex
def mount(_params, _session, socket) do
  {:ok, assign(socket, form: to_form(%{}, as: "account"))}
end
```

ソケットに`form`キーでアサインされている値は以下です。

```elixir
%Phoenix.HTML.Form{
  source: %{},
  impl: Phoenix.HTML.FormData.Map,
  id: "account",
  name: "account",
  data: %{},
  hidden: [],
  params: %{},
  errors: [],
  options: [],
  index: nil,
  action: nil
}
```

フォーム用の構造体をアサインしていることになります。

次に`render`を確認します。

```elixir:auth_sample/lib/auth_sample_web/live/account_confirmation_instructions_live.ex
def render(assigns) do
  ~H"""
  <.header>Resend confirmation instructions</.header>

  <.simple_form for={@form} id="resend_confirmation_form" phx-submit="send_instructions">
    <.input field={@form[:email]} type="email" label="Email" required />
    <:actions>
      <.button phx-disable-with="Sending...">Resend confirmation instructions</.button>
    </:actions>
  </.simple_form>

  <p>
    <.link href={~p"/accounts/register"}>Register</.link>
    |
    <.link href={~p"/accounts/log_in"}>Log in</.link>
  </p>
  """
end
```

ここで確認すべきはフォームで何を入力するかとフォームを送信した際にどのイベントを呼ぶのかです。
フォームで何を入力するかはメールアドレスです。送信先のイベントは`"send_instructions"`です。

次にフォーム送信先のイベント`"send_instructions"`を確認します。

```elixir:auth_sample/lib/auth_sample_web/live/account_confirmation_instructions_live.ex
def handle_event("send_instructions", %{"account" => %{"email" => email}}, socket) do
  if account = Accounts.get_account_by_email(email) do
    Accounts.deliver_account_confirmation_instructions(
      account,
      &url(~p"/accounts/confirm/#{&1}")
    )
  end

  info =
    "If your email is in our system and it has not been confirmed yet, you will receive an email with instructions shortly."

  {:noreply,
   socket
   |> put_flash(:info, info)
   |> redirect(to: ~p"/")}
end
```

フォームから受け取るパラメータは以下です。

```elixir
%{"account" => %{"email" => "taro@sample.com"}}
```

フォームから受け取ったメールアドレスでアカウントを取得します。

```elixir:auth_sample/lib/auth_sample_web/live/account_confirmation_instructions_live.ex
account = Accounts.get_account_by_email(email)
```

アカウントが取得できた場合は`Accounts.deliver_account_confirmation_instructions/2`関数を実行します。
`Accounts.deliver_account_confirmation_instructions/2`は[前回の記事](https://qiita.com/suzuki-t0505/items/5477e9b2cb6c172b778d#%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E7%99%BB%E9%8C%B2)で解説したので詳しい解説は省略します。
`Accounts.deliver_account_confirmation_instructions/2`は認証用のトークンを生成し、メールを送信する関数です。

メールが送信できたらhome（`http://localhost:4000`)にリダイレクトしメッセージを表示させます。

では、まだ認証をしていないアカウントのメールアドレスを入力して「Resend confirmation instructions」ボタンを押してフォームを送信をします。

![Screenshot from 2023-03-03 18-27-41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646507/015a4fdd-98b6-177e-ecb4-c94597530ff8.png)

送信できたら[http://localhost:4000/dev/mailbox](http://localhost:4000/dev/mailbox)にアクセスしてメールを確認します。

![Screenshot from 2023-03-03 18-28-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646507/784d36ce-6228-13f1-6788-305a429d5d5a.png)

これで[http://localhost:4000/accounts/confirm/xxx](http://localhost:4000/accounts/confirm/xxx)にアクセスして認証をすることができます。


## 終わり

今回もここまで読んで頂きありがとうございました。

次回は[ログイン](https://qiita.com/suzuki-t0505/items/207660d202d693502cdd)の部分について見ていきます。