hexdocs.pmのEctoをざっくり読んだメモ
※私の読書メモなので、hexdocsをみんな読もう!
Ectoは4つの主要コンポーネントに分かれている
要約すると
- Ecto.Repo
- where the data is
- Ecto.Schema
- what the data is
- Ecto.Query
- how to read the data
- Ecto.Changeset
- how to change the data
Ecto.Repo
リポジトリ(repositories)はデータストアのラッパー。
リポジトリ(repository)を通して、既存のエントリーを作成、更新、破棄、問い合わせることができる。
リポジトリは、データベースと通信するためのアダプタと認証情報を必要とする。
リポジトリは次のように定義できる。(Phoenixだと自動生成される)
defmodule Sample.Repo do
use Ecto.Repo,
otp_app: :sample,
adapter: Ecto.Adapters.Postgres
end
リポジトリの構成はアプリケーション環境内に存在する必要があり、通常はアプリケーション環境で定義される。
config :sample,
ecto_repos: [Sample.Repo]
config :sample, Sample.Repo,
username: "postgres",
password: "postgres",
hostname: "localhost",
database: "sample_dev",
stacktrace: true,
show_sensitive_data_on_connection_error: true,
pool_size: 10
Ecto.Schema
スキーマは、外部データをElixir構造体にマッピングするために使用する。
データベースのテーブルをElixirのデータにマッピングするためによく使うが、他にもいろいろな使い道がある。
スキーマによって、開発者はデータの形を定義することができる。
defmodule Basic.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :name, :string
field :age, :integer
timestamps()
end
end
iex(1)> alias SampleP.Accounts.User
iex(2)> user = %User{name: "taro", age: 13}
%SampleP.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:built, "users">,
id: nil,
name: "taro",
age: 13,
inserted_at: nil,
updated_at: nil
}
iex(3)> user.name
"taro"
iex(4)> user2 = %User{name: "jiro", age: 11}
%SampleP.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:built, "users">,
id: nil,
name: "jiro",
age: 11,
inserted_at: nil,
updated_at: nil
}
iex(5)> alias SampleP.Repo
iex(6)> Repo.insert!(user2)
[debug] QUERY OK db=3.7ms queue=1.0ms idle=1386.8ms
INSERT INTO "users" ("name","age","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" ["jiro", 11, ~N[2023-11-12 05:13:08], ~N[2023-11-12 05:13:08]]
↳ :erl_eval.do_apply/7, at: erl_eval.erl:746
%SampleP.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: 2,
name: "jiro",
age: 11,
inserted_at: ~N[2023-11-12 05:13:08],
updated_at: ~N[2023-11-12 05:13:08]
}
Ecto.Changeset
Changesetは、変更がデータに適用される前に、その変更を追跡し検証する方法を提供する。
Changesetにより、開発者は変更をデータに適用する前に、フィルター、キャスト、検証を行うことができる。
defmodule Sample.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
schema "users" do
field :name, :string
field :age, :integer
timestamps()
end
@doc false
def changeset(user, attrs) do
user
|> cast(attrs, [:name, :age])
|> validate_required([:name, :age])
end
end
changeset/2
関数は、まず構造体、パラメータ、許可されたフィールドのリストを指定して Ecto.Changeset.cast/4
を呼び出します。
フィールドリストに明示的にリストされていないパラメータは無視されます。
castの後、Changesetは変更されたフィールドのみを検証する多くのEcto.Changeset.validate_*
関数に渡されます。言い換えると、フィールドがパラメータとして与えられなかった場合、そのフィールドはまったく検証されません。
defmodule Sample.Accounts do
〜略〜
def create_user(attrs \\ %{}) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
end
Ecto.Query
Elixir構文で記述されたクエリは、指定されたリポジトリから情報を取得するために使用される。
Ectoクエリはセキュアでコンポーザブルです。
※コンポーザブル
「構成可能な」という意味がある。
複数の要素や部品などを結合して、構成や組み立てが可能な、といった意味がある。
iex(8)> import Ecto.Query, only: [from: 2]
iex(10)> query = from u in User,
...(10)> where: u.age > 12,
...(10)> select: u
#Ecto.Query<from u0 in SampleP.Accounts.User, where: u0.age > 12, select: u0>
iex(11)> Repo.all(query)
[debug] QUERY OK source="users" db=5.7ms queue=3.4ms idle=1290.3ms
SELECT u0."id", u0."name", u0."age", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."age" > 12) []
↳ :erl_eval.do_apply/7, at: erl_eval.erl:746
[
%SampleP.Accounts.User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: 1,
name: "mamiko",
age: 15,
inserted_at: ~N[2023-11-12 05:07:28],
updated_at: ~N[2023-11-12 05:07:28]
}
]
最後に
Ectoの4要素をざっくり読んでいきました。
実装に落とし込むには、さらに下記ドキュメントを読むとよさそうです。