JavaScriptを有効にしてください

【Elixir】Ecto.Schema.embedded_schema を使ってリクエストを処理する方法

 ·  ☕ 3 分で読めます

【Elixir】Ecto.Schema.embedded_schema を複雑な使ってリクエストに対してのリクエストを処理する方法のメモ

Elixir で LiveView に対する認証・認可を自作する

以下のドキュメントを参考にしています。
Ecto.Schema — Ecto v3.9.4 - HexDocs # embedded_schema

環境

  • Elixir 1.14.2
  • Phoenix 1.6.3

【Elixir】Ecto.Schema.embedded_schema を使ってリクエストを処理する方法

Elixir で changeset を渡して Ecto.Schema.embedded_schema を使って複雑なリクエストに対して処理する方法のメモ。
僕は複数テーブルに対してのデータを取得して複数テーブルのデータする際に使用しました。

Ecto.Schema.embedded_schema とは

Ectoスキーマをデータベーステーブルと関連付けずに定義ができます。
これは、構造化データを操作するためにEctoの機能を活用したいが、データベースに保存する必要はない場合に便利です。
Ecto.Schema — Ecto v3.9.4 - HexDocs # embedded_schema

注意事項

PHPer の Elixir の初心者が Laravelのリクエストの用に別途処理を分けたいと思って作ったものです。
Elixir として正しいものかどうかはわかりません。

また今回のサンプルはだいぶ長くなってしまったので処理を短くしています。
そのまま使ってもエラーになる可能性がありますのでお気をつけください。

またテンプレート側の記載は省いております。

実装

まずはリクエスト処理用の Ecto スキーマを作成します。

リクエスト用 Ecto 実装

リクエスト処理を行うためのEctoをまず実装します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
defmodule Demo.Requests.UserRegist do
  use Ecto.Schema
  import Ecto.Changeset

  embedded_schema do
    field :email, :string
    field :name, :string
    field :address, :string
  end

  @doc false
  def changeset(user_regist, attrs) do
    account
    # 変換
    |> cast(attrs, [
      :email,
      :name,
      :address
    ])
    # 必須チェック
    |> validate_required([
      :email,
      :name,
      :address
    ])
  end

  def to_user(user_regist) do
    Map.take(user_regist, [:email, :name])
  end

  def to_address(user_regist) do
    Map.take(user_regist, [:address])
  end
end

Ecto 定義

データベースへの登録を行うためEctoを定義します。

User用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
defmodule Demo.Models.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field :email, :string
    field :name, :string
    timestamps()
  end

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

Address用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
defmodule Demo.Models.Address do
  use Ecto.Schema
  import Ecto.Changeset

  schema "addresses" do
    field :address, :string
    timestamps()
  end

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

コントローラー

コントローラー側の実装を行ってみます。
UserRegistEcto.Schema.embedded_schema を使用しているのでそこでリクエストパラメータ用のバリデーションを行っています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
defmodule DemoWeb.UserController do
  use DemoWeb, :controller

  alias Demo.Repo
  alias Demo.Requests.UserRegist
  alias Demo.Models.User
  alias Demo.Models.Address

  # ユーザ登録画面
  def new(conn, _params) do
    changeset = UserRegist.changeset(%UserRegist{}, %{})
    render(conn, "new.html", changeset: changeset)
  end

  # ユーザ登録処理
  def create(conn, %{"user" => user_params}) do

    changeset = UserRegist.changeset(%UserRegist{}, user_params)
    # バリデーションチェック
    if changeset.valid? do

      result =
        Repo.transaction(fn ->
          # ユーザ登録
          user = User.changeset(%User{}, UserRegist.to_user(user_params))
          {:ok, user} = Repo.insert(user)
          # アドレス登録
          address = UserRegist.to_address(user_params)
          # ユーザIDをアドレスに付与
          address = Map.put(address, :user_id, user.id)
          address = Address.changeset(%Address{}, address)
          {:ok, address} = Repo.insert(address)
          {:ok, user, address}
        end)

      {:ok, value} ->
        # 登録成功
        conn
        |> put_flash(:info, "User created successfully.")
        |> redirect(to: Routes.user_path(conn, :show, user))
      _ ->
         # エラー発生
        changeset = %{changeset | action: :create}
        conn
        |> put_flash(:error, "An error occurred.")
        |> render("new.html", changeset: changeset)
      end
    else
      # エラー発生
      changeset = %{changeset | action: :create}

      conn
      |> put_flash(:error, "An error occurred.")
      |> render("new.html", changeset: changeset)
    end
  end
end

これでデータベースへの登録とは別にリクエストのバリデーションのチェックが可能になります。

参考

共有

こぴぺたん
著者
こぴぺたん
Copy & Paste Engineer