sb logoToday I Learned

How to grasp the business logic with unique_index

How do I ensure a unique index when only a user tries to get more than one ticket for a paid conference? And be more flexible in accepting such users get more than one ticket for a free conference?

How are our goals here?

  • sent paid conference to Ticket.changeset/1 ensure the status: :paid, create one.
  • sent free conference to Ticket.changeset/1 ensure the status: :free, create many.

How we can apply these to our business logic:

  create table(:tickets) do
    add :conference_id, references(:conferences), null: false
    add :user_id, references(:users), null: false
    add :status, :string, null: false, default: "free"
   end

  create unique_index(:tickets, [:conference_id, :user_id, :status], where: "status = 'paid'")

How can we play?

iex> Ticket.changeset(%{conference: %{is_paid: false}, user: %{...}, status: :free}) |> Repo.insert()
[debug] QUERY OK
{:ok, 
  %Ticket%{id: 1, status: :free, conference_id: 1, user_id: 1}
}
iex> Ticket.changeset(%{conference: %{is_paid: false}, user: %{...}, status: :free}) |> Repo.insert()
[debug] QUERY OK
{:ok, 
  %Ticket%{id: 2, status: :free, conference_id: 1, user_id: 1}
}
iex> Ticket.changeset(%{conference: %{is_paid: true}, user: %{...}, status: :paid}) |> Repo.insert()
[debug] QUERY OK
{:ok, 
  %Ticket%{id: 3, status: :paid, conference_id: 2, user_id: 1}
}
iex> Ticket.changeset(%{conference: %{is_paid: true}, user: %{...}, status: :paid}) |> Repo.insert()
[debug] QUERY ERROR
** (Ecto.ConstraintError)

cool!