Elixirでバリデーションを実装してみた

この記事は、[fukuoka.ex Elixir／Phoenix Advent Calendar 2020](https://qiita.com/advent-calendar/2020/fukuokaex) 14日目です。

前日は、 @piacerex さんの [[書評] プログラミングElixir第2版のインプレッションとアップデート](https://qiita.com/piacerex/items/810f407b838bad599e2f) でした

# Elixirでバリデーションを実装してみた:rainbow:

@tuchiro さんのこちらの記事の手順に沿って進めました。

<b>DBラッパーEctoのchangesetでお手軽入力チェック</b>
https://qiita.com/tuchiro/items/de56e38efa8c397abdff
（※環境構築のみ元記事のPhoenix 1.3から現行最新版のPhoenix 1.5に変更、以降は記事通りの手順です）

# 実装の概要:writing_hand:

- PhoenixのSSR（Server Side Rendering）であるEExでユーザー登録を行うWebアプリを実装する。
- Webアプリは、PhoenixのWeb CRUD自動生成コマンドで構築する
- DBはデフォルトのPostgreSQLを選択する。
- Ecto.Changesetで入力チェックを行う。

### バリデーション仕様は以下の通り

- 必須項目チェックの内容をカスタマイズする
- パスワードの桁範囲、許容文字チェックを行う（パスワードのハッシュ化については別の記事が参考になるので割愛）
- 電話番号が全角入力されたら自動的に半角変換する
- emailアドレスが正しい書式かフォーマットをチェックする
- コード値項目は事前に定義したメンバーと一致するか妥当性チェックを行う。
- メールアドレスを業務的なユニークキーとして、重複チェックを行う。

# 完成したもの:sparkles::sparkles:

以下コマンドで環境構築後、元記事の「depsにMojiexを追加」以降をそのまま進めると、バリデーションがかかるようになりました。

```sh
$mix phx.new ecto_changeset_sample
$cd ecto_changeset_sample
$mix ecto.create
```

下記の通り、不正な値を入力して、SAVEボタンを押下すると、バリデーションエラーが表示されます。

![スクリーンショット 2020-12-14 18.35.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148205/542836b1-23dc-2efa-c36e-9e71ee99d2e4.png)

# 実装箇所(changesetの内容):computer:

lib/ecto_changeset_sample/accounts/user.exをカスタマイズ

```elixir:
  @doc false
  def changeset(user, attrs) do
    user
    |> cast(attrs, [:name, :age, :email, :password, :phone_number, :grade, :resident_type])
    |> validate_required([:name, :age, :email, :password, :grade])
    |> validate_length(:password, min: 8, max: 16)
    |> validate_format(:password, ~r/\A(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\d)[a-zA-Z\d]/)
    |> validate_confirmation(:password, message: "does not match password")
    |> put_change(:phone_number, Mojiex.convert(attrs["phone_number"],{:ze,:he}))
    |> validate_length(:phone_number, min: 10, max: 11)
    |> validate_format(:phone_number, ~r/[0-9]{10,11}/)
    |> validate_format(:email, ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/)
    |> validate_inclusion(:grade, Map.values(grade()))
    |> validate_inclusion(:resident_type, Map.values(resident_type()))
    |> unique_constraint(:email)
  end
end
```

- validate_required は必須チェック(全項目必須適用になっている)
- validate_length で文字長の範囲チェック
- validate_format で正規表現チェック
- validate_confirmation で確認入力チェック（主にパスワード用）
- put_change を使えば、半角全角大文字小文字など文字揺らぎの自動変換も可能
- validate_inclusion で選択肢の妥当性チェック（数字の範囲などもチェック可能）
（tuchiroさんの記事より抜粋）

バリデーションは実装できましたが、大元のchangesetについては、
時間が足りず調べられなかった為、次の記事を読んで学びたいと思います。

<b>elixirschool チェンジセット</b>
https://elixirschool.com/ja/lessons/ecto/changesets/

# 手順以外で実施したこと:bulb:

## PostgreSQLインストール

[How to Install and Configure PostgreSQL Server on macOS Mac OS X](https://youtu.be/XbJx91en1HI?t=27)
↑↑こちらの動画は @piacerex さんにご紹介頂きました。
説明が丁寧でわかりやすく、導入後も使いやすいと思いました。

## Phoenix起動時、cryptoライブラリロードエラーが出た

次のエラーが表示されました。

![スクリーンショット 2020-12-14 21.53.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148205/bd6a1022-7f7c-9834-a08a-1114739053e2.png)
（エラーは、下記の @takasehideki さんの記事と同じものです。）
[macOS CatalinaでErlang 22.2がビルドできない時の対処法](https://qiita.com/takasehideki/items/2edacb7811521f71a9e7#%E5%85%B6%E3%81%AE%E5%A3%B1erlang-2231%E4%BB%A5%E9%99%8D%E3%81%AB%E3%81%99%E3%82%8B)

**【対処方法】** 以下のElixir/Erlangバージョンに上げた

- Elixir-1.11.2
- Erlang-23.1.5 

**【ポイント:bulb:】**
**エラーの原因は、環境の差異、Elixir／Erlangのバージョンが古い為によるもので、
数年前の記事などを参考にする場合は、新しいバージョンに更新した方が良いようです。**

asdfでerlang install,brew update失敗など、色々あり・・・
asdfをアンインストールして、brew自体を入れ直してなんとかなりました。
[asdfのアンインストール手順はこちら](https://asdf-vm.com/#/core-manage-asdf?id=remove)
[Homebrewのアンインストール手順はこちら](https://weblabo.oscasierra.net/homebrew-uninstall/)
[Phoenixのアンインストール/インストールの手順はこちら](https://qiita.com/piacerex/items/da66ae3de9b4b29c09dc)

## Node.jsインストール

- Node.jsインストール
　[nodejs.orgのサイトより](https://nodejs.org/ja/download/)
- cd assets
- npm install


## Ecto.Repoのコンパイルでエラーが出た

次のエラーが表示されました。
![スクリーンショット 2020-12-14 20.07.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148205/e2927632-a9e6-2769-f6da-39ae531b9fde.png)
**【対処方法】** 下記コマンドを実行

```sh
$export MIX_ENV=dev
$mix deps.get
$mix ecto create
```

**【ポイント:bulb:】**
**エラーの原因は、Ectoアダプターの設定が無いとのことで、Elixir PJの起動環境が、
以前実施したProdのままになっていたようなので、devに戻しました。**

## DB接続エラーが出た

次のエラーが表示されました。

```
DBConnection.ConnectionError
(Mix) The database for EctoChangesetSample.Repo couldn’t be created: killed`
```
</font>

**【対処方法】** 下記ウインドウを出し、PostgreSQLを起動

![スクリーンショット 2020-12-14 21.44.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148205/1dfeb8bd-49b1-a4e9-c00f-fb75b90c2de1.png)

**【ポイント:bulb:】**
**エラーの原因は、PostgreSQLが起動していなかった為で、起動したら、動くようになりました。**

## テーブル重複エラーが出た

![スクリーンショット 2020-12-15 0.19.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148205/248aa4a6-6951-ee2a-79be-28e3d7eed41c.png)


**【対処方法】** 下記コマンドを実行

```sh
$mix ecto.migrate --step 1
```

**【ポイント:bulb:】**
**エラーの原因は、PJを作り直す前にmigrateしてテーブル作成済のところに、再度migrateした為で、
migrateのステップを先頭からやり直したら上手くいきました。**

以下を参考にしました。

<b>mix ecto.migrate で重複エラーが起きた時のメモ【調査中】</b>
https://qiita.com/VA-11_Hall-A/items/a8fe72835108ea612d16

# 感想

環境の部分に時間を多く取られてしまった為、数をこなして、エラーの原因の切り分けをできるようになっていきたいと思いました。
チェンジセットや正規表現については、勉強して今後実装を試していきたいと思います。
環境により、結果やエラーの内容は変わると思いますが、こちらの記事がどなたかの参考になりましたら幸いです。

**最後に** 
@piacerex さん、今回環境周りで詰まっておりましたところ、丁寧に質問に答えてくださり、本当にありがとうございました。:bow:
Elixirコミュニティの皆さま、エリジョ(Elixir女子部)や勉強会にて、2020年は大変お世話になりました。
いつもご親切にありがとうございます。:relaxed:

明日は @piacerex さんの「[WSL2でElixir本体のビルドとiexバージョン変更](https://qiita.com/piacerex/items/7abdfbffcc10bb7106d3)」 です！ お楽しみに！:rocket: