Table of Contents

Introduction
Migration to Create a Table
Defining Models
Creating a New Record
Retrieving All Records
Retrieving All Records with a Query
Retrieving a Single Record
Updating a Record
Deleting a Record
Retrieving Associated Records
Adding Associated Records

Introduction

Migration to Create a Table

Ruby / Rails / Active Record

$ rails generate migration CreatePeople
$ rails generate migration CreatePets
$ rails generate migration AddPeoplePetsJunctionTable
class CreatePeople < ActiveRecord::Migration[5.0]
  def change
    create_table :people do |t|
      t.string :name
      t.integer :age
      t.string :location

      t.timestamps
    end
  end
end
class CreatePets < ActiveRecord::Migration[5.0]
  def change
    create_table :pets do |t|
      t.string :name
      t.integer :age
      t.string :animal

      t.timestamps
    end
  end
end
class AddPeoplePetsJunctionTable < ActiveRecord::Migration[5.0]
  def change
    create_table :people_pets, id: false do |t|
      t.belongs_to :person, index: true
      t.belongs_to :pet, index: true
    end
  end
end
$ rake db:migrate

Elixir / Phoenix / Ecto

$ mix ecto.gen.migration CreatePerson
$ mix ecto.gen.migration CreatePet
$ mix ecto.gen.migration AddPeoplePetsJunctionTable
defmodule YourPhoenixApp.Repo.Migrations.CreatePerson do
  use Ecto.Migration

  def change do
    create table(:people) do
      add :name, :string
      add :age, :integer
      add :location, :string

      timestamps()
    end

  end
end
defmodule YourPhoenixApp.Repo.Migrations.CreatePet do
  use Ecto.Migration

  def change do
    create table(:pets) do
      add :name, :string
      add :age, :integer
      add :animal, :string

      timestamps()
    end

  end
end
defmodule YourPhoenixApp.Repo.Migrations.AddPeoplePetsJunctionTable do
  use Ecto.Migration

  def change do
    create table(:people_pets, primary_key: false) do
      add :person_id, references(:people)
      add :pet_id, references(:pets)
    end
  end
end
$ mix ecto.migrate

Defining Models

Ruby / Rails / Active Record

class Person < ApplicationRecord
  has_and_belongs_to_many :pets
  before_destroy :clear_habtm

  def clear_habtm
    pets.destroy_all
  end
end
class Pet < ApplicationRecord
  has_and_belongs_to_many :people
  before_destroy :clear_habtm

  def clear_habtm
    people.destroy_all
  end
end

Elixir / Phoenix / Ecto

defmodule YourPhoenixApp.Person do
  use YourPhoenixApp.Web, :model

  schema "people" do
    field :name, :string
    field :age, :integer
    field :location, :string
    many_to_many :pets, YourPhoenixApp.Pet, join_through: "people_pets", on_delete: :delete_all

    timestamps()
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name, :age, :location])
    |> validate_required([:name, :age, :location])
  end
end
defmodule YourPhoenixApp.Pet do
  use YourPhoenixApp.Web, :model

  schema "pets" do
    field :name, :string
    field :age, :integer
    field :animal, :string
    many_to_many :people, YourPhoenixApp.Person, join_through: "people_pets", on_delete: :delete_all

    timestamps()
  end

  def changeset(struct, params \\ %{}) do
    struct
    |> cast(params, [:name, :age, :animal])
    |> validate_required([:name, :age, :animal])
  end
end

Aliases for Convenience

Whether you’re working in an interactive Elixir session or within a Phoenix app, ou’ll want to alias your models and the Repo for convenience. This way you’ll be able to write Person instead of the full YourPhoenixApp.Person.

alias YourPhoenixApp.Person
alias YourPhoenixApp.Pet
alias YourPhoenixApp.Repo

Without further ado, let’s get started!

Creating a New Record

Ruby / Rails / Active Record

Person.create(name: "Rick")

Elixir / Phoenix / Ecto

%Person{ name: "Rick" }
|> Person.changeset
|> Repo.insert

(Note here that the call to Person.changeset isn’t strictly necessary but it’s nice to have because it will apply the validations imposed by the changeset, keeping everything tight and tidy).

Retrieving All Records

Ruby / Rails / Active Record

Person.all

Elixir / Phoenix / Ecto

Person |> Repo.all

Retrieving All Records with a Query

Ruby / Rails / Active Record

Person.where(age: 38)

# or

Person.where('age >= ?', 30)

Elixir / Phoenix / Ecto

require Ecto.Query

Person
|> Ecto.Query.where(age: 38)
|> Repo.all

# or

Ecto.Query.from(p in Person, where: p.age >= 30) |> Repo.all

Retrieving a Single Record

Getting by record ID

Ruby / Rails / Active Record

Person.find(46)

Elixir / Phoenix / Ecto

Person |> Repo.get(46)

Getting by a specified identifier

Ruby / Rails / Active Record

Person.find_by_name("Rick")

Elixir / Phoenix / Ecto

Person |> Repo.get_by(name: "Rick")

Get the first (or last) record

Ruby / Rails / Active Record

Person.first

Elixir / Phoenix / Ecto

Person
|> Ecto.Query.first
|> Repo.one

Updating a Record

Ruby / Rails / Active Record

Person.find(46)
      .update(name: "Morty")

Elixir / Phoenix / Ecto

Person
|> Repo.get(46)
|> Person.changeset(%{ name: "Morty" })
|> Repo.update

Deleting a Record

Ruby / Rails / Active Record

Person.find(46)
      .delete

Elixir / Phoenix / Ecto

Person
|> Repo.get(46)
|> Repo.delete

Retrieving Associated Records

Ruby / Rails / Active Record

Person.find(46)
      .pets

Elixir / Phoenix / Ecto

person = Person
         |> Repo.get(46)
         |> Repo.preload(:pets)

person.pets

Adding Associated Records

Ruby / Rails / Active Record

Person.find(46)
      .pets
      .create(name: "Snuffles")

Elixir / Phoenix / Ecto

morty = Person
        |> Repo.get(46)
        |> Repo.preload(:pets)

snuffles = %Pet{ name: "Snuffles" }
           |> Pet.changeset
           |> Repo.insert

morty
|> Ecto.Changeset.change
|> Ecto.Changeset.put_assoc(:pets, [ snuffles ] ++ morty.pets)
|> Repo.update