hexdocs.pmのEctoをざっくり読んだメモ

hexdocs.pmのEctoをざっくり読んだメモ
※私の読書メモなので、hexdocsをみんな読もう！

https://hexdocs.pm/ecto/Ecto.html

## 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だと自動生成される）

```lib/sample/repo.ex
defmodule Sample.Repo do
  use Ecto.Repo,
    otp_app: :sample,
    adapter: Ecto.Adapters.Postgres
end
```

リポジトリの構成はアプリケーション環境内に存在する必要があり、通常はアプリケーション環境で定義される。

```config/config.exs
config :sample,
  ecto_repos: [Sample.Repo]
```

```config/dev.exs
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のデータにマッピングするためによく使うが、他にもいろいろな使い道がある。

スキーマによって、開発者はデータの形を定義することができる。

```elixir
defmodule Basic.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field :name, :string
    field :age, :integer

    timestamps()
  end
end
```

```elixir
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"
```

```elixir
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により、開発者は変更をデータに適用する前に、フィルター、キャスト、検証を行うことができる。

```elixir
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_*` 関数に渡されます。言い換えると、フィールドがパラメータとして与えられなかった場合、そのフィールドはまったく検証されません。

```elixir
defmodule Sample.Accounts do
  〜略〜

  def create_user(attrs \\ %{}) do
    %User{}
    |> User.changeset(attrs)
    |> Repo.insert()
  end
end
```

### Ecto.Query

Elixir構文で記述されたクエリは、指定されたリポジトリから情報を取得するために使用される。
Ectoクエリはセキュアでコンポーザブルです。

※コンポーザブル
「構成可能な」という意味がある。
複数の要素や部品などを結合して、構成や組み立てが可能な、といった意味がある。

```elixir
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の４要素をざっくり読んでいきました。
実装に落とし込むには、さらに下記ドキュメントを読むとよさそうです。

https://hexdocs.pm/ecto/Ecto.Repo.html

https://hexdocs.pm/ecto/Ecto.Repo.html#schema-api

https://hexdocs.pm/ecto/Ecto.Changeset.html

https://hexdocs.pm/ecto/Ecto.Query.html