Examples

View Source

Examples of using Aurora.Ctx in common scenarios.

Basic CRUD Operations

Basic context setup and standard database operations.

defmodule MyApp.Inventory do
  use Aurora.Ctx,
    schema_module: MyApp.Inventory.Product,
    repo_module: MyApp.Repo
end

# Usage examples
alias MyApp.Inventory

# Create a product
{:ok, product} = Inventory.create_product(%{
  name: "Premium Widget",
  price: Decimal.new("29.99")
})

# List products with sorting
products = Inventory.list_products(order_by: [desc: :inserted_at])

# Get product with preloaded associations
{:ok, product} = Inventory.get_product(1, preload: [:category, :supplier])

# Update a product
{:ok, updated} = Inventory.update_product(product, %{price: Decimal.new("39.99")})

# Delete a product
{:ok, _deleted} = Inventory.delete_product(product)

Query Usage

More complex database operations using query options.

Custom Query Options

Combining filtering, preloading, and sorting in queries.

# List active products with preloaded categories
products = Inventory.list_products(
  where: [status: "active"],
  preload: [:category],
  order_by: [asc: :name],
  limit: 10
)

# Get a product with specific preloads
product = Inventory.get_product!(1, 
  preload: [
    category: [:parent_category],
    reviews: [:user]
  ]
)

Working with Changesets

Data validation and form handling with changesets.

# Create a changeset for a new product
changeset = Inventory.change_product(%Product{})

# Create a changeset with attributes
changeset = Inventory.change_product(%Product{}, %{
  name: "New Widget",
  price: "19.99"
})

# Working with forms in Phoenix
def new(conn, _params) do
  changeset = Inventory.change_product(%Product{})
  render(conn, "new.html", changeset: changeset)
end

def create(conn, %{"product" => product_params}) do
  case Inventory.create_product(product_params) do
    {:ok, product} -> 
      redirect(to: Routes.product_path(conn, :show, product))
    {:error, changeset} ->
      render(conn, "new.html", changeset: changeset)
  end
end

Query Options

Available options for customizing database queries.

Filtering

Filter query results using different conditions.

# Multiple conditions
products = Inventory.list_products(
  where: [
    status: "active",
    category_id: 1
  ]
)

# Comparison operators
products = Inventory.list_products(
  where: [
    {:price, :gt, Decimal.new("100.00")},
    {:stock_level, :le, 5}
  ]
)

# Range queries
products = Inventory.list_products(
  where: {:price, :between, Decimal.new("10.00"), Decimal.new("50.00")}
)

# OR conditions
products = Inventory.list_products(
  where: [status: "active"],
  or_where: [status: "pending_review"]
)

Sorting

Order query results with various sorting strategies.

# Multiple sort fields
products = Inventory.list_products(
  order_by: [
    asc: :category_id,
    desc: :price,
    asc: :name
  ]
)

# Null handling
products = Inventory.list_products(
  order_by: [
    asc_nulls_last: :discontinued_at,
    desc_nulls_first: :updated_at
  ]
)

Controlled Pagination

Implement and navigate through paginated results. Results are wrapped in an Aurora.Ctx.Pagination struct that provides additional metadata and navigation helpers.

# Basic pagination
page1 = Inventory.list_products_paginated(
  paginate: %{page: 1, per_page: 20}
)

# Access pagination info
IO.puts "Total entries: #{page1.entries_count}"
IO.puts "Total pages: #{page1.pages_count}"
IO.puts "Current page: #{page1.page}"

# Navigate through pages
next_page = Inventory.next_products_page(page1)
prev_page = Inventory.previous_products_page(next_page)
page5 = Inventory.to_products_page(page1, 5)

# Combine with other options
filtered_page = Inventory.list_products_paginated(
  where: [category_id: 1],
  order_by: [desc: :inserted_at],
  paginate: %{page: 1, per_page: 10}
)

Preloading Associates

Load associated data with simple and nested preloads.

# Nested preloads
product = Inventory.get_product!(1,
  preload: [
    category: [:parent_category, :subcategories],
    reviews: [:user, comments: [:user]],
    supplier: [:address, :contacts]
  ]
)

# Filtered preloads with custom queries
products = Inventory.list_products(
  preload: [
    reviews: from(r in Review, where: r.rating > 3)
  ]
)

Working with Transactions

Combine multiple database operations in transactions.

alias Ecto.Multi
alias MyApp.{Inventory, Orders}

# Complex multi-operation transaction
def create_order_with_inventory_update(attrs) do
  Multi.new()
  |> Multi.run(:product, fn repo, _ ->
    Inventory.get_product(attrs.product_id)
  end)
  |> Multi.run(:check_stock, fn _repo, %{product: product} ->
    if product.stock >= attrs.quantity do
      {:ok, product}
    else
      {:error, :insufficient_stock}
    end
  end)
  |> Multi.run(:order, fn repo, %{product: product} ->
    Orders.create_order(attrs)
  end)
  |> Multi.run(:update_inventory, fn repo, %{product: product, order: order} ->
    Inventory.update_product(product, %{
      stock: product.stock - order.quantity
    })
  end)
  |> MyApp.Repo.transaction()
end

# Usage
case create_order_with_inventory_update(%{product_id: 1, quantity: 5}) do
  {:ok, %{order: order, product: product}} ->
    {:ok, order}
  {:error, failed_operation, failed_value, changes_so_far} ->
    {:error, :order_creation_failed}
end

Custom Changesets

Configure and use custom changeset functions.

# Using custom changeset functions
defmodule MyApp.Inventory do
  use Aurora.Ctx,
    schema_module: MyApp.Inventory.Product,
    repo_module: MyApp.Repo,
    update_changeset_function: :custom_changeset,
    create_changeset: :create_changeset

  # Your additional context functions...
end

# Schema with multiple changeset functions
defmodule MyApp.Inventory.Product do
  use Ecto.Schema
  import Ecto.Changeset

  schema "products" do
    field :name, :string
    field :price, :decimal
    field :status, :string
    timestamps()
  end

  def create_changeset(product, attrs) do
    product
    |> cast(attrs, [:name, :price])
    |> validate_required([:name, :price])
    |> validate_number(:price, greater_than: 0)
  end

  def custom_changeset(product, attrs) do
    product
    |> cast(attrs, [:name, :price, :status])
    |> validate_required([:status])
    |> validate_inclusion(:status, ["active", "inactive"])
  end
end

# Usage
product = Inventory.new_product()
changeset = Inventory.change_product(product, %{name: "New Product"})

For more examples and use cases, check the test files in the GitHub repository.