Elixir Digitalization Implementors/fukuoka.ex/kokura.exのpiacereです
ご覧いただいて、ありがとうございます
前回に引き続き、Phoenixユーザ認証ライブラリ「phx_gen_auth」のプロダクション向け改造ポイントとして、今回は、ページヘッダーの変更について解説します
phx_gen_auth用の各リンクをプルダウンメニュー化したり、ナビゲーションバーに配置するといったページデザインを行うとき、今回の内容が役に立つと思います

Elixir ranked second on the Qiita Advent calendar 
In the programming language category ranking, Rust was 1st, Elixir was 2nd, Golang was 3rd, and all were modern programming languages.
https://qiita.com/advent-calendar/2020/elixir
In addition, our Elixir community "fukuoka.ex" has won the top spot in the Web Technology category.
https://qiita.com/advent-calendar/2020/fukuokaex
本コラムの検証環境
本コラムは、以下環境で検証しています(Windowsで実施していますが、Linuxやmacでも動作する想定です)
- Windows 11
- Elixir 1.12.0 on WSL2 Ubuntu ※最新版のインストール手順はコチラ
- Phoenix 1.6.0 ※最新版のインストール手順はコチラ
- PostgreSQL 10.17 ※最新版のインストール手順はコチラ
なお、本コラム内で扱うPhoenix PJ名は「basic」、phx_gen_authのコンテキスト名は「Accounts」を前提にしています
③ページヘッダーの変更
③-1:ヘッダーの構成
未ログイン時のヘッダーは、下記の赤囲みのような出力を行います
ログイン済みの場合は、下記の緑囲みのような出力に切り替わります(ログアウトすると未ログイン時に戻ります)
この制御をしている対象ファイルは、以下の通りです
ファイルパス | 役割
-+-
lib/basic_web/templates/layout/_account_menu.html.heex | ログイン前/後のメニューリンクがある
lib/basic_web/templates/layout/root.html.heex | _account_menu.html.heexをrenderで読み込んでいる
各ファイルは、下記のような内容です
<ul>
<%= if @current_account do %>
<li><%= @current_account.email %></li>
<li><%= link "Settings", to: Routes.account_settings_path(@conn, :edit) %></li>
<li><%= link "Log out", to: Routes.account_session_path(@conn, :delete), method: :delete %></li>
<% else %>
<li><%= link "Register", to: Routes.account_registration_path(@conn, :new) %></li>
<li><%= link "Log in", to: Routes.account_session_path(@conn, :new) %></li>
<% end %>
</ul>
root.html.heex は、_account_menu.html.heex をrenderで読み込んでいます
<!DOCTYPE html>
…
<body>
<header>
<section class="container">
<nav>
<ul>
<li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
<%= if function_exported?(Routes, :live_dashboard_path, 2) do %>
<li><%= link "LiveDashboard", to: Routes.live_dashboard_path(@conn, :home) %></li>
<% end %>
</ul>
<%= render "_account_menu.html", assigns %>
…
③-2.調整例1:ヘッダーそのものを削除する
RegisterやLogin、メールアドレス表示などを、根本的にデザイン変更し、表示位置を変えたりしたい場合は、元々あるリンクを削除した方が都合が良いです
この対応は簡単で、root.html.heex の _account_menu.html をrenderしているところをコメントアウト or 削除するだけです
<!DOCTYPE html>
…
<body>
<header>
<section class="container">
<nav>
<ul>
<li><a href="https://hexdocs.pm/phoenix/overview.html">Get Started</a></li>
<%= if function_exported?(Routes, :live_dashboard_path, 2) do %>
<li><%= link "LiveDashboard", to: Routes.live_dashboard_path(@conn, :home) %></li>
<% end %>
</ul>
<%= # render "_account_menu.html", assigns # <-- comment-out here %>
…
もしくは、_account_menu.html の内容を空にするでもOKです
この通り、phx_gen_auth関連のリンクが、全て削除され、ここからのログインやユーザ登録等は一切できなくなります
なお、URL直打ちすれば、該当ページは利用できるので、ページの別場所からリンクを設定し、phx_gen_authの機能を各種ページ箇所にバラすことも可能です
一方、使用しないURLを機能させないようにしたい場合(後述のRegister削除など)は、下記のように、router.exからエントリーを削除します
defmodule BasicWeb.Router do
…
## Authentication routes
scope "/", BasicWeb do
pipe_through [:browser, :redirect_if_account_is_authenticated]
# v-- comment-out start
# get "/accounts/register", AccountRegistrationController, :new
# post "/accounts/register", AccountRegistrationController, :create
# get "/accounts/log_in", AccountSessionController, :new
# post "/accounts/log_in", AccountSessionController, :create
# get "/accounts/reset_password", AccountResetPasswordController, :new
# post "/accounts/reset_password", AccountResetPasswordController, :create
# get "/accounts/reset_password/:token", AccountResetPasswordController, :edit
# put "/accounts/reset_password/:token", AccountResetPasswordController, :update
# ^-- comment-out end
end
…
③-3.調整例2:ヘッダーからRegisterを削除する(招待制サイト向き対応)
たとえば、会員制サイトの中でも、招待制のサイトであれば、ユーザ自身によるユーザ登録を禁止している場合があります
一方、デフォルトのphx_gen_authは、ユーザ自身によるユーザ登録を許可しているため、都合が悪いです
しかも初期状態では、仮登録等を挟まないので、いきなりユーザ登録/ログインができてしまいます(なお、仮登録を有効にするカスタマイズは、次回以降のコラムで紹介予定です)
こういうケースでは、_account_menu.html.heex の方を直接いじって、Register リンクを削除します
<ul>
<%= if @current_account do %>
<li><%= @current_account.email %></li>
<li><%= link "Settings", to: Routes.account_settings_path(@conn, :edit) %></li>
<li><%= link "Log out", to: Routes.account_session_path(@conn, :delete), method: :delete %></li>
<% else %>
<!--<li>--><%= # link "Register", to: Routes.account_registration_path(@conn, :new) %><!--</li>-->
<li><%= link "Log in", to: Routes.account_session_path(@conn, :new) %></li>
<% end %>
</ul>
なお、HTML部分を削除するだけで無く、Registerのlink生成しているElixirコードもコメントアウト or 削除することをお忘れなく
これで、Registerリンクのみが消え、画面からのユーザ登録ができなくなります(URL直打ちも出来なくするには、router.ex 側も修正してください)
招待制のユーザ登録導線側の実装は、機会があれば、別途コラムで紹介します
最後に
今回は、「phx_gen_auth」のプロダクション向け改造ポイントの中でも、ナビゲーションとデザインに関係してくるページヘッダーの変更を行いました
ヘッダーの変更は、定義ファイルの位置さえ把握してしまえば、難しく無いかと思いますが、ページデザイン内にphx_gen_authの機能をバラす際には必須のテクニックとなります
Elixir/Phoenixで、会員制サイトのような、ユーザ認証付きWebサイトを開発する際は、phx_gen_authを活用するとハッピーになれますよ
なお、気が向いたら、上記を全てセッティングした会員制WebサイトのテンプレートをOSS化したいなぁって思っています … つまり、Elixir/Phoenixであれば、会員制Webサイトの構築をイチからやらなくて良くなります