This page is a work in progress. You can help by suggesting edits!

## Schemas

### Generating

    $ mix phx.gen.html \
        Accounts \       # domain
        Profile \        # schema
        profiles \       # table name
        email:string \
        age:integer

### Schema

    defmodule Myapp.Accounts.User do
      use Ecto.Schema

      schema "users" do
        field :name
        field :age, :integer
        field :password, virtual: true

        timestamps()
      end
    end

### Field types

Field
`:id`
`:binary`
`:boolean`
`:string`
`:integer`
`:float`
`:decimal`
`{:array, inner_type}`
`:map`

## Changesets

    def changeset(user, params \\ :empty) do
      %User{}
      |> Ecto.Changeset.change   # basic casting to changeset

      user
      |> cast(params, ~w(name email), ~w(age)) # params to Changeset

      |> validate_format(:email, ~r/@/)

      |> validate_inclusion(:age, 18..100)
      |> validate_exclusion(:role, ~w(admin superadmin))
      |> validate_subset(:pets, ~w(cat dog parrot whale))

      |> validate_length(:body, min: 1)
      |> validate_length(:body, min: 1, max: 160)
      |> validate_length(:partners, is: 2)

      |> validate_number(:pi, greater_than: 3)
      |> validate_number(:pi, less_than: 4)
      |> validate_number(:pi, equal_to: 42)

      |> validate_change(:title, fn _, _ -> [])
      |> validate_confirmation(:password, message: "does not match")

      |> unique_constraint(:email)
      |> foreign_key_constraint(:post_id)
      |> assoc_constraint(:post)      # ensure post_id exists
      |> no_assoc_constraint(:post)   # negative (useful for deletions)
    end

### Changeset fields

    changeset.valid?
    changeset.errors     #=> [title: "empty"]

    changeset.changes    #=> %{}
    changeset.params[:title]

    changeset.required   #=> [:title]
    changeset.optional   #=> [:body]

### Updating

    changeset #(or model)
    |> change(title: "New title")
    |> change(%{ title: "New title" })
    |> put_change(:title, "New title")
    |> force_change(:title, "New title")
    |> update_change(:title, &(&1 <> "..."))

    |> delete_change(:title)
    |> merge(other_changeset)

    |> add_error(:title, "empty")

### Getting

    get_change(changeset, :title)    #=> "hi" (if changed)
    get_field(changeset, :title)     #=> "hi" (even if unchanged)

    fetch_change(changeset, :title)  #=> {:ok, "hi"} | :error
    fetch_field(changeset, :title)   #=> {:changes | :model, "value"} | :error

## Repo

### Get one

    Repo.get(User, id)
    Repo.get_by(User, email: "john@hello.com")  #=> %User{} | nil

    # also get! get_by!

### Create/update

    changeset |> Repo.update
    changeset |> Repo.insert
    changeset |> Repo.insert_or_update

    User
    |> Ecto.Changeset.change(%{name: "hi"})
    |> Repo.insert

## Many

### Queries

    from p in Post,
      where: p.title == "Hello",
      where: [state: "Sweden"],

      limit: 1,
      offset: 10,

      order_by: c.name,
      order_by: [c.name, c.title],
      order_by: [asc: c.name, desc: c.title],

      preload: [:comments],
      preload: [comments: {c, likes: l}],

      join: c in assoc(c, :comments),
      join: p in Post, on: c.post_id == p.id,
      group_by: p,

      select: p,
      select: {p.title, p.description},
      select: [p.title, p.description],

### Get many

    Repo.all(User)

### Update many

    Repo.update_all(Post, set: [title: "Title"])
    Repo.update_all(Post, inc: [views: 1])

### Chaining `_all` with queries

    from(p in Post, where: p.id < 10)
    |> Repo.update_all(...)

    from(p in Post, where: p.id < 10)
    |> Repo.all()

## References

- Based on Ecto 1.3.

0 Comments for this cheatsheet. Write yours!