Elixir入門からPhoenixでCRUD + SPAまで

この記事は「Elixir Advent Calendar 2023　9日目の記事です

piyoyo_exのMotzです。
普段はTypeScript,React,Go,Pythonなどを書くことが多いです。
今回Elixirに入門してPhoenixで簡単なSPAを作成したので、その際の開発体験や感想を共有できればと思います。

## Elixir,Phoenixについて
#### Elixir 
> Elixir (エリクサー) は並行処理の機能や関数型といった特徴を持つ、Erlangの仮想マシン (BEAM) 上で動作するコンピュータプログラミング言語である。ElixirはErlangで実装されているため、分散システム、耐障害性、ソフトリアルタイムシステム等の機能を使用することができるが、拡張機能として、マクロを使ったメタプログラミング、そしてポリモーフィズムなどのプログラミング・パラダイムもプロトコルを介して実装されている
[wikipedia Elixir](https://ja.wikipedia.org/wiki/Elixir_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E))

並行処理、関数型、分散システム、メタプログラミング...なにやら好奇心をそそられる単語が並んでいますね🤔
そして登場時期は2012年　新しいです!

#### Phoenix

>Phoenixは、「開発者の高い生産性」と「高いアプリケーションパフォーマンス」を両立します。 また、リアルタイム機能を実装するためのチャネルや、非常に高速なプリコンパイル済みテンプレートなど、いくつかの興味深い新しい工夫が施されています。
[Phoenix v1.6 hexdocs 日本語訳](https://zenn.dev/koga1020/books/phoenix-guide-ja-1-6/viewer/overview)

開発生産性とパフォーマンスの両立とは夢が詰まってますね🥳

**現在Elixir Advent Calendar 2023も盛りあがっています!**

https://qiita.com/advent-calendar/2023/elixir

## 動作環境

Elixirは1.15.7,Poenixは1.7.10を使用しています。
```bash
elixir -v
#Elixir 1.15.7 (compiled with Erlang/OTP 26)
```

```elixir:mix.exs
{:phoenix, "~> 1.7.10"},
```

## Hello Phoenix !

今回はDBの設定や起動の手間を省くためにやSqlite3を使用しますが、Postgres、Mysqlを使用する場合は以下のガイドを参考してください。

https://hexdocs.pm/phoenix/up_and_running.html

### sqlite3を使用する場合

`mix phx.new <アプリ名>　`
でPhoenixでphoenixアプリケーションを作成できます。以下実行例
```bash
mix phx.new hello

* creating hello/lib/hello/application.ex
* creating hello/lib/hello.ex
* creating hello/lib/hello_web/controllers/error_json.ex
~~~~~~~~~~~~~~~~~~略~~~~~~~~~~~~~~~~~~~~~~
* extracting hello/assets/vendor/heroicons/optimized

Fetch and install dependencies? [Yn] Y
* running mix deps.get
* running mix assets.setup
* running mix deps.compile

We are almost there! The following steps are missing:

    $ cd hello

Then configure your database in config/dev.exs and run:

    $ mix ecto.create

Start your Phoenix app with:

    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server
```

その後は表示されているコマンドを順に実行し、DBのためのファイル作成とサーバを起動します。

```bash
cd hello
mix ecto.create
iex -S phx.server #インタラクティブシェルを開きながらサーバーを起動します
```
起動できたら
[http://localhost:4000](http://localhost:4000)へアクセスしましょう。
何回やっても初めてのHello Worldはドキドキですよね!

![スクリーンショット 2023-12-09 3.21.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708210/2675fb3a-a180-2618-137b-7674fefc2d30.png)

## PhoenixでCRUD + SPA

https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Live.html

PhoenixのScaffoldingは以下のパターンで使用できます。
`mix phx.gen.live <コンテキスト> <スキーマ> <テーブル名> <カラム名>:<型> ...`

```bash

mix phx.gen.live Accounts User users name:string age:integer

* creating lib/hello_web/live/user_live/show.ex
* creating lib/hello_web/live/user_live/index.ex
* creating lib/hello_web/live/user_live/form_component.ex
* creating lib/hello_web/live/user_live/index.html.heex
* creating lib/hello_web/live/user_live/show.html.heex
* creating test/hello_web/live/user_live_test.exs
* creating lib/hello/accounts/user.ex
* creating priv/repo/migrations/20231208193941_create_users.exs
* creating lib/hello/accounts.ex
* injecting lib/hello/accounts.ex
* creating test/hello/accounts_test.exs
* injecting test/hello/accounts_test.exs
* creating test/support/fixtures/accounts_fixtures.ex
* injecting test/support/fixtures/accounts_fixtures.ex

Add the live routes to your browser scope in lib/hello_web/router.ex:

    live "/users", UserLive.Index, :index
    live "/users/new", UserLive.Index, :new
    live "/users/:id/edit", UserLive.Index, :edit

    live "/users/:id", UserLive.Show, :show
    live "/users/:id/show/edit", UserLive.Show, :edit


Remember to update your repository by running migrations:

    $ mix ecto.migrate
```
phx.gen.liveの結果に記述の通り`lib/hello_web/router.ex`ファイルを編集して、migrateしましょう。

```elixir:lib/hello_web/router.ex
~~~~~~~
  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", HelloWeb do
    pipe_through :browser
    live "/users", UserLive.Index, :index
    live "/users/new", UserLive.Index, :new
    live "/users/:id/edit", UserLive.Index, :edit

    live "/users/:id", UserLive.Show, :show
    live "/users/:id/show/edit", UserLive.Show, :edit

    get "/", PageController, :home
  end
~~~~~~~
```
```bash
mix ecto.migrate
phx server
```

ここまで実行すれば[http://localhost:4000/users](http://localhost:4000/users)
にアクセスしましょう。心ゆくまでSPAを堪能してください

![スクリーンショット 2023-12-09 4.43.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708210/1157b5aa-3a23-152d-eef7-a4800196ca3f.png)


## Elixir + Phoenixのここがスゴイ

主に開発していて良いと感じた部分です。Elixirの並行処理や高可用性などパフォーマンスに関する非機能要件は含んでいません(ここも良いらしい)。

- **mixが標準搭載**: 他の言語ではパッケージ管理ツールが別になっているものも多いですよね。特にnpm、pipなどで入門者はつまづきがちですよね。
- **Phoenix LiveView**: リアルタイムなWeb UI を作成できるPhoenixの機能です。Webosocketプロトコルを使用しているので一般的にhttpよりも少ないヘッダーで通信できます。フロントでの記述量を大幅に減らすことができて最高です⚙️。
- **Scaffolding**: Railsにもあるやつです。簡単なコマンドで基本的なCRUD操作のためのコードを自動生成できます。先ほどのようにLiveViewを使用したSPAまで生成できちゃいます
- **hexdocsが整備されてて見やすい**: パッケージのドキュメントが掲載されるページです。パッケージの公式ページで見やすいものってあまり無いですよね。特にpipとnpm
- **Phoenixのホットリロード**: モダンなWebフレームワークでは標準だけど、やっぱり有り難い。さらに簡単にインタラクティブシェルを立ち上げられるので関数の動作確認するときなどとても便利!
- **美しい文法**:パイプや関数パターンマッチング、関数の呼び出し方など書いていて楽しいです🤗
まだあまり詳しくないですが、学習意欲を掻き立てられますね!
- **Tailwind CSSがインストール済み**: 皆さん好きですよね、デファクト化しつつあると思います。

### Phoenixを使用した感想
LiveViewを初めとした革新的な新機能を導入しながら、細かい部分の使い心地も非常に良かったです。
こまれまでのフレームワークやエコシステムのいい点を取り込みつつ、煩わしさが取り除かれており面白くて、丁寧なフレームワークという印象でした。

## Tips 

github hero iconsをassetsに入れない方法

`git init .`するとやたら多くのファイルが作成されていてビックリするかもしれません。
主にheroiconsのCSVが原因です。CSVということもあり特にサイズが大きいくももないので、Githubにそのままpushしても構いません。

余分なファイルがコミットされるのが嫌な方(自分もそうでした)はassets/heroiconsを削除して、mix.exとmix.exsを書き換えることで、githubに余分なファイルをpushせずに済みます
他のelxirパッケージが保存されている /depsにheroiconsも保存されます。

```bash
rm -r ./assets/vendor/heroicons
```

assets/tailwind.config.js
```
-      let iconsDir = path.join(__dirname, "./vendor/heroicons/optimized")
+      let iconsDir = path.join(__dirname, "../deps/heroicons/optimized")
```

```elixir:mix.exs
~~~~~~~
defp deps do
    [
      {:phoenix, "~> 1.7.10"},
      {:phoenix_ecto, "~> 4.4"},
      {:ecto_sql, "~> 3.10"},
      {:ecto_sqlite3, ">= 0.0.0"},
      {:phoenix_html, "~> 3.3"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:phoenix_live_view, "~> 0.20.1"},
      {:heroicons,
       github: "tailwindlabs/heroicons",
       tag: "v2.0.18",
       app: false,
       compile: false,
       sparse: "optimized"},
       ~~~~~~~
    ]
  end
~~~~~~~
```

ビューにheroiconsを使用するコードを追加して動作を確認しましょう

```heex:lib/hello_web/controllers/page_html/home.html.heex
+    <.icon name="hero-star" class="w-16 h-16" />
```
    
```bash
mix deps.get
mix phx.servcer
```
最新のPhoenixの更新でオプションとして追加されたようです
開発コミュニティが活発そうでいいですね~🥰

https://github.com/elixir-lang/elixir/pull/13128

## 参考文献
https://qiita.com/t-yamanashi/items/ef4c5bcab4a130105760

https://github.com/phoenixframework/phoenix/issues/5622

https://hexdocs.pm/

https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Live.html

https://hexdocs.pm/phoenix/Mix.Tasks.Phx.Gen.Live.html

https://qiita.com/sand/items/71d0b35d74a4781f3564

https://ja.wikipedia.org/wiki/Elixir_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E)