Repository: phoenixframework/phoenix (elixir)

Issue:
Does not seem possible to use assign in custom error html
### Environment

* Elixir version (elixir -v): 15
* Phoenix version (mix deps): main
* Operating system: mac

### Actual behavior

If you try to use assign in a custom error html like this:

```elixir
defmodule ExampleWeb.ErrorHTML do
  use ExampleWeb, :html

  def render("404.html" = template, assigns) do
    assigns =
      assign(assigns,
        message: hello
      )

    ~H"""
    <h1 class="text-2xl font-bold tracking-tight text-gray-900 sm:text-4xl">
      <%= @status %> <%= @message %>
    </h1>
    """
  end
end
```

You get an error like:

```
(ArgumentError) assign/3 expects a socket from Phoenix.LiveView/Phoenix.LiveComponent  or an assigns map from Phoenix.Component as first argument ...
```

### Expected behavior

This should be a valid assigns so I think it should work?

Pull request:
Set __changed__: nil on controller render for LiveView compatibility
Closes #5726

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e9077442d..7231eb218 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,7 +6,16 @@ This release requires Erlang/OTP 25+.
 
 `put_secure_browser_headers` has been updated to the latest security practices. In particular, it sets the `content-security-policy` header to `"base-uri 'self'; frame-ancestors 'self';"` if none is set, restricting embedding of your application and the use of `<base>` element to same origin respectively. If you expect your application to be embedded by third-parties, you want to consult the documentation.
 
-The deprecated headers `x-download-options` and `x-frame-options` are no longer set.
+The headers `x-download-options` and `x-frame-options` are no longer set as they have been deprecated by standards.
+
+## Deprecations
+
+This release introduces deprecation warnings for several features that have been soft-deprecated in the past.
+
+  * `use Phoenix.Controller` must now specify the `:formats` option, which may be set to an empty list if the formats are not known upfront
+  * The `:namespace` and `:put_default_views` options on `use Phoenix.Controller` are deprecated and emit a warning on use
+  * Specifying layouts without modules, such as `put_layout(conn, :print)` or `put_layout(conn, html: :print)` is deprecated
+  * The `:trailing_slash` option in `Phoenix.Router` has been deprecated in favor of using `Phoenix.VerifiedRoutes`. The overall usage of helpers will be deprecated in the future
 
 ## v1.7
 
diff --git a/guides/contexts.md b/guides/contexts.md
deleted file mode 100644
index 9e9699979..000000000
--- a/guides/contexts.md
+++ /dev/null
@@ -1,1451 +0,0 @@
-# Contexts
-
-> **Requirement**: This guide expects that you have gone through the [introductory guides](installation.html) and got a Phoenix application [up and running](up_and_running.html).
-
-> **Requirement**: This guide expects that you have gone through the [Request life-cycle guide](request_lifecycle.html).
-
-> **Requirement**: This guide expects that you have gone through the [Ecto guide](ecto.html).
-
-So far, we've built pages, wired up controller actions through our routers, and learned how Ecto allows data to be validated and persisted. Now it's time to tie it all together by writing web-facing features that interact with our greater Elixir application.
-
-When building a Phoenix project, we are first and foremost building an Elixir application. Phoenix's job is to provide a web interface into our Elixir application. Naturally, we compose our applications with modules and functions, but we often assign specific responsibilities to certain modules and give them names: such as controllers, routers, and live views.
-
-As everything else, contexts in Phoenix are modules, but with the distinct responsibility of drawing boundaries and grouping functionality. In other words, they allow us to reason and discuss about application design.
-
-## Thinking about contexts
-
-Contexts are dedicated modules that expose and group related functionality. For example, anytime you call Elixir's standard library, be it `Logger.info/1` or `Stream.map/2`, you are accessing different contexts. Internally, Elixir's logger is made of multiple modules, but we never interact with those modules directly. We call the `Logger` module the context, exactly because it exposes and groups all of the logging functionality.
-
-By giving modules that expose and group related functionality the name **contexts**, we help developers identify these patterns and talk about them. At the end of the day, contexts are just modules, as are your controllers, views, etc.
-
-In Phoenix, contexts often encapsulate data access and data validation. They often talk to a database or APIs. Overall, think of them as boundaries to decouple and isolate parts of your application. Let's use these ideas to build out our web application. Our goal is to build an ecommerce system where we can showcase products, allow users to add products to their cart, and complete their orders.
-
-### Adding a Catalog Context
-
-An ecommerce platform has wide-reaching coupling across a codebase so it's important to think about writing well-defined modules. With that in mind, our goal is to build a product catalog API that handles creating, updating, and deleting the products available in our system. We'll start off with the basic features of showcasing our products, and we will add shopping cart features later. We'll see how starting with a solid foundation with isolated boundaries allows us to grow our application naturally as we add functionality.
-
-Phoenix includes the `mix phx.gen.html`, `mix phx.gen.json`, `mix phx.gen.live`, and `mix phx.gen.context` generators that apply the ideas of isolating functionality in our applications into contexts. These generators are a great way to hit the ground running while Phoenix nudges you in the right direction to grow your application. Let's put these tools to use for our new product catalog context.
-
-In order to run the context generators, we need to come up with a module name that groups the related functionality that we're building. In the [Ecto guide](ecto.html), we saw how we can use Changesets and Repos to validate and persist user schemas, but we didn't integrate this with our application at large. In fact, we didn't think about where a "user" in our application should live at all. Let's take a step back and think about the different parts of our system. We know that we'll have products to showcase on pages for sale, along with descriptions, pricing, etc. Along with selling products, we know we'll need to support carting, order checkout, and so on. While the products being purchased are related to the cart and checkout processes, showcasing a product and managing the *exhibition* of our products is distinctly different than tracking what a user has placed in their cart or how an order is placed. A `Catalog` context is a natural place for the management of our product details and the showcasing of those products we have for sale.
-
-> #### A note on scopes {: .info}
->
-> Many generators support the `--scope` option to generate scoped resources. For example, a scope might be the logged in user account of your application. Scoped resources are useful when different entities should see different resources. If a resource is scoped by user, the user will only be able to manage and see their own resources. For our catalog, we want everyone to see the same products, therefore we use `--no-scope`. Note that if no scopes are configured, `--no-scope` is the default. We will see scopes in user later in this guide. You can also learn more about scopes in the [Scopes](scopes.html) guide.
-
-To jump-start our catalog context, we'll use `mix phx.gen.html` which creates a context module that wraps up Ecto access for creating, updating, and deleting products, along with web files like controllers and templates for the web interface into our context. Run the following command at your project root:
-
-```console
-$ mix phx.gen.html Catalog Product products title:string \
-description:string price:decimal views:integer --no-scope
-
-* creating lib/hello_web/controllers/product_controller.ex
-* creating lib/hello_web/controllers/product_html/edit.html.heex
-* creating lib/hello_web/controllers/product_html/index.html.heex
-* creating lib/hello_web/controllers/product_html/new.html.heex
-* creating lib/hello_web/controllers/product_html/show.html.heex
-* creating lib/hello_web/controllers/product_html/product_form.html.heex
-* creating lib/hello_web/controllers/product_html.ex
-* creating test/hello_web/controllers/product_controller_test.exs
-* creating lib/hello/catalog/product.ex
-* creating priv/repo/migrations/20250201185747_create_products.exs
-* creating lib/hello/catalog.ex
-* injecting lib/hello/catalog.ex
-* creating test/hello/catalog_test.exs
-* injecting test/hello/catalog_test.exs
-* creating test/support/fixtures/catalog_fixtures.ex
-* injecting test/support/fixtures/catalog_fixtures.ex
-
-Add the resource to your browser scope in lib/hello_web/router.ex:
-
-    resources "/products", ProductController
-
-Remember to update your repository by running migrations:
-
-    $ mix ecto.migrate
-```
-
-Phoenix generated the web files as expected in `lib/hello_web/`. We can also see our context files were generated inside a `lib/hello/catalog.ex` file and our product schema in the directory of the same name. Note the difference between `lib/hello` and `lib/hello_web`. We have a `Catalog` module to serve as the public API for product catalog functionality, as well as a `Catalog.Product` struct, which is an Ecto schema for casting and validating product data. Phoenix also provided web and context tests for us, it also included test helpers for creating entities via the `Hello.Catalog` context, which we'll look at later. For now, let's follow the instructions and add the route according to the console instructions, in `lib/hello_web/router.ex`:
-
-```diff
-  scope "/", HelloWeb do
-    pipe_through :browser
-
-    get "/", PageController, :index
-+   resources "/products", ProductController
-  end
-```
-
-With the new route in place, Phoenix reminds us to update our repo by running `mix ecto.migrate`, but first we need to make a few tweaks to the generated migration in `priv/repo/migrations/*_create_products.exs`:
-
-```diff
-  def change do
-    create table(:products) do
-      add :title, :string
-      add :description, :string
--     add :price, :decimal
-+     add :price, :decimal, precision: 15, scale: 6, null: false
--     add :views, :integer
-+     add :views, :integer, default: 0, null: false
-
-      timestamps()
-    end
-```
-
-We modified our price column to a specific precision of 15, scale of 6, along with a not-null constraint. This ensures we store currency with proper precision for any mathematical operations we may perform. Next, we added a default value and not-null constraint to our views count. With our changes in place, we're ready to migrate up our database. Let's do that now:
-
-```console
-$ mix ecto.migrate
-14:09:02.260 [info] == Running 20250201185747 Hello.Repo.Migrations.CreateProducts.change/0 forward
-
-14:09:02.262 [info] create table products
-
-14:09:02.273 [info] == Migrated 20250201185747 in 0.0s
-```
-
-Before we jump into the generated code, let's start the server with `mix phx.server` and visit [http://localhost:4000/products](http://localhost:4000/products). Let's follow the "New Product" link and click the "Save" button without providing any input. When we submit the form, we can see all the validation errors inline with the inputs. Nice! Out of the box, the context generator included the schema fields in our form template and we can see our default validations for required inputs are in effect. Let's enter some example product data and resubmit the form:
-
-```text
-Product created successfully.
-
-Title: Metaprogramming Elixir
-Description: Write Less Code, Get More Done (and Have Fun!)
-Price: 15.000000
-Views: 0
-```
-
-If we follow the "Back" link, we get a list of all products, which should contain the one we just created. Likewise, we can update this record or delete it. Now that we've seen how it works in the browser, it's time to take a look at the generated code.
-
-> #### Naming things is hard {: .tip}
->
-> When starting a web application, it may be hard to draw lines or name its different contexts, especially when the domain you are working with is not as well established as e-commerce.
->
-> If you're stuck when defining or naming a context, you can simply create a new context using the plural form of the resource you're creating. For example, a `Products` context for managing products. You will find that, even in such cases, you will organically discover other resources that belong to the `Products` context, such as categories or image galleries.
->
-> As your applications grows and the different parts of your system become clear, you can simply rename the context or move resources around. The beauty of Elixir modules is that they are stateless, so moving them around should be simply a matter of renaming the module names (and renaming the files for consistency).
-
-## Starting with generators
-
-That little `mix phx.gen.html` command packed a surprising punch. We got a lot of functionality out-of-the-box for creating, updating, and deleting products in our catalog. This is far from a full-featured app, but remember, generators are first and foremost learning tools and a starting point for you to begin building real features. Code generation can't solve all your problems, but it will teach you the ins and outs of Phoenix and nudge you towards the proper mindset when designing your application.
-
-Let's first check out the `ProductController` that was generated in `lib/hello_web/controllers/product_controller.ex`:
-
-```elixir
-defmodule HelloWeb.ProductController do
-  use HelloWeb, :controller
-
-  alias Hello.Catalog
-  alias Hello.Catalog.Product
-
-  def index(conn, _params) do
-    products = Catalog.list_products()
-    render(conn, :index, products: products)
-  end
-
-  def new(conn, _params) do
-    changeset = Catalog.change_product(%Product{})
-    render(conn, :new, changeset: changeset)
-  end
-
-  def create(conn, %{"product" => product_params}) do
-    case Catalog.create_product(product_params) do
-      {:ok, product} ->
-        conn
-        |> put_flash(:info, "Product created successfully.")
-        |> redirect(to: ~p"/products/#{product}")
-
-      {:error, %Ecto.Changeset{} = changeset} ->
-        render(conn, :new, changeset: changeset)
-    end
-  end
-
-  def show(conn, %{"id" => id}) do
-    product = Catalog.get_product!(id)
-    render(conn, :show, product: product)
-  end
-  ...
-end
-```
-
-We've seen how controllers work in our [controller guide](controllers.html), so the code probably isn't too surprising. What is worth noticing is how our controller calls into the `Catalog` context. We can see that the `index` action fetches a list of products with `Catalog.list_products/0`, and how products are persisted in the `create` action with `Catalog.create_product/1`. We haven't yet looked at the catalog context, so we don't yet know how product fetching and creation is happening under the hood – *but that's the point*. Our Phoenix controller is the web interface into our greater application. It shouldn't be concerned with the details of how products are fetched from the database or persisted into storage. We only care about telling our application to perform some work for us. This is great because our business logic and storage details are decoupled from the web layer of our application. If we move to a full-text storage engine later for fetching products instead of a SQL query, our controller doesn't need to be changed. Likewise, we can reuse our context code from any other interface in our application, be it a channel, mix task, or long-running process importing CSV data.
-
-In the case of our `create` action, when we successfully create a product, we use `Phoenix.Controller.put_flash/3` to show a success message, and then we redirect to the router's product show page. Conversely, if `Catalog.create_product/1` fails, we render our `"new.html"` template and pass along the Ecto changeset for the template to lift error messages from.
-
-Next, let's dig deeper and check out our `Catalog` context in `lib/hello/catalog.ex`:
-
-```elixir
-defmodule Hello.Catalog do
-  @moduledoc """
-  The Catalog context.
-  """
-
-  import Ecto.Query, warn: false
-  alias Hello.Repo
-
-  alias Hello.Catalog.Product
-
-  @doc """
-  Returns the list of products.
-
-  ## Examples
-
-      iex> list_products()
-      [%Product{}, ...]
-
-  """
-  def list_products do
-    Repo.all(Product)
-  end
-  ...
-end
-```
-
-This module will be the public API for all product catalog functionality in our system. For example, in addition to product detail management, we may also handle product category classification and product variants for things like optional sizing, trims, etc. If we look at the `list_products/0` function, we can see the private details of product fetching. And it's super simple. We have a call to `Repo.all(Product)`. We saw how Ecto repo queries worked in the [Ecto guide](ecto.html), so this call should look familiar. Our `list_products` function is a generalized function name specifying the *intent* of our code – namely to list products. The details of that intent where we use our Repo to fetch the products from our PostgreSQL database is hidden from our callers. This is a common theme we'll see re-iterated as we use the Phoenix generators. Phoenix will push us to think about where we have different responsibilities in our application, and then to wrap up those different areas behind well-named modules and functions that make the intent of our code clear, while encapsulating the details.
-
-Now we know how data is fetched, but how are products persisted? Let's take a look at the `Catalog.create_product/1` function:
-
-```elixir
-  @doc """
-  Creates a product.
-
-  ## Examples
-
-      iex> create_product(%{field: value})
-      {:ok, %Product{}}
-
-      iex> create_product(%{field: bad_value})
-      {:error, %Ecto.Changeset{}}
-
-  """
-  def create_product(attrs \\ %{}) do
-    %Product{}
-    |> Product.changeset(attrs)
-    |> Repo.insert()
-  end
-```
-
-There's more documentation than code here, but a couple of things are important to highlight. First, we can see again that our Ecto Repo is used under the hood for database access. You probably also noticed the call to `Product.changeset/2`. We talked about changesets before, and now we see them in action in our context.
-
-If we open up the `Product` schema in `lib/hello/catalog/product.ex`, it will look immediately familiar:
-
-```elixir
-defmodule Hello.Catalog.Product do
-  use Ecto.Schema
-  import Ecto.Changeset
-
-  schema "products" do
-    field :description, :string
-    field :price, :decimal
-    field :title, :string
-    field :views, :integer
-
-    timestamps()
-  end
-
-  @doc false
-  def changeset(product, attrs) do
-    product
-    |> cast(attrs, [:title, :description, :price, :views])
-    |> validate_required([:title, :description, :price, :views])
-  end
-end
-```
-
-This is just what we saw before when we ran `mix phx.gen.schema`, except here we see a `@doc false` above our `changeset/2` function. This tells us that while this function is publicly callable, it's not part of the public context API. Callers that build changesets do so via the context API. For example, `Catalog.create_product/1` calls into our `Product.changeset/2` to build the changeset from user input. Callers, such as our controller actions, do not access `Product.changeset/2` directly. All interaction with our product changesets is done through the public `Catalog` context.
-
-## Adding Catalog functions
-
-As we've seen, your context modules are dedicated modules that expose and group related functionality. Phoenix generates generic functions, such as `list_products` and `update_product`, but they only serve as a basis for you to grow your business logic and application from. Let's add one of the basic features of our catalog by tracking product page view count.
-
-For any ecommerce system, the ability to track how many times a product page has been viewed is essential for marketing, suggestions, ranking, etc. While we could try to use the existing `Catalog.update_product` function, along the lines of `Catalog.update_product(product, %{views: product.views + 1})`, this would not only be prone to race conditions, but it would also require the caller to know too much about our Catalog system. To see why the race condition exists, let's walk through the possible execution of events:
-
-Intuitively, you would assume the following events:
-
-  1. User 1 loads the product page with count of 13
-  2. User 1 saves the product page with count of 14
-  3. User 2 loads the product page with count of 14
-  4. User 2 saves the product page with count of 15
-
-While in practice this would happen:
-
-  1. User 1 loads the product page with count of 13
-  2. User 2 loads the product page with count of 13
-  3. User 1 saves the product page with count of 14
-  4. User 2 saves the product page with count of 14
-
-The race conditions would make this an unreliable way to update the existing table since multiple callers may be updating out of date view values. There's a better way.
-
-Let's think of a function that describes what we want to accomplish. Here's how we would like to use it:
-
-```elixir
-product = Catalog.inc_page_views(product)
-```
-
-That looks great. Our callers will have no confusion over what this function does, and we can wrap up the increment in an atomic operation to prevent race conditions.
-
-Open up your catalog context (`lib/hello/catalog.ex`), and add this new function:
-
-```elixir
-  def inc_page_views(%Product{} = product) do
-    {1, [%Product{views: views}]} =
-      from(p in Product, where: p.id == ^product.id, select: [:views])
-      |> Repo.update_all(inc: [views: 1])
-
-    put_in(product.views, views)
-  end
-```
-
-We built a query for fetching the current product given its ID which we pass to `Repo.update_all`. Ecto's `Repo.update_all` allows us to perform batch updates against the database, and is perfect for atomically updating values, such as incrementing our views count. The result of the repo operation returns the number of updated records, along with the selected schema values specified by the `select` option. When we receive the new product views, we use `put_in(product.views, views)` to place the new view count within the product struct.
-
-With our context function in place, let's make use of it in our product controller. Update your `show` action in `lib/hello_web/controllers/product_controller.ex` to call our new function:
-
-```elixir
-  def show(conn, %{"id" => id}) do
-    product =
-      id
-      |> Catalog.get_product!()
-      |> Catalog.inc_page_views()
-
-    render(conn, :show, product: product)
-  end
-```
-
-We modified our `show` action to pipe our fetched product into `Catalog.inc_page_views/1`, which will return the updated product. Then we rendered our template just as before. Let's try it out. Refresh one of your product pages a few times and watch the view count increase.
-
-We can also see our atomic update in action in the ecto debug logs:
-
-```text
-[debug] QUERY OK source="products" db=0.5ms idle=834.5ms
-UPDATE "products" AS p0 SET "views" = p0."views" + $1 WHERE (p0."id" = $2) RETURNING p0."views" [1, 1]
-```
-
-Good work!
-
-As we've seen, designing with contexts gives you a solid foundation to grow your application from. Using discrete, well-defined APIs that expose the intent of your system allows you to write more maintainable applications with reusable code. Now that we know how to start extending our context API, lets explore handling relationships within a context.
-
-## In-context relationships
-
-Our basic catalog features are nice, but let's take it up a notch by categorizing products. Many ecommerce solutions allow products to be categorized in different ways, such as a product being marked for fashion, power tools, and so on. Starting with a one-to-one relationship between product and categories will cause major code changes later if we need to start supporting multiple categories. Let's set up a category association that will allow us to start off tracking a single category per product, but easily support more later as we grow our features.
-
-For now, categories will contain only textual information. Our first order of business is to decide where categories live in the application. We have our `Catalog` context, which manages the exhibition of our products. Product categorization is a natural fit here. Phoenix is also smart enough to generate code inside an existing context, which makes adding new resources to a context a breeze. Run the following command at your project root:
-
-> Sometimes it may be tricky to determine if two resources belong to the same context or not. In those cases, prefer distinct contexts per resource and refactor later if necessary. Otherwise you can easily end up with large contexts of loosely related entities. Also keep in mind that the fact two resources are related does not necessarily mean they belong to the same context, otherwise you would quickly end up with one large context, as the majority of resources in an application are connected to each other. To sum it up: if you are unsure, you should prefer separate modules (contexts).
-
-```console
-$ mix phx.gen.context Catalog Category categories \
-title:string:unique --no-scope
-
-You are generating into an existing context.
-...
-Would you like to proceed? [Yn] y
-* creating lib/hello/catalog/category.ex
-* creating priv/repo/migrations/20250203192325_create_categories.exs
-* injecting lib/hello/catalog.ex
-* injecting test/hello/catalog_test.exs
-* injecting test/support/fixtures/catalog_fixtures.ex
-
-Remember to update your repository by running migrations:
-
-    $ mix ecto.migrate
-```
-
-This time around, we used `mix phx.gen.context`, which is just like `mix phx.gen.html`, except it doesn't generate the web files for us. Since we already have controllers and templates for managing products, we can integrate the new category features into our existing web form and product show page. We can see we now have a new `Category` schema alongside our product schema at `lib/hello/catalog/category.ex`, and Phoenix told us it was *injecting* new functions in our existing Catalog context for the category functionality. The injected functions will look very familiar to our product functions, with new functions like `create_category`, `list_categories`, and so on. Before we migrate up, we need to do a second bit of code generation. Our category schema is great for representing an individual category in the system, but we need to support a many-to-many relationship between products and categories. Fortunately, ecto allows us to do this simply with a join table, so let's generate that now with the `ecto.gen.migration` command:
-
-```console
-$ mix ecto.gen.migration create_product_categories
-
-* creating priv/repo/migrations/20250203192958_create_product_categories.exs
-```
-
-Next, let's open up the new migration file and add the following code to the `change` function:
-
-```elixir
-defmodule Hello.Repo.Migrations.CreateProductCategories do
-  use Ecto.Migration
-
-  def change do
-    create table(:product_categories, primary_key: false) do
-      add :product_id, references(:products, on_delete: :delete_all)
-      add :category_id, references(:categories, on_delete: :delete_all)
-    end
-
-    create index(:product_categories, [:product_id])
-    create unique_index(:product_categories, [:category_id, :product_id])
-  end
-end
-```
-
-We created a `product_categories` table and used the `primary_key: false` option since our join table does not need a primary key. Next we defined our `:product_id` and `:category_id` foreign key fields, and passed `on_delete: :delete_all` to ensure the database prunes our join table records if a linked product or category is deleted. By using a database constraint, we enforce data integrity at the database level, rather than relying on ad-hoc and error-prone application logic.
-
-Next, we created indexes for our foreign keys, one of which is a unique index to ensure a product cannot have duplicate categories. Note that we do not necessarily need single-column index for `category_id` because it is in the leftmost prefix of multicolumn index, which is enough for the database optimizer. Adding a redundant index, on the other hand, only adds overhead on write.
-
-With our migrations in place, we can migrate up.
-
-```console
-$ mix ecto.migrate
-
-18:20:36.489 [info] == Running 20250222231834 Hello.Repo.Migrations.CreateCategories.change/0 forward
-
-18:20:36.493 [info] create table categories
-
-18:20:36.508 [info] create index categories_title_index
-
-18:20:36.512 [info] == Migrated 20250222231834 in 0.0s
-
-18:20:36.547 [info] == Running 20250222231930 Hello.Repo.Migrations.CreateProductCategories.change/0 forward
-
-18:20:36.547 [info] create table product_categories
-
-18:20:36.557 [info] create index product_categories_product_id_index
-
-18:20:36.560 [info]  create index product_categories_category_id_product_id_index
-
-18:20:36.562 [info] == Migrated 20250222231930 in 0.0s
-```
-
-Now that we have a `Catalog.Product` schema and a join table to associate products and categories, we're nearly ready to start wiring up our new features. Before we dive in, we first need real categories to select in our web UI. Let's quickly seed some new categories in the application. Add the following code to your seeds file in `priv/repo/seeds.exs`:
-
-```elixir
-for title <- ["Home Improvement", "Power Tools", "Gardening", "Books", "Education"] do
-  {:ok, _} = Hello.Catalog.create_category(%{title: title})
-end
-```
-
-We simply enumerate over a list of category titles and use the generated `create_category/1` function of our catalog context to persist the new records. We can run the seeds with `mix run`:
-
-```console
-$ mix run priv/repo/seeds.exs
-
-[debug] QUERY OK db=3.1ms decode=1.1ms queue=0.7ms idle=2.2ms
-INSERT INTO "categories" ("title","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Home Improvement", ~N[2025-02-03 19:39:53], ~N[2025-02-03 19:39:53]]
-[debug] QUERY OK db=1.2ms queue=1.3ms idle=12.3ms
-INSERT INTO "categories" ("title","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Power Tools", ~N[2025-02-03 19:39:53], ~N[2025-02-03 19:39:53]]
-[debug] QUERY OK db=1.1ms queue=1.1ms idle=15.1ms
-INSERT INTO "categories" ("title","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Gardening", ~N[2025-02-03 19:39:53], ~N[2025-02-03 19:39:53]]
-[debug] QUERY OK db=2.4ms queue=1.0ms idle=17.6ms
-INSERT INTO "categories" ("title","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Books", ~N[2025-02-03 19:39:53], ~N[2025-02-03 19:39:53]]
-```
-
-Perfect. Before we integrate categories in the web layer, we need to let our context know how to associate products and categories. First, open up `lib/hello/catalog/product.ex` and add the following association:
-
-```diff
-+ alias Hello.Catalog.Category
-
-  schema "products" do
-    field :description, :string
-    field :price, :decimal
-    field :title, :string
-    field :views, :integer
-
-+   many_to_many :categories, Category, join_through: "product_categories", on_replace: :delete
-
-    timestamps()
-  end
-
-```
-
-We used `Ecto.Schema`'s `many_to_many` macro to let Ecto know how to associate our product to multiple categories through the `"product_categories"` join table. We also used the `on_replace: :delete` option to declare that any existing join records should be deleted when we are changing our categories.
-
-With our schema associations set up, we can implement the selection of categories in our product form. To do so, we need to translate the user input of catalog IDs from the front-end to our many-to-many association. Fortunately Ecto makes this a breeze now that our schema is set up. Open up your catalog context and make the following changes:
-
-```diff
-+ alias Hello.Catalog.Category
-
-- def get_product!(id), do: Repo.get!(Product, id)
-+ def get_product!(id) do
-+   Product |> Repo.get!(id) |> Repo.preload(:categories)
-+ end
-
-  def create_product(attrs \\ %{}) do
-    %Product{}
--   |> Product.changeset(attrs)
-+   |> change_product(attrs)
-    |> Repo.insert()
-  end
-
-  def update_product(%Product{} = product, attrs) do
-    product
--   |> Product.changeset(attrs)
-+   |> change_product(attrs)
-    |> Repo.update()
-  end
-
-  def change_product(%Product{} = product, attrs \\ %{}) do
--   Product.changeset(product, attrs)
-+   categories = list_categories_by_id(attrs["category_ids"])
-
-+   product
-+   |> Repo.preload(:categories)
-+   |> Product.changeset(attrs)
-+   |> Ecto.Changeset.put_assoc(:categories, categories)
-  end
-
-+ def list_categories_by_id(nil), do: []
-+ def list_categories_by_id(category_ids) do
-+   Repo.all(from c in Category, where: c.id in ^category_ids)
-+ end
-```
-
-First, we added `Repo.preload` to preload our categories when we fetch a product. This will allow us to reference `product.categories` in our controllers, templates, and anywhere else we want to make use of category information. Next, we modified our `create_product` and `update_product` functions to call into our existing `change_product` function to produce a changeset. Within `change_product` we added a lookup to find all categories if the `"category_ids"` attribute is present. Then we preloaded categories and called `Ecto.Changeset.put_assoc` to place the fetched categories into the changeset. Finally, we implemented the `list_categories_by_id/1` function to query the categories matching the category IDs, or return an empty list if no `"category_ids"` attribute is present. Now our `create_product` and `update_product` functions receive a changeset with the category associations all ready to go once we attempt an insert or update against our repo.
-
-Next, let's expose our new feature to the web by adding the category input to our product form. To keep our form template tidy, let's write a new function to wrap up the details of rendering a category select input for our product. Open up your `ProductHTML` view in `lib/hello_web/controllers/product_html.ex` and key this in:
-
-```elixir
-  def category_opts(changeset) do
-    existing_ids =
-      changeset
-      |> Ecto.Changeset.get_change(:categories, [])
-      |> Enum.map(& &1.data.id)
-
-    for cat <- Hello.Catalog.list_categories() do
-      [key: cat.title, value: cat.id, selected: cat.id in existing_ids]
-    end
-  end
-```
-
-We added a new `category_opts/1` function which generates the select options for a multiple select tag we will add soon. We calculated the existing category IDs from our changeset, then used those values when we generate the select options for the input tag. We did this by enumerating over all of our categories and returning the appropriate `key`, `value`, and `selected` values. We marked an option as selected if the category ID was found in those category IDs in our changeset.
-
-With our `category_opts` function in place, we can open up `lib/hello_web/controllers/product_html/product_form.html.heex` and add:
-
-```diff
-  ...
-  <.input field={f[:views]} type="number" label="Views" />
-
-+ <.input field={f[:category_ids]} type="select" multiple options={category_opts(@changeset)} />
-
-  <.button>Save Product</.button>
-```
-
-We added a `category_select` above our save button. Now let's try it out. Next, let's show the product's categories in the product show template. Add the following code to the list in `lib/hello_web/controllers/product_html/show.html.heex`:
-
-```diff
-<.list>
-  ...
-+ <:item title="Categories">
-+   <ul>
-+     <li :for={cat <- @product.categories}>{cat.title}</li>
-+   </ul>
-+ </:item>
-</.list>
-```
-
-Now if we start the server with `mix phx.server` and visit [http://localhost:4000/products/new](http://localhost:4000/products/new), we'll see the new category multiple select input. Enter some valid product details, select a category or two, and click save.
-
-```text
-Title: Elixir Flashcards
-Description: Flash card set for the Elixir programming language
-Price: 5.000000
-Views: 0
-Categories:
-Education
-Books
-```
-
-It's not much to look at yet, but it works! We added relationships within our context complete with data integrity enforced by the database. Not bad. Let's keep building!
-
-## Cross-context dependencies
-
-Now that we have the beginnings of our product catalog features, let's begin to work on the other main features of our application – carting products from the catalog. In order to properly track products that have been added to a user's cart, we'll need a new place to persist this information, along with point-in-time product information like the price at time of carting. This is necessary so we can detect product price changes in the future. We know what we need to build, but now we need to decide where the cart functionality lives in our application.
-
-If we take a step back and think about the isolation of our application, the exhibition of products in our catalog distinctly differs from the responsibilities of managing a user's cart. A product catalog shouldn't care about the rules of our shopping cart system, and vice-versa. There's a clear need here for a separate context to handle the new cart responsibilities. Let's call it `ShoppingCart`.
-
-Let's create a `ShoppingCart` context to handle basic cart duties. Before we write code, let's imagine we have the following feature requirements:
-
-  1. Add products to a user's cart from the product show page
-  2. Store point-in-time product price information at time of carting
-  3. Store and update quantities in cart
-  4. Calculate and display sum of cart prices
-
-From the description, it's clear we need a `Cart` resource for storing the user's cart, along with a `CartItem` to track products in the cart. With our plan set, let's get to work.
-
-Initially we mentioned that the generators support scoping resources. For our cart, we want to scope by user. A user should only be able to manage their own cart. In our application, there is no authentication system and also no scope defined yet. While it is possible to create a scope from scratch, we will use the `mix phx.gen.auth` authentication generator which will create a user scope for us:
-
-```console
-mix phx.gen.auth Accounts User user
-
-An authentication system can be created in two different ways:
-- Using Phoenix.LiveView (default)
-- Using Phoenix.Controller only
-Do you want to create a LiveView based authentication system? [Yn] n
-
-...
-* creating lib/hello/accounts/scope.ex
-...
-* injecting config/config.exs
-...
-
-Please re-fetch your dependencies with the following command:
-
-    $ mix deps.get
-
-Remember to update your repository by running migrations:
-
-    $ mix ecto.migrate
-
-Once you are ready, visit "/user/register"
-to create your account and then access "/dev/mailbox" to
-see the account confirmation email.
-```
-
-After following the instructions to re-fetch dependencies and migrating the database, we can start the server with `mix phx.server` and re-visit the home page [`http://localhost:4000/`](http://localhost:4000/) and should see new registration and login links at the top of the page. On the registration page, create a new user. In development, an confirmation email is sent to the dev mailbox, which is accessible at [`http://localhost:4000/dev/mailbox`](http://localhost:4000/dev/mailbox). After clicking the confirmation link, you should be successfully logged in.
-
-Looking at the generated scope file `lib/hello/accounts/scope.ex`:
-
-```elixir
-defmodule Hello.Accounts.Scope do
-  ...
-  alias Hello.Accounts.User
-
-  defstruct user: nil
-
-  @doc """
-  Creates a scope for the given user.
-
-  Returns nil if no user is given.
-  """
-  def for_user(%User{} = user) do
-    %__MODULE__{user: user}
-  end
-
-  def for_user(nil), do: nil
-end
-```
-
-we can see that it is simply a struct with a `user` field. The authentication system ensures that the `current_scope` assign is set to an `%Scope{}` struct that identifies the current user and our generators can rely on the struct to properly scope resources.
-
-Let's generate our new context:
-
-```console
-$ mix phx.gen.context ShoppingCart Cart carts
-
-* creating lib/hello/shopping_cart/cart.ex
-* creating priv/repo/migrations/20250205203128_create_carts.exs
-* creating lib/hello/shopping_cart.ex
-* injecting lib/hello/shopping_cart.ex
-* creating test/hello/shopping_cart_test.exs
-* injecting test/hello/shopping_cart_test.exs
-* creating test/support/fixtures/shopping_cart_fixtures.ex
-* injecting test/support/fixtures/shopping_cart_fixtures.ex
-
-Remember to update your repository by running migrations:
-
-    $ mix ecto.migrate
-```
-
-We generated our new context `ShoppingCart`, with a new `ShoppingCart.Cart` schema to tie a user to their cart which holds cart items. With our cart in place, let's generate our cart items:
-
-```console
-$ mix phx.gen.context ShoppingCart CartItem cart_items \
-cart_id:references:carts product_id:references:products \
-price_when_carted:decimal quantity:integer --no-scope
-
-You are generating into an existing context.
-...
-Would you like to proceed? [Yn] y
-* creating lib/hello/shopping_cart/cart_item.ex
-* creating priv/repo/migrations/20250205213410_create_cart_items.exs
-* injecting lib/hello/shopping_cart.ex
-* injecting test/hello/shopping_cart_test.exs
-* injecting test/support/fixtures/shopping_cart_fixtures.ex
-
-Remember to update your repository by running migrations:
-
-    $ mix ecto.migrate
-```
-
-We generated a new resource inside our `ShoppingCart` named `CartItem`. This schema and table will hold references to a cart and product, along with the price at the time we added the item to our cart, and the quantity the user wishes to purchase. Let's touch up the generated migration file in `priv/repo/migrations/*_create_cart_items.ex`:
-
-```diff
-    create table(:cart_items) do
--     add :price_when_carted, :decimal
-+     add :price_when_carted, :decimal, precision: 15, scale: 6, null: false
-      add :quantity, :integer
--     add :cart_id, references(:carts, on_delete: :nothing)
-+     add :cart_id, references(:carts, on_delete: :delete_all)
--     add :product_id, references(:products, on_delete: :nothing)
-+     add :product_id, references(:products, on_delete: :delete_all)
-
-      timestamps()
-    end
-
--   create index(:cart_items, [:cart_id])
-    create index(:cart_items, [:product_id])
-+   create unique_index(:cart_items, [:cart_id, :product_id])
-```
-
-We used the `:delete_all` strategy again to enforce data integrity. This way, when a cart or product is deleted from the application, we don't have to rely on application code in our `ShoppingCart` or `Catalog` contexts to worry about cleaning up the records. This keeps our application code decoupled and the data integrity enforcement where it belongs – in the database. We also added a unique constraint to ensure a duplicate product is not allowed to be added to a cart. As with the `product_categories` table, using a multi-column index lets us remove the separate index for the leftmost field (`cart_id`). With our database tables in place, we can now migrate up:
-
-```console
-$ mix ecto.migrate
-
-16:59:51.941 [info] == Running 20250205203342 Hello.Repo.Migrations.CreateCarts.change/0 forward
-
-16:59:51.945 [info] create table carts
-
-16:59:51.952 [info] == Migrated 20250205203342 in 0.0s
-
-16:59:51.988 [info] == Running 20250205213410 Hello.Repo.Migrations.CreateCartItems.change/0 forward
-
-16:59:51.988 [info] create table cart_items
-
-16:59:51.998 [info] create index cart_items_cart_id_index
-
-16:59:52.000 [info] create index cart_items_product_id_index
-
-16:59:52.001 [info] create index cart_items_cart_id_product_id_index
-
-16:59:52.002 [info] == Migrated 20250205213410 in 0.0s
-```
-
-Our database is ready to go with new `carts` and `cart_items` tables, but now we need to map that back into application code. You may be wondering how we can mix database foreign keys across different tables and how that relates to the context pattern of isolated, grouped functionality. Let's jump in and discuss the approaches and their tradeoffs.
-
-### Cross-context data
-
-So far, we've done a great job isolating the two main contexts of our application from each other, but now we have a necessary dependency to handle.
-
-Our `Catalog.Product` resource serves to keep the responsibilities of representing a product inside the catalog, but ultimately for an item to exist in the cart, a product from the catalog must be present. Given this, our `ShoppingCart` context will have a data dependency on the `Catalog` context. With that in mind, we have two options. One is to expose APIs on the `Catalog` context that allows us to efficiently fetch product data for use in the `ShoppingCart` system, which we would manually stitch together. Or we can use database joins to fetch the dependent data. Both are valid options given your tradeoffs and application size, but joining data from the database when you have a hard data dependency is just fine for a large class of applications and is the approach we will take here.
-
-Now that we know where our data dependencies exist, let's add our schema associations so we can tie shopping cart items to products. First, let's make a quick change to our cart schema in `lib/hello/shopping_cart/cart.ex` to associate a cart to its items:
-
-```diff
-  schema "carts" do
--   field :user_id, :id
-+   belongs_to :user, Hello.Accounts.User
-+   has_many :items, Hello.ShoppingCart.CartItem
-
-    timestamps()
-  end
-```
-
-Now that our cart is associated to the items we place in it, let's set up the cart item associations inside `lib/hello/shopping_cart/cart_item.ex`:
-
-```diff
-  schema "cart_items" do
-    field :price_when_carted, :decimal
-    field :quantity, :integer
--   field :cart_id, :id
--   field :product_id, :id
-
-+   belongs_to :cart, Hello.ShoppingCart.Cart
-+   belongs_to :product, Hello.Catalog.Product
-
-    timestamps()
-  end
-
-  @doc false
-  def changeset(cart_item, attrs) do
-    cart_item
-    |> cast(attrs, [:price_when_carted, :quantity])
-    |> validate_required([:price_when_carted, :quantity])
-+   |> validate_number(:quantity, greater_than_or_equal_to: 0, less_than: 100)
-  end
-```
-
-First, we replaced the `cart_id` field with a standard `belongs_to` pointing at our `ShoppingCart.Cart` schema. Next, we replaced our `product_id` field by adding our first cross-context data dependency with a `belongs_to` for the `Catalog.Product` schema. Here, we intentionally coupled the data boundaries because it provides exactly what we need: an isolated context API with the bare minimum knowledge necessary to reference a product in our system. Next, we added a new validation to our changeset. With `validate_number/3`, we ensure any quantity provided by user input is between 0 and 100.
-
-With our schemas in place, we can start integrating the new data structures and `ShoppingCart` context APIs into our web-facing features.
-
-### Adding Shopping Cart functions
-
-As we mentioned before, the context generators are only a starting point for our application. We can and should write well-named, purpose built functions to accomplish the goals of our context. We have a few new features to implement. First, we need to ensure every user of our application is granted a cart if one does not yet exist. From there, we can then allow users to add items to their cart, update item quantities, and calculate cart totals. Let's get started!
-
-We won't focus on a real user authentication system at this point, but by the time we're done, you'll be able to naturally integrate one with what we've written here. To simulate a current user session, open up your `lib/hello_web/router.ex` and key this in:
-
-Because we used `mix phx.gen.auth`, we already have a real authentication system in place. We can use the `current_scope` assign to access the currently authenticated user. Let's add a new plug that assigns a cart if there is an authenticated user:
-
-```diff
-  pipeline :browser do
-    plug :accepts, ["html"]
-    plug :fetch_session
-    plug :fetch_live_flash
-    plug :put_root_layout, html: {HelloWeb.LayoutView, :root}
-    plug :protect_from_forgery
-    plug :put_secure_browser_headers
-    plug :fetch_current_scope_for_user
-+   plug :fetch_current_cart
-  end
-
-+ alias Hello.ShoppingCart
-+
-+ defp fetch_current_cart(%{assigns: %{current_scope: scope}} = conn, _opts) when not is_nil(scope) do
-+   if cart = ShoppingCart.get_cart(scope) do
-+     assign(conn, :cart, cart)
-+   else
-+     {:ok, new_cart} = ShoppingCart.create_cart(scope)
-+     assign(conn, :cart, new_cart)
-+   end
-+ end
-+
-+ defp fetch_current_cart(conn, _opts), do: conn
-```
-
-We added a new `:fetch_current_cart` plug which either finds a cart for the user UUID or creates a cart for the current user and assigns the result in the connection assigns. We'll need to implement our `ShoppingCart.get_cart/1`, but let's add our routes first.
-
-We'll need to implement a cart controller for handling cart operations like viewing a cart, updating quantities, and initiating the checkout process, as well as a cart items controller for adding and removing individual items to and from the cart. The authentication system already generated different router scopes that have different authentication requirements:
-
-```elixir
-...
-  ## Authentication routes
-
-  scope "/", HelloWeb do
-    pipe_through [:browser, :redirect_if_user_is_authenticated]
-
-    get "/user/register", UserRegistrationController, :new
-    post "/user/register", UserRegistrationController, :create
-  end
-
-  scope "/", HelloWeb do
-    pipe_through [:browser, :require_authenticated_user]
-
-    get "/user/settings", UserSettingsController, :edit
-    put "/user/settings", UserSettingsController, :update
-    get "/user/settings/confirm-email/:token", UserSettingsController, :confirm_email
-  end
-...
-```
-
-As you can see, the registration route has a `:redirect_if_user_is_authenticated` plug, which means it will redirect to the home page if the user is already authenticated. The user settings routes use a `:require_authenticated_user` plug, which means they will redirect to the log in page if the user is not authenticated. These plugs are defined in the `lib/hello_web/user_auth.ex` module.
-
-For our cart routes, we want to only allow access to authenticated users. Add the following routes to your router in `lib/hello_web/router.ex`:
-
-```diff
-   scope "/", HelloWeb do
-     pipe_through :browser
-
-     get "/", PageController, :index
-     resources "/products", ProductController
-   end
-
-+  scope "/", HelloWeb do
-+    pipe_through [:browser, :require_authenticated_user]
-+
-+    resources "/cart_items", CartItemController, only: [:create, :delete]
-+
-+    get "/cart", CartController, :show
-+    put "/cart", CartController, :update
-+  end
-```
-
-We added a `resources` declaration for a `CartItemController`, which will wire up the routes for a create and delete action for adding and removing individual cart items. Next, we added two new routes pointing at a `CartController`. The first route, a GET request, will map to our show action, to show the cart contents. The second route, a PUT request, will handle the submission of a form for updating our cart quantities.
-
-With our routes in place, let's add the ability to add an item to our cart from the product show page. Create a new file at `lib/hello_web/controllers/cart_item_controller.ex` and key this in:
-
-```elixir
-defmodule HelloWeb.CartItemController do
-  use HelloWeb, :controller
-
-  alias Hello.ShoppingCart
-
-  def create(conn, %{"product_id" => product_id}) do
-    case ShoppingCart.add_item_to_cart(conn.assigns.current_scope, conn.assigns.cart, product_id) do
-      {:ok, _item} ->
-        conn
-        |> put_flash(:info, "Item added to your cart")
-        |> redirect(to: ~p"/cart")
-
-      {:error, _changeset} ->
-        conn
-        |> put_flash(:error, "There was an error adding the item to your cart")
-        |> redirect(to: ~p"/cart")
-    end
-  end
-
-  def delete(conn, %{"id" => product_id}) do
-    {:ok, _cart} = ShoppingCart.remove_item_from_cart(conn.assigns.current_scope, conn.assigns.cart, product_id)
-    redirect(conn, to: ~p"/cart")
-  end
-end
-```
-
-We defined a new `CartItemController` with the create and delete actions that we declared in our router. For `create`, we call a `ShoppingCart.add_item_to_cart/2` function which we'll implement in a moment. If successful, we show a flash successful message and redirect to the cart show page; else, we show a flash error message and redirect to the cart show page. For `delete`, we'll call a `remove_item_from_cart` function which we'll implement on our `ShoppingCart` context  and then redirect back to the cart show page. We haven't implemented these two shopping cart functions yet, but notice how their names scream their intent: `add_item_to_cart` and `remove_item_from_cart` make it obvious what we are accomplishing here. It also allows us to spec out our web layer and context APIs without thinking about all the implementation details at once.
-
-Let's implement the new interface for the `ShoppingCart` context API in `lib/hello/shopping_cart.ex`:
-
-```diff
-+  alias Hello.Catalog
--  alias Hello.ShoppingCart.Cart
-+  alias Hello.ShoppingCart.{Cart, CartItem}
-   alias Hello.Accounts.Scope
-
-+  def get_cart(%Scope{} = scope) do
-+    Repo.one(
-+      from(c in Cart,
-+        where: c.user_id == ^scope.user.id,
-+        left_join: i in assoc(c, :items),
-+        left_join: p in assoc(i, :product),
-+        order_by: [asc: i.inserted_at],
-+        preload: [items: {i, product: p}]
-+      )
-+    )
-+  end
-
-   def create_cart(%Scope{} = scope, attrs \\ %{}) do
-     with {:ok, cart = %Cart{}} <-
-            %Cart{}
-            |> Cart.changeset(attrs, scope)
-            |> Repo.insert() do
-       broadcast(scope, {:created, cart})
--      {:ok, cart}
-+      {:ok, get_cart!(scope, cart.id)}
-     end
-   end
-+
-+  def add_item_to_cart(%Scope{} = scope, %Cart{} = cart, product_id) do
-+    true = cart.user_id == scope.user.id
-+    product = Catalog.get_product!(product_id)
-+
-+    %CartItem{quantity: 1, price_when_carted: product.price}
-+    |> CartItem.changeset(%{})
-+    |> Ecto.Changeset.put_assoc(:cart, cart)
-+    |> Ecto.Changeset.put_assoc(:product, product)
-+    |> Repo.insert(
-+      on_conflict: [inc: [quantity: 1]],
-+      conflict_target: [:cart_id, :product_id]
-+    )
-+  end
-+
-+  def remove_item_from_cart(%Scope{} = scope, %Cart{} = cart, product_id) do
-+    true = cart.user_id == scope.user.id
-+
-+    {1, _} =
-+      Repo.delete_all(
-+        from(i in CartItem,
-+          where: i.cart_id == ^cart.id,
-+          where: i.product_id == ^product_id
-+        )
-+      )
-+
-+    {:ok, get_cart(scope)}
-+  end
-```
-
-We started by implementing `get_cart/1` which fetches our cart and joins the cart items, and their products so that we have the full cart populated with all preloaded data. Next, we modified our `create_cart` function to use `get_cart` to reload the cart contents.
-
-Next, we wrote our new `add_item_to_cart/3` function which accepts a scope, a cart struct and a product id. We proceed to fetch the product with `Catalog.get_product!/1`, showing how contexts can naturally invoke other contexts if required. You could also have chosen to receive the product as argument and you would achieve similar results. Then we used an upsert operation against our repo to either insert a new cart item into the database, or increase the quantity by one if it already exists in the cart. This is accomplished via the `on_conflict` and `conflict_target` options, which tells our repo how to handle an insert conflict.
-
-Finally, we implemented `remove_item_from_cart/3` where we simply issue a `Repo.delete_all` call with a query to delete the cart item in our cart that matches the product ID. Finally, we reload the cart contents by calling `get_cart/1`.
-
-With our new cart functions in place, we can now expose the "Add to cart" button on the product catalog show page. Open up your template in `lib/hello_web/controllers/product_html/show.html.heex` and make the following changes:
-
-```diff
-...
-     <.link href={~p"/products/#{@product}/edit"}>
-       <.button>Edit product</.button>
-     </.link>
-+    <.link href={~p"/cart_items?product_id=#{@product.id}"} method="post">
-+      <.button>Add to cart</.button>
-+    </.link>
-...
-```
-
-The `link` function component from `Phoenix.Component` accepts a `:method` attribute to issue an HTTP verb when clicked, instead of the default GET request. With this link in place, the "Add to cart" link will issue a POST request, which will be matched by the route we defined in router which dispatches to the `CartItemController.create/2` function.
-
-Let's try it out. Start your server with `mix phx.server` and visit a product page. If we try clicking the add to cart link, we'll be greeted by an error page. If you are authenticated the following logs should be visible in the console:
-
-```text
-[info] POST /cart_items
-[debug] Processing with HelloWeb.CartItemController.create/2
-  Parameters: %{"_method" => "post", "product_id" => "1", ...}
-  Pipelines: [:browser, :require_authenticated_user]
-[debug] QUERY OK source="user_tokens" db=2.4ms idle=1340.8ms
-...
-[debug] QUERY OK source="cart_items" db=2.5ms
-INSERT INTO "cart_items" ...
-[info] Sent 302 in 24ms
-[info] GET /cart
-[debug] Processing with HelloWeb.CartController.show/2
-  Parameters: %{}
-  Pipelines: [:browser, :require_authenticated_user]
-[debug] QUERY OK source="user_tokens" db=1.6ms idle=430.2ms
-...
-[debug] QUERY OK source="carts" db=1.9ms idle=1798.5ms
-...
-[info] Sent 500 in 18ms
-[error] ** (UndefinedFunctionError) function HelloWeb.CartController.init/1 is undefined (module HelloWeb.CartController is not available)
-    ...
-```
-
-It's working! Kind of. If we follow the logs, we see our POST to the `/cart_items` path. Next, we can see our `ShoppingCart.add_item_to_cart` function successfully inserted a row into the `cart_items` table, and then we issued a redirect to `/cart`. Before our error, we also see a query to the `carts` table, which means we're fetching the current user's cart. So far so good. We know our `CartItem` controller and new `ShoppingCart` context functions are doing their jobs, but we've hit our next unimplemented feature when the router attempts to dispatch to a nonexistent cart controller. Let's create the cart controller, view, and template to display and manage user carts.
-
-Create a new file at `lib/hello_web/controllers/cart_controller.ex` and key this in:
-
-```elixir
-defmodule HelloWeb.CartController do
-  use HelloWeb, :controller
-
-  alias Hello.ShoppingCart
-
-  def show(conn, _params) do
-    render(conn, :show, changeset: ShoppingCart.change_cart(conn.assigns.current_scope, conn.assigns.cart))
-  end
-end
-```
-
-We defined a new cart controller to handle the `get "/cart"` route. For showing a cart, we render a `"show.html"` template which we'll create in a moment. We know we need to allow the cart items to be changed by quantity updates, so right away we know we'll need a cart changeset. Fortunately, the context generator included a `ShoppingCart.change_cart/1` function, which we'll use. We pass it our cart struct which is already in the connection assigns thanks to the `fetch_current_cart` plug we defined in the router.
-
-Next, we can implement the view and template. Create a new view file at `lib/hello_web/controllers/cart_html.ex` with the following content:
-
-```elixir
-defmodule HelloWeb.CartHTML do
-  use HelloWeb, :html
-
-  alias Hello.ShoppingCart
-
-  embed_templates "cart_html/*"
-
-  def currency_to_str(%Decimal{} = val), do: "$#{Decimal.round(val, 2)}"
-end
-```
-
-We created a view to render our `show.html` template and aliased our `ShoppingCart` context so it will be in scope for our template. We'll need to display the cart prices like product item price, cart total, etc, so we defined a `currency_to_str/1` which takes our decimal struct, rounds it properly for display, and prepends a USD dollar sign.
-
-Next we can create the template at `lib/hello_web/controllers/cart_html/show.html.heex`:
-
-```heex
-<.header>
-  My Cart
-  <:subtitle :if={@cart.items == []}>Your cart is empty</:subtitle>
-</.header>
-
-<div :if={@cart.items !== []}>
-  <.form :let={f} for={@changeset} action={~p"/cart"}>
-    <.inputs_for :let={%{data: item} = item_form} field={f[:items]}>
-      <.input field={item_form[:quantity]} type="number" label={item.product.title} />
-      {currency_to_str(ShoppingCart.total_item_price(item))}
-    </.inputs_for>
-    <.button>Update cart</.button>
-  </.form>
-  <b>Total</b>: {currency_to_str(ShoppingCart.total_cart_price(@cart))}
-</div>
-
-<.back navigate={~p"/products"}>Back to products</.back>
-```
-
-We started by showing the empty cart message if our preloaded `cart.items` is empty. If we have items, we use the `form` component provided by our `HelloWeb.CoreComponents` to take our cart changeset that we assigned in the `CartController.show/2` action and create a form which maps to our cart controller `update/2` action. Within the form, we use the [`inputs_for`](https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#inputs_for/1) component to render inputs for the nested cart items. This will allow us to map item inputs back together when the form is submitted. Next, we display a number input for the item quantity and label it with the product title. We finish the item form by converting the item price to string. We haven't written the `ShoppingCart.total_item_price/1` function yet, but again we employed the idea of clear, descriptive public interfaces for our contexts. After rendering inputs for all the cart items, we show an "update cart" submit button, along with the total price of the entire cart. This is accomplished with another new `ShoppingCart.total_cart_price/1` function which we'll implement in a moment. Finally, we added a `back` component to go back to our products page.
-
-We're almost ready to try out our cart page, but first we need to implement our new currency calculation functions. Open up your shopping cart context at `lib/hello/shopping_cart.ex` and add these new functions:
-
-```elixir
-  def total_item_price(%CartItem{} = item) do
-    Decimal.mult(item.product.price, item.quantity)
-  end
-
-  def total_cart_price(%Cart{} = cart) do
-    Enum.reduce(cart.items, 0, fn item, acc ->
-      item
-      |> total_item_price()
-      |> Decimal.add(acc)
-    end)
-  end
-```
-
-We implemented `total_item_price/1` which accepts a `%CartItem{}` struct. To calculate the total price, we simply take the preloaded product's price and multiply it by the item's quantity. We used `Decimal.mult/2` to take our decimal currency struct and multiply it with proper precision. Similarly for calculating the total cart price, we implemented a `total_cart_price/1` function which accepts the cart and sums the preloaded product prices for items in the cart. We again make use of the `Decimal` functions to add our decimal structs together.
-
-Now that we can calculate price totals, let's try it out! Visit [`http://localhost:4000/cart`](http://localhost:4000/cart) and you should already see your first item in the cart. Going back to the same product and clicking "add to cart" will show our upsert in action. Your quantity should now be two. Nice work!
-
-Our cart page is almost complete, but submitting the form will yield yet another error.
-
-```text
-[info] POST /cart
-...
-[error] ** (UndefinedFunctionError) function HelloWeb.CartController.update/2 is undefined or private
-```
-
-Let's head back to our `CartController` at `lib/hello_web/controllers/cart_controller.ex` and implement the update action:
-
-```elixir
-  def update(conn, %{"cart" => cart_params}) do
-    case ShoppingCart.update_cart(conn.assigns.current_scope, conn.assigns.cart, cart_params) do
-      {:ok, _cart} ->
-        redirect(conn, to: ~p"/cart")
-
-      {:error, _changeset} ->
-        conn
-        |> put_flash(:error, "There was an error updating your cart")
-        |> redirect(to: ~p"/cart")
-    end
-  end
-```
-
-We started by plucking out the cart params from the form submit. Next, we call our existing `ShoppingCart.update_cart/2` function which was added by the context generator. We'll need to make some changes to this function, but the interface is good as is. If the update is successful, we redirect back to the cart page, otherwise we show a flash error message and send the user back to the cart page to fix any mistakes. Out-of-the-box, our `ShoppingCart.update_cart/2` function only concerned itself with casting the cart params into a changeset and updates it against our repo. For our purposes, we now need it to handle nested cart item associations, and most importantly, business logic for how to handle quantity updates like zero-quantity items being removed from the cart.
-
-Head back over to your shopping cart context in `lib/hello/shopping_cart.ex` and replace your `update_cart/2` function with the following implementation:
-
-```elixir
-  def update_cart(%Scope{} = scope, %Cart{} = cart, attrs) do
-    true = cart.user_id == scope.user.id
-    
-    changeset =
-      cart
-      |> Cart.changeset(attrs, scope)
-      |> Ecto.Changeset.cast_assoc(:items, with: &CartItem.changeset/2)
-
-    Ecto.Multi.new()
-    |> Ecto.Multi.update(:cart, changeset)
-    |> Ecto.Multi.delete_all(:discarded_items, fn %{cart: cart} ->
-      from(i in CartItem, where: i.cart_id == ^cart.id and i.quantity == 0)
-    end)
-    |> Repo.transaction()
-    |> case do
-      {:ok, %{cart: cart}} ->
-        broadcast(scope, {:updated, cart})
-        {:ok, cart}
-  
-      {:error, :cart, changeset, _changes_so_far} ->
-        {:error, changeset}
-    end
-  end
-```
-
-We started much like how our out-of-the-box code started – we take the cart struct and cast the user input to a cart changeset, except this time we use `Ecto.Changeset.cast_assoc/3` to cast the nested item data into `CartItem` changesets. Remember the [`<.inputs_for />`](https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#inputs_for/1) call in our cart form template? That hidden ID data is what allows Ecto's `cast_assoc` to map item data back to existing item associations in the cart. Next we use `Ecto.Multi.new/0`, which you may not have seen before. Ecto's `Multi` is a feature that allows lazily defining a chain of named operations to eventually execute inside a database transaction. Each operation in the multi chain receives the values from the previous steps and executes until a failed step is encountered. When an operation fails, the transaction is rolled back and an error is returned, otherwise the transaction is committed.
-
-For our multi operations, we start by issuing an update of our cart, which we named `:cart`. After the cart update is issued, we perform a multi `delete_all` operation, which takes the updated cart and applies our zero-quantity logic. We prune any items in the cart with zero quantity by returning an ecto query that finds all cart items for this cart with an empty quantity. Calling `Repo.transaction/1` with our multi will execute the operations in a new transaction and we return the success or failure result to the caller just like the original function.
-
-Let's head back to the browser and try it out. Add a few products to your cart, update the quantities, and watch the values changes along with the price calculations. Setting any quantity to 0 will also remove the item. You can also try logging out and registering a new user to see how the carts are scoped to the current user. Pretty neat!
-
-## Adding an Orders context
-
-With our `Catalog` and `ShoppingCart` contexts, we're seeing first-hand how our well-considered modules and function names are yielding clear and maintainable code. Our last order of business is to allow the user to initiate the checkout process. We won't go as far as integrating payment processing or order fulfillment, but we'll get you started in that direction. Like before, we need to decide where code for completing an order should live. Is it part of the catalog? Clearly not, but what about the shopping cart? Shopping carts are related to orders – after all, the user has to add items in order to purchase any products – but should the order checkout process be grouped here?
-
-If we stop and consider the order process, we'll see that orders involve related, but distinctly different data from the cart contents. Also, business rules around the checkout process are much different than carting. For example, we may allow a user to add a back-ordered item to their cart, but we could not allow an order with no inventory to be completed. Additionally, we need to capture point-in-time product information when an order is completed, such as the price of the items *at payment transaction time*. This is essential because a product price may change in the future, but the line items in our order must always record and display what we charged at time of purchase. For these reasons, we can start to see that ordering can stand on its own with its own data concerns and business rules.
-
-Naming wise, `Orders` clearly defines our context, so let's get started by again taking advantage of the context generators. Note that the `user` scope generated by `mix phx.gen.auth` is marked as default scope (in your `config/config.exs`), therefore we don't need to specify it in our command. There can be different scopes in an application, in which case the `--scope` option can be used when running the generators. Run the following command in your console:
-
-```console
-$ mix phx.gen.context Orders Order orders total_price:decimal
-
-* creating lib/hello/orders/order.ex
-* creating priv/repo/migrations/20250209214612_create_orders.exs
-* creating lib/hello/orders.ex
-* injecting lib/hello/orders.ex
-* creating test/hello/orders_test.exs
-* injecting test/hello/orders_test.exs
-* creating test/support/fixtures/orders_fixtures.ex
-* injecting test/support/fixtures/orders_fixtures.ex
-
-Remember to update your repository by running migrations:
-
-    $ mix ecto.migrate
-```
-
-We generated an `Orders` context. The order is automatically scoped to the current user and added a `total_price` column. With our starting point in place, let's open up the newly created migration in `priv/repo/migrations/*_create_orders.exs` and make the following changes:
-
-```diff
-  def change do
-    create table(:orders) do
--     add :total_price, :decimal
-+     add :total_price, :decimal, precision: 15, scale: 6, null: false
-      add :user_id, references(:user, type: :id, on_delete: :delete_all)
-
-      timestamps()
-    end
-  end
-```
-
-Like we did previously, we gave appropriate precision and scale options for our decimal column which will allow us to store currency without precision loss. We also added a not-null constraint to enforce all orders to have a price.
-
-The orders table alone doesn't hold much information, but we know we'll need to store point-in-time product price information of all the items in the order. For that, we'll add an additional struct for this context named `LineItem`. Line items will capture the price of the product *at payment transaction time*. Please run the following command:
-
-```console
-$ mix phx.gen.context Orders LineItem order_line_items \
-price:decimal quantity:integer \
-order_id:references:orders product_id:references:products --no-scope
-
-You are generating into an existing context.
-...
-Would you like to proceed? [Yn] y
-* creating lib/hello/orders/line_item.ex
-* creating priv/repo/migrations/20250209215050_create_order_line_items.exs
-* injecting lib/hello/orders.ex
-* injecting test/hello/orders_test.exs
-* injecting test/support/fixtures/orders_fixtures.ex
-
-Remember to update your repository by running migrations:
-
-    $ mix ecto.migrate
-```
-
-We used the `phx.gen.context` command to generate the `LineItem` Ecto schema and inject supporting functions into our orders context. Like before, let's modify the migration in `priv/repo/migrations/*_create_order_line_items.exs` and make the following decimal field changes:
-
-```diff
-  def change do
-    create table(:order_line_items) do
--     add :price, :decimal
-+     add :price, :decimal, precision: 15, scale: 6, null: false
-      add :quantity, :integer
-      add :order_id, references(:orders, on_delete: :nothing)
-      add :product_id, references(:products, on_delete: :nothing)
-
-      timestamps()
-    end
-
-    create index(:order_line_items, [:order_id])
-    create index(:order_line_items, [:product_id])
-  end
-```
-
-With our migration in place, let's wire up our orders and line items associations in `lib/hello/orders/order.ex`:
-
-```diff
-  schema "orders" do
-    field :total_price, :decimal
--   field :user_id, :id
-
-+   belongs_to :user, Hello.Accounts.User
-+   has_many :line_items, Hello.Orders.LineItem
-+   has_many :products, through: [:line_items, :product]
-
-    timestamps()
-  end
-```
-
-We used `has_many :line_items` to associate orders and line items, just like we've seen before. Next, we used the `:through` feature of `has_many`, which allows us to instruct ecto how to associate resources across another relationship. In this case, we can associate products of an order by finding all products through associated line items. Next, let's wire up the association in the other direction in `lib/hello/orders/line_item.ex`:
-
-```diff
-  schema "order_line_items" do
-    field :price, :decimal
-    field :quantity, :integer
--   field :order_id, :id
--   field :product_id, :id
-
-+   belongs_to :order, Hello.Orders.Order
-+   belongs_to :product, Hello.Catalog.Product
-
-    timestamps()
-  end
-```
-
-We used `belongs_to` to associate line items to orders and products. With our associations in place, we can start integrating the web interface into our order process. Open up your router `lib/hello_web/router.ex` and add the following line:
-
-```diff
-  scope "/", HelloWeb do
-    pipe_through [:browser, :require_authenticated_user]
-
-    resources "/cart_items", CartItemController, only: [:create, :delete]
-
-    get "/cart", CartController, :show
-    put "/cart", CartController, :update
-
-+   resources "/orders", OrderController, only: [:create, :show]
-  end
-```
-
-We wired up `create` and `show` routes for our generated `OrderController`, since these are the only actions we need at the moment. With our routes in place, we can now migrate up:
-
-```console
-$ mix ecto.migrate
-
-17:14:37.715 [info] == Running 20250209214612 Hello.Repo.Migrations.CreateOrders.change/0 forward
-
-17:14:37.720 [info] create table orders
-
-17:14:37.755 [info] == Migrated 20250209214612 in 0.0s
-
-17:14:37.784 [info] == Running 20250209215050 Hello.Repo.Migrations.CreateOrderLineItems.change/0 forward
-
-17:14:37.785 [info] create table order_line_items
-
-17:14:37.795 [info] create index order_line_items_order_id_index
-
-17:14:37.796 [info] create index order_line_items_product_id_index
-
-17:14:37.798 [info] == Migrated 20250209215050 in 0.0s
-```
-
-Before we render information about our orders, we need to ensure our order data is fully populated and can be looked up by a current user. Open up your orders context in `lib/hello/orders.ex` and adjust your `get_order!/2` to include a preload:
-
-```diff
-   def get_order!(%Scope{} = scope, id) do
--    Repo.get_by!(Order, id: id, user_id: scope.user.id)
-+    Order
-+    |> Repo.get_by!(id: id, user_id: scope.user.id)
-+    |> Repo.preload([line_items: [:product]])
-   end
-```
-
-To complete an order, our cart page can issue a POST to the `OrderController.create` action, but we need to implement the operations and logic to actually complete an order. Like before, we'll start at the web interface. Create a new file at `lib/hello_web/controllers/order_controller.ex` and key this in:
-
-```elixir
-defmodule HelloWeb.OrderController do
-  use HelloWeb, :controller
-
-  alias Hello.Orders
-
-  def create(conn, _) do
-    case Orders.complete_order(conn.assigns.current_scope, conn.assigns.cart) do
-      {:ok, order} ->
-        conn
-        |> put_flash(:info, "Order created successfully.")
-        |> redirect(to: ~p"/orders/#{order}")
-
-      {:error, _reason} ->
-        conn
-        |> put_flash(:error, "There was an error processing your order")
-        |> redirect(to: ~p"/cart")
-    end
-  end
-end
-```
-
-We wrote the `create` action to call an as-yet-implemented `Orders.complete_order/2` function. Our code is technically "creating" an order, but it's important to step back and consider the naming of your interfaces. The act of *completing* an order is extremely important in our system. Money changes hands in a transaction, physical goods could be automatically shipped, etc. Such an operation deserves a better, more obvious function name, such as `complete_order`. If the order is completed successfully we redirect to the show page, otherwise a flash error is shown as we redirect back to the cart page.
-
-Here is also a good opportunity to highlight that contexts can naturally work with data defined by other contexts too. This will be especially common with data that is used throughout the application, such as the cart here (but it can also be the current user or the current project, and so forth, depending on your project).
-
-Now we can implement our `Orders.complete_order/2` function. To complete an order, our job will require a few operations:
-
-  1. A new order record must be persisted with the total price of the order
-  2. All items in the cart must be transformed into new order line items records
-    with quantity and point-in-time product price information
-  3. After successful order insert (and eventual payment), items must be pruned
-    from the cart
-
-From our requirements alone, we can start to see why a generic `create_order` function doesn't cut it. Let's implement this new function in `lib/hello/orders.ex`:
-
-```elixir
-  alias Hello.Orders.LineItem
-  alias Hello.ShoppingCart
-
-  def complete_order(%Scope{} = scope, %ShoppingCart.Cart{} = cart) do
-    true = cart.user_id == scope.user.id
-
-    line_items =
-      Enum.map(cart.items, fn item ->
-        %{product_id: item.product_id, price: item.product.price, quantity: item.quantity}
-      end)
-
-    order =
-      Ecto.Changeset.change(%Order{},
-        user_id: scope.user.id,
-        total_price: ShoppingCart.total_cart_price(cart),
-        line_items: line_items
-      )
-
-    Ecto.Multi.new()
-    |> Ecto.Multi.insert(:order, order)
-    |> Ecto.Multi.run(:prune_cart, fn _repo, _changes ->
-      ShoppingCart.prune_cart_items(scope, cart)
-    end)
-    |> Repo.transaction()
-    |> case do
-      {:ok, %{order: order}} ->
-        broadcast(scope, {:created, order})
-        {:ok, order}
-
-      {:error, name, value, _changes_so_far} ->
-        {:error, {name, value}}
-    end
-  end
-```
-
-We started by mapping the `%ShoppingCart.CartItem{}`'s in our shopping cart into a map of order line items structs. The job of the order line item record is to capture the price of the product *at payment transaction time*, so we reference the product's price here. Next, we create a bare order changeset with `Ecto.Changeset.change/2` and associate our user UUID, set our total price calculation, and place our order line items in the changeset. With a fresh order changeset ready to be inserted, we can again make use of `Ecto.Multi` to execute our operations in a database transaction. We start by inserting the order, followed by a `run` operation. The `Ecto.Multi.run/3` function allows us to run any code in the function which must either succeed with `{:ok, result}` or error, which halts and rolls back the transaction. Here, we simply call into our shopping cart context and ask it to prune all items in a cart. Running the transaction will execute the multi as before and we return the result to the caller.
-
-To close out our order completion, we need to implement the `ShoppingCart.prune_cart_items/1` function in `lib/hello/shopping_cart.ex`:
-
-```elixir
-  def prune_cart_items(%Scope{} = scope, %Cart{} = cart) do
-    {_, _} = Repo.delete_all(from(i in CartItem, where: i.cart_id == ^cart.id))
-    {:ok, get_cart(scope)}
-  end
-```
-
-Our new function accepts the cart struct and issues a `Repo.delete_all` which accepts a query of all items for the provided cart. We return a success result by simply reloading the pruned cart to the caller. With our context complete, we now need to show the user their completed order. Head back to your order controller and add the `show/2` action:
-
-```elixir
-  def show(conn, %{"id" => id}) do
-    order = Orders.get_order!(conn.assigns.current_scope, id)
-    render(conn, :show, order: order)
-  end
-```
-
-We added the show action to pass our `conn.assigns.current_scope` to `get_order!` which authorizes orders to be viewable only by the owner of the order. Next, we can implement the view and template. Create a new view file at `lib/hello_web/controllers/order_html.ex` with the following content:
-
-```elixir
-defmodule HelloWeb.OrderHTML do
-  use HelloWeb, :html
-
-  embed_templates "order_html/*"
-end
-```
-Next we can create the template at `lib/hello_web/controllers/order_html/show.html.heex`:
-
-```heex
-<.header>
-  Thank you for your order!
-  <:subtitle>
-     <strong>Email: </strong>{@current_scope.user.email}
-  </:subtitle>
-</.header>
-
-<.table id="items" rows={@order.line_items}>
-  <:col :let={item} label="Title">{item.product.title}</:col>
-  <:col :let={item} label="Quantity">{item.quantity}</:col>
-  <:col :let={item} label="Price">
-    {HelloWeb.CartHTML.currency_to_str(item.price)}
-  </:col>
-</.table>
-
-<strong>Total price:</strong>
-{HelloWeb.CartHTML.currency_to_str(@order.total_price)}
-
-<.back navigate={~p"/products"}>Back to products</.back>
-```
-
-To show our completed order, we displayed the order's user, followed by the line item listing with product title, quantity, and the price we "transacted" when completing the order, along with the total price.
-
-Our last addition will be to add the "complete order" button to our cart page to allow completing an order. Add the following button to the <.header> of the cart show template in `lib/hello_web/controllers/cart_html/show.html.heex`:
-
-```diff
-  <.header>
-    My Cart
-+   <:actions>
-+     <.link href={~p"/orders"} method="post">
-+       <.button>Complete order</.button>
-+     </.link>
-+   </:actions>
-  </.header>
-```
-
-We added a link with `method="post"` to send a POST request to our `OrderController.create` action. If we head back to our cart page at [`http://localhost:4000/cart`](http://localhost:4000/cart) and complete an order, we'll be greeted by our rendered template:
-
-```text
-Thank you for your order!
-
-User uuid: 08964c7c-908c-4a55-bcd3-9811ad8b0b9d
-Title                   Quantity Price
-Metaprogramming Elixir  2        $15.00
-
-Total price: $30.00
-```
-
-Nice work! We haven't added payments, but we can already see how our `ShoppingCart` and `Orders` context splitting is driving us towards a maintainable solution. With our cart items separated from our order line items, we are well equipped in the future to add payment transactions, cart price detection, and more.
-
-Great work!
-
-## FAQ
-
-### When to use code generators?
-
-In this guide, we have used code generators for schemas, contexts, controllers, and more. If you are happy to move forward with Phoenix defaults, feel free to rely on generators to scaffold large parts of your application. When using Phoenix generators, the main question you need to answer is: does this new functionality (with its schema, table, and fields) belong to one of the existing contexts or a new one?
-
-This way, Phoenix generators guide you to use contexts to group related functionality, instead of having several dozens of schemas laying around without any structure. And remember: if you're stuck when trying to come up with a context name, you can simply use the plural form of the resource you're creating.
-
-### How do I structure code inside contexts?
-
-You may wonder how to organize the code inside contexts. For example, should you define a module for changesets (such as ProductChangesets) and another module for queries (such as ProductQueries)?
-
-One important benefit of contexts is that this decision does not matter much. The context is your public API, the other modules are private. Contexts isolate these modules into small groups so the surface area of your application is the context and not _all of your code_.
-
-So while you and your team could establish patterns for organizing these private modules, it is also our opinion it is completely fine for them to be different. The major focus should be on how the contexts are defined and how they interact with each other (and with your web application).
-
-Think about it as a well-kept neighbourhood. Your contexts are houses, you want to keep them well-preserved, well-connected, etc. Inside the houses, they may all be a little bit different, and that's fine.
-
-### Returning Ecto structures from context APIs
-
-As we explored the context API, you might have wondered:
-
-> If one of the goals of our context is to encapsulate Ecto Repo access, why does `create_user/1` return an `Ecto.Changeset` struct when we fail to create a user?
-
-Although Changesets are part of Ecto, they are not tied to the database, and they can be used to map data from and to any source, which makes it a general and useful data structure for tracking field changes, perform validations, and generate error messages.
-
-For those reasons, `%Ecto.Changeset{}` is a good choice to model the data changes between your contexts and your web layer - regardless if you are talking to an API or the database.
-
-Finally, note that your controllers and views are not hardcoded to work exclusively with Ecto either. Instead, Phoenix defines protocols such as `Phoenix.Param` and `Phoenix.HTML.FormData`, which allow any library to extend how Phoenix generates URL parameters or renders forms. Conveniently for us, the `phoenix_ecto` project implements those protocols, but you could as well bring your own data structures and implement them yourself.
diff --git a/guides/data_modelling/contexts.md b/guides/data_modelling/contexts.md
new file mode 100644
index 000000000..ace0ff155
--- /dev/null
+++ b/guides/data_modelling/contexts.md
@@ -0,0 +1,37 @@
+# 1. Intro to Contexts
+
+The collection of Phoenix "Guides" in the sidebar teach about different parts of a Phoenix application. We've explored [the request life-cycle](request_lifecycle.html), wired up controller actions through our routers, and learned how Ecto allows data to be validated and persisted. Now it's time to tie it all together by writing web-facing features that interact with our greater Elixir application.
+
+When building a Phoenix project, we are first and foremost building an Elixir application. Phoenix's job is to provide a web interface into our Elixir application. Naturally, we compose our applications with modules and functions, but we often assign specific responsibilities to certain modules and give them names: such as controllers, routers, and live views.
+
+However, the most important part of your web application is often where we encapsulate data access and data validation. We call these modules **contexts**. They often talk to a database or APIs. By giving modules that expose and group related data the name **contexts**, we help developers identify these patterns and talk about them. At the end of the day, contexts are just modules, as are your controllers, views, etc.
+
+Overall, think of them as boundaries to decouple and isolate parts of your application. They are not a new concept either. For example, anytime you call Elixir's standard library, be it `Logger.info/1` or `Stream.map/2`, you are accessing different contexts. Internally, Elixir's logger is made of multiple modules, but we never interact with those modules directly. We call the `Logger` module the context, exactly because it exposes and groups all of the logging functionality.
+
+Let's use these ideas to build out our web application. Our goal is to build an ecommerce system where we can showcase products, allow users to add products to their cart, and complete their orders. Opposite to other Phoenix guides, **these guides are meant to be read in order**.
+
+## Our ecommerce application
+
+Let's start an application from scratch to build our ecommerce, using Phoenix Express. We will call the application `hello`.
+
+For macOS/Ubuntu:
+
+```bash
+$ curl https://new.phoenixframework.org/hello | sh
+```
+
+For Windows PowerShell:
+
+```cmd
+> curl.exe -fsSO https://new.phoenixframework.org/hello.bat; .\hello.bat
+```
+
+If those commands do not work, see the [Installation Guide](installation.html) and then run `mix phx.new`:
+
+```console
+$ mix phx.new hello
+```
+
+Follow any of the steps printed on the screen and open up the generated `hello` project in your editor.
+
+We are ready to move to the next chapter.
diff --git a/guides/data_modelling/cross_context_boundaries.md b/guides/data_modelling/cross_context_boundaries.md
new file mode 100644
index 000000000..86d9d2bbd
--- /dev/null
+++ b/guides/data_modelling/cross_context_boundaries.md
@@ -0,0 +1,563 @@
+# 4. Cross-context Boundaries
+
+Now that we have the beginnings of our product catalog features, let's begin to work on the other main features of our application – carting products from the catalog. In order to properly track products that have been added to a user's cart, we'll need a new place to persist this information, along with point-in-time product information like the price at time of carting. This is necessary so we can detect product price changes in the future. We know what we need to build, but now we need to decide where the cart functionality lives in our application.
+
+If we take a step back and think about the isolation of our application, the exhibition of products in our catalog distinctly differs from the responsibilities of managing a user's cart. A product catalog shouldn't care about the rules of our shopping cart system, and vice-versa. There's a clear need here for a separate context to handle the new cart responsibilities. Let's call it `ShoppingCart`.
+
+Let's create a `ShoppingCart` context to handle basic cart duties. Before we write code, let's imagine we have the following feature requirements:
+
+  1. Add products to a user's cart from the product show page
+  2. Store point-in-time product price information at time of carting
+  3. Store and update quantities in cart
+  4. Calculate and display sum of cart prices
+
+From the description, it's clear we need a `Cart` resource for storing the user's cart, along with a `CartItem` to track products in the cart. With our plan set, let's get to work.
+
+## Adding authentication
+
+Most of the cart functionality is tied to a specific user. Therefore, in order to allow each user to manage their own cart (and only their own carts), we must be able to authenticate users. To do so, we will use Phoenix's built-in `mix phx.gen.auth` generator to scaffold a solution for us:
+
+```console
+mix phx.gen.auth Accounts User user
+
+An authentication system can be created in two different ways:
+- Using Phoenix.LiveView (default)
+- Using Phoenix.Controller only
+Do you want to create a LiveView based authentication system? [Yn] n
+
+...
+* creating lib/hello/accounts/scope.ex
+...
+* injecting config/config.exs
+...
+
+Please re-fetch your dependencies with the following command:
+
+    $ mix deps.get
+
+Remember to update your repository by running migrations:
+
+    $ mix ecto.migrate
+
+Once you are ready, visit "/user/register"
+to create your account and then access "/dev/mailbox" to
+see the account confirmation email.
+```
+
+After following the instructions to re-fetch dependencies and migrating the database, we can start the server with `mix phx.server` and re-visit the home page [`http://localhost:4000/`](http://localhost:4000/). There, we should see new registration and login links at the top of the page. On the registration page, create a new user. In development, a confirmation email is sent to the dev mailbox, which is accessible at [`http://localhost:4000/dev/mailbox`](http://localhost:4000/dev/mailbox). After clicking the confirmation link, you should be successfully logged in.
+
+One of the benefits of `mix phx.gen.auth` is that it also generates a scope file at `lib/hello/accounts/scope.ex`. In a nutshell, authentication tells us who a user based on their email address, but it doesn't tell us the resources the user owns or has access to. In order to do so, we need authorization. Scopes help us tie generated resources, such as the Cart we will create, to users. Let's open up the file:
+
+```elixir
+defmodule Hello.Accounts.Scope do
+  ...
+  alias Hello.Accounts.User
+
+  defstruct user: nil
+
+  @doc """
+  Creates a scope for the given user.
+
+  Returns nil if no user is given.
+  """
+  def for_user(%User{} = user) do
+    %__MODULE__{user: user}
+  end
+
+  def for_user(nil), do: nil
+end
+```
+
+We can see that it is simply a struct with a `user` field. The authentication system ensures that the `current_scope` assign is accordingly set with the current user. Let's see it in practice.
+
+## Generating scoped resources
+
+Let's generate our new context:
+
+```console
+$ mix phx.gen.context ShoppingCart Cart carts
+
+* creating lib/hello/shopping_cart/cart.ex
+* creating priv/repo/migrations/20250205203128_create_carts.exs
+* creating lib/hello/shopping_cart.ex
+* injecting lib/hello/shopping_cart.ex
+* creating test/hello/shopping_cart_test.exs
+* injecting test/hello/shopping_cart_test.exs
+* creating test/support/fixtures/shopping_cart_fixtures.ex
+* injecting test/support/fixtures/shopping_cart_fixtures.ex
+
+Remember to update your repository by running migrations:
+
+    $ mix ecto.migrate
+```
+
+We generated our new context `ShoppingCart`, with a new `ShoppingCart.Cart` schema. Open up the generated schema and migration files and you will see it has automatically included a `user_id` field, thanks to the scope. Furthermore, when we explore the code later on, we will learn all queries to the carts table have been properly scoped.
+
+With our cart in place, let's generate our cart items. This time we will pass the `--no-scope` flag, because we will associate `cart_items` to `carts` and the `carts` are already scoped to the user:
+
+```console
+$ mix phx.gen.context ShoppingCart CartItem cart_items \
+cart_id:references:carts product_id:references:products \
+price_when_carted:decimal quantity:integer --no-scope
+
+You are generating into an existing context.
+...
+Would you like to proceed? [Yn] y
+* creating lib/hello/shopping_cart/cart_item.ex
+* creating priv/repo/migrations/20250205213410_create_cart_items.exs
+* injecting lib/hello/shopping_cart.ex
+* injecting test/hello/shopping_cart_test.exs
+* injecting test/support/fixtures/shopping_cart_fixtures.ex
+
+Remember to update your repository by running migrations:
+
+    $ mix ecto.migrate
+```
+
+We generated a new resource inside our `ShoppingCart` named `CartItem`. This schema and table will hold references to a cart and product, along with the price at the time we added the item to our cart, and the quantity the user wishes to purchase. Let's touch up the generated migration file in `priv/repo/migrations/*_create_cart_items.ex`:
+
+```diff
+    create table(:cart_items) do
+-     add :price_when_carted, :decimal
++     add :price_when_carted, :decimal, precision: 15, scale: 6, null: false
+      add :quantity, :integer
+-     add :cart_id, references(:carts, on_delete: :nothing)
++     add :cart_id, references(:carts, on_delete: :delete_all)
+-     add :product_id, references(:products, on_delete: :nothing)
++     add :product_id, references(:products, on_delete: :delete_all)
+
+      timestamps()
+    end
+
+-   create index(:cart_items, [:cart_id])
+    create index(:cart_items, [:product_id])
++   create unique_index(:cart_items, [:cart_id, :product_id])
+```
+
+We used the `:delete_all` strategy again to enforce data integrity. This way, when a cart or product is deleted from the application, we don't have to rely on application code in our `ShoppingCart` or `Catalog` contexts to worry about cleaning up the records. This keeps our application code decoupled and the data integrity enforcement where it belongs – in the database. We also added a unique constraint to ensure a duplicate product is not allowed to be added to a cart. As with the `product_categories` table, using a multi-column index lets us remove the separate index for the leftmost field (`cart_id`). With our database tables in place, we can now migrate up:
+
+```console
+$ mix ecto.migrate
+
+16:59:51.941 [info] == Running 20250205203342 Hello.Repo.Migrations.CreateCarts.change/0 forward
+
+16:59:51.945 [info] create table carts
+
+16:59:51.952 [info] == Migrated 20250205203342 in 0.0s
+
+16:59:51.988 [info] == Running 20250205213410 Hello.Repo.Migrations.CreateCartItems.change/0 forward
+
+16:59:51.988 [info] create table cart_items
+
+16:59:52.000 [info] create index cart_items_product_id_index
+
+16:59:52.001 [info] create index cart_items_cart_id_product_id_index
+
+16:59:52.002 [info] == Migrated 20250205213410 in 0.0s
+```
+
+Our database is ready to go with new `carts` and `cart_items` tables, but now we need to map that back into application code. You may be wondering how we can mix database foreign keys across different tables and how that relates to the context pattern of isolated, grouped functionality. Let's jump in and discuss the approaches and their tradeoffs.
+
+## Cross-context data
+
+So far, we've done a great job isolating the two main contexts of our application from each other, but now we have a necessary dependency to handle.
+
+Our `Catalog.Product` resource serves to keep the responsibilities of representing a product inside the catalog, but ultimately for an item to exist in the cart, a product from the catalog must be present. Given this, our `ShoppingCart` context will have a data dependency on the `Catalog` context. With that in mind, we have two options. One is to expose APIs on the `Catalog` context that allows us to efficiently fetch product data for use in the `ShoppingCart` system, which we would manually stitch together. Or we can use database joins to fetch the dependent data. Both are valid options given your tradeoffs and application size, but joining data from the database when you have a hard data dependency is just fine for a large class of applications and is the approach we will take here.
+
+Now that we know where our data dependencies exist, let's add our schema associations so we can tie shopping cart items to products. First, let's make a quick change to our cart schema in `lib/hello/shopping_cart/cart.ex` to associate a cart to its items:
+
+```diff
+  schema "carts" do
+-   field :user_id, :id
++   belongs_to :user, Hello.Accounts.User
++   has_many :items, Hello.ShoppingCart.CartItem
+
+    timestamps()
+  end
+```
+
+Now that our cart is associated to the items we place in it, let's set up the cart item associations inside `lib/hello/shopping_cart/cart_item.ex`:
+
+```diff
+  schema "cart_items" do
+    field :price_when_carted, :decimal
+    field :quantity, :integer
+-   field :cart_id, :id
+-   field :product_id, :id
+
++   belongs_to :cart, Hello.ShoppingCart.Cart
++   belongs_to :product, Hello.Catalog.Product
+
+    timestamps()
+  end
+
+  @doc false
+  def changeset(cart_item, attrs) do
+    cart_item
+    |> cast(attrs, [:price_when_carted, :quantity])
+    |> validate_required([:price_when_carted, :quantity])
++   |> validate_number(:quantity, greater_than_or_equal_to: 0, less_than: 100)
+  end
+```
+
+First, we replaced the `cart_id` field with a standard `belongs_to` pointing at our `ShoppingCart.Cart` schema. Next, we replaced our `product_id` field by adding our first cross-context data dependency with a `belongs_to` for the `Catalog.Product` schema. Here, we intentionally coupled the data boundaries because it provides exactly what we need: an isolated context API with the bare minimum knowledge necessary to reference a product in our system. Next, we added a new validation to our changeset. With `validate_number/3`, we ensure any quantity provided by user input is between 0 and 100.
+
+With our schemas in place, we can start integrating the new data structures and `ShoppingCart` context APIs into our web-facing features.
+
+## Adding Cart functions
+
+As we mentioned before, the context generators are only a starting point for our application. We can and should write well-named, purpose built functions to accomplish the goals of our context. We have a few new features to implement. First, we need to ensure every user of our application is granted a cart if one does not yet exist. From there, we can then allow users to add items to their cart, update item quantities, and calculate cart totals. Let's get started!
+
+We won't focus on a real user authentication system at this point, but by the time we're done, you'll be able to naturally integrate one with what we've written here. To simulate a current user session, open up your `lib/hello_web/router.ex` and key this in:
+
+Because we used `mix phx.gen.auth`, we already have a real authentication system in place. We can use the `current_scope` assign to access the currently authenticated user. Let's add a new plug that assigns a cart if there is an authenticated user:
+
+```diff
+  pipeline :browser do
+    plug :accepts, ["html"]
+    plug :fetch_session
+    plug :fetch_live_flash
+    plug :put_root_layout, html: {HelloWeb.LayoutView, :root}
+    plug :protect_from_forgery
+    plug :put_secure_browser_headers
+    plug :fetch_current_scope_for_user
++   plug :fetch_current_cart
+  end
+
++ alias Hello.ShoppingCart
++
++ defp fetch_current_cart(%{assigns: %{current_scope: scope}} = conn, _opts) when not is_nil(scope) do
++   if cart = ShoppingCart.get_cart(scope) do
++     assign(conn, :cart, cart)
++   else
++     {:ok, new_cart} = ShoppingCart.create_cart(scope)
++     assign(conn, :cart, new_cart)
++   end
++ end
++
++ defp fetch_current_cart(conn, _opts), do: conn
+```
+
+We added a new `:fetch_current_cart` plug which either finds a cart for the user UUID or creates a cart for the current user and assigns the result in the connection assigns. We'll need to implement our `ShoppingCart.get_cart/1`, but let's add our routes first.
+
+We'll need to implement a cart controller for handling cart operations like viewing a cart, updating quantities, and initiating the checkout process, as well as a cart items controller for adding and removing individual items to and from the cart. The authentication system already generated different router scopes that have different authentication requirements:
+
+```elixir
+...
+  ## Authentication routes
+
+  scope "/", HelloWeb do
+    pipe_through [:browser, :redirect_if_user_is_authenticated]
+
+    get "/user/register", UserRegistrationController, :new
+    post "/user/register", UserRegistrationController, :create
+  end
+
+  scope "/", HelloWeb do
+    pipe_through [:browser, :require_authenticated_user]
+
+    get "/user/settings", UserSettingsController, :edit
+    put "/user/settings", UserSettingsController, :update
+    get "/user/settings/confirm-email/:token", UserSettingsController, :confirm_email
+  end
+...
+```
+
+As you can see, the registration route has a `:redirect_if_user_is_authenticated` plug, which means it will redirect to the home page if the user is already authenticated. The user settings routes use a `:require_authenticated_user` plug, which means they will redirect to the log in page if the user is not authenticated. These plugs are defined in the `lib/hello_web/user_auth.ex` module.
+
+For our cart routes, we want to only allow access to authenticated users. Add the following routes to your router in `lib/hello_web/router.ex`:
+
+```diff
+   scope "/", HelloWeb do
+     pipe_through :browser
+
+     get "/", PageController, :index
+     resources "/products", ProductController
+   end
+
++  scope "/", HelloWeb do
++    pipe_through [:browser, :require_authenticated_user]
++
++    resources "/cart_items", CartItemController, only: [:create, :delete]
++
++    get "/cart", CartController, :show
++    put "/cart", CartController, :update
++  end
+```
+
+We added a `resources` declaration for a `CartItemController`, which will wire up the routes for a create and delete action for adding and removing individual cart items. Next, we added two new routes pointing at a `CartController`. The first route, a GET request, will map to our show action, to show the cart contents. The second route, a PUT request, will handle the submission of a form for updating our cart quantities.
+
+With our routes in place, let's add the ability to add an item to our cart from the product show page. Create a new file at `lib/hello_web/controllers/cart_item_controller.ex` and key this in:
+
+```elixir
+defmodule HelloWeb.CartItemController do
+  use HelloWeb, :controller
+
+  alias Hello.ShoppingCart
+
+  def create(conn, %{"product_id" => product_id}) do
+    case ShoppingCart.add_item_to_cart(conn.assigns.current_scope, conn.assigns.cart, product_id) do
+      {:ok, _item} ->
+        conn
+        |> put_flash(:info, "Item added to your cart")
+        |> redirect(to: ~p"/cart")
+
+      {:error, _changeset} ->
+        conn
+        |> put_flash(:error, "There was an error adding the item to your cart")
+        |> redirect(to: ~p"/cart")
+    end
+  end
+
+  def delete(conn, %{"id" => product_id}) do
+    {:ok, _cart} = ShoppingCart.remove_item_from_cart(conn.assigns.current_scope, conn.assigns.cart, product_id)
+    redirect(conn, to: ~p"/cart")
+  end
+end
+```
+
+We defined a new `CartItemController` with the create and delete actions that we declared in our router. For `create`, we call a `ShoppingCart.add_item_to_cart/2` function which we'll implement in a moment. If successful, we show a flash successful message and redirect to the cart show page; else, we show a flash error message and redirect to the cart show page. For `delete`, we'll call a `remove_item_from_cart` function which we'll implement on our `ShoppingCart` context  and then redirect back to the cart show page. We haven't implemented these two shopping cart functions yet, but notice how their names scream their intent: `add_item_to_cart` and `remove_item_from_cart` make it obvious what we are accomplishing here. It also allows us to spec out our web layer and context APIs without thinking about all the implementation details at once.
+
+Let's implement the new interface for the `ShoppingCart` context API in `lib/hello/shopping_cart.ex`:
+
+```diff
++  alias Hello.Catalog
+-  alias Hello.ShoppingCart.Cart
++  alias Hello.ShoppingCart.{Cart, CartItem}
+   alias Hello.Accounts.Scope
+
++  def get_cart(%Scope{} = scope) do
++    Repo.one(
++      from(c in Cart,
++        where: c.user_id == ^scope.user.id,
++        left_join: i in assoc(c, :items),
++        left_join: p in assoc(i, :product),
++        order_by: [asc: i.inserted_at],
++        preload: [items: {i, product: p}]
++      )
++    )
++  end
+
+   def create_cart(%Scope{} = scope, attrs \\ %{}) do
+     with {:ok, cart = %Cart{}} <-
+            %Cart{}
+            |> Cart.changeset(attrs, scope)
+            |> Repo.insert() do
+       broadcast(scope, {:created, cart})
+-      {:ok, cart}
++      {:ok, get_cart(scope, cart.id)}
+     end
+   end
++
++  def add_item_to_cart(%Scope{} = scope, %Cart{} = cart, product_id) do
++    true = cart.user_id == scope.user.id
++    product = Catalog.get_product!(product_id)
++
++    %CartItem{quantity: 1, price_when_carted: product.price}
++    |> CartItem.changeset(%{})
++    |> Ecto.Changeset.put_assoc(:cart, cart)
++    |> Ecto.Changeset.put_assoc(:product, product)
++    |> Repo.insert(
++      on_conflict: [inc: [quantity: 1]],
++      conflict_target: [:cart_id, :product_id]
++    )
++  end
++
++  def remove_item_from_cart(%Scope{} = scope, %Cart{} = cart, product_id) do
++    true = cart.user_id == scope.user.id
++
++    {1, _} =
++      Repo.delete_all(
++        from(i in CartItem,
++          where: i.cart_id == ^cart.id,
++          where: i.product_id == ^product_id
++        )
++      )
++
++    {:ok, get_cart(scope)}
++  end
+```
+
+We started by implementing `get_cart/1` which fetches our cart and joins the cart items, and their products so that we have the full cart populated with all preloaded data. Next, we modified our `create_cart` function to use `get_cart` to reload the cart contents.
+
+Next, we wrote our new `add_item_to_cart/3` function which accepts a scope, a cart struct and a product id. We proceed to fetch the product with `Catalog.get_product!/1`, showing how contexts can naturally invoke other contexts if required. You could also have chosen to receive the product as argument and you would achieve similar results. Then we used an upsert operation against our repo to either insert a new cart item into the database, or increase the quantity by one if it already exists in the cart. This is accomplished via the `on_conflict` and `conflict_target` options, which tells our repo how to handle an insert conflict.
+
+Finally, we implemented `remove_item_from_cart/3` where we simply issue a `Repo.delete_all` call with a query to delete the cart item in our cart that matches the product ID. Finally, we reload the cart contents by calling `get_cart/1`.
+
+With our new cart functions in place, we can now expose the "Add to cart" button on the product catalog show page. Open up your template in `lib/hello_web/controllers/product_html/show.html.heex` and make the following changes:
+
+```diff
+...
+     <.link class="btn" href={~p"/products/#{@product}/edit"}>
+       Edit product
+     </.link>
++    <.link class="btn" href={~p"/cart_items?product_id=#{@product.id}"} method="post">
++      Add to cart
++    </.link>
+...
+```
+
+The `link` function component from `Phoenix.Component` accepts a `:method` attribute to issue an HTTP verb when clicked, instead of the default GET request. With this link in place, the "Add to cart" link will issue a POST request, which will be matched by the route we defined in router which dispatches to the `CartItemController.create/2` function.
+
+Let's try it out. Start your server with `mix phx.server` and visit a product page. If we try clicking the add to cart link, we'll be greeted by an error page. If you are authenticated the following logs should be visible in the console:
+
+```text
+[info] POST /cart_items
+[debug] Processing with HelloWeb.CartItemController.create/2
+  Parameters: %{"_method" => "post", "product_id" => "1", ...}
+  Pipelines: [:browser, :require_authenticated_user]
+[debug] QUERY OK source="user_tokens" db=2.4ms idle=1340.8ms
+...
+[debug] QUERY OK source="cart_items" db=2.5ms
+INSERT INTO "cart_items" ...
+[info] Sent 302 in 24ms
+[info] GET /cart
+[debug] Processing with HelloWeb.CartController.show/2
+  Parameters: %{}
+  Pipelines: [:browser, :require_authenticated_user]
+[debug] QUERY OK source="user_tokens" db=1.6ms idle=430.2ms
+...
+[debug] QUERY OK source="carts" db=1.9ms idle=1798.5ms
+...
+[info] Sent 500 in 18ms
+[error] ** (UndefinedFunctionError) function HelloWeb.CartController.init/1 is undefined (module HelloWeb.CartController is not available)
+    ...
+```
+
+It's working! Kind of. If we follow the logs, we see our POST to the `/cart_items` path. Next, we can see our `ShoppingCart.add_item_to_cart` function successfully inserted a row into the `cart_items` table, and then we issued a redirect to `/cart`. Before our error, we also see a query to the `carts` table, which means we're fetching the current user's cart. So far so good. We know our `CartItem` controller and new `ShoppingCart` context functions are doing their jobs, but we've hit our next unimplemented feature when the router attempts to dispatch to a nonexistent cart controller. Let's create the cart controller, view, and template to display and manage user carts.
+
+Create a new file at `lib/hello_web/controllers/cart_controller.ex` and key this in:
+
+```elixir
+defmodule HelloWeb.CartController do
+  use HelloWeb, :controller
+
+  alias Hello.ShoppingCart
+
+  def show(conn, _params) do
+    render(conn, :show, changeset: ShoppingCart.change_cart(conn.assigns.current_scope, conn.assigns.cart))
+  end
+end
+```
+
+We defined a new cart controller to handle the `get "/cart"` route. For showing a cart, we render a `"show.html"` template which we'll create in a moment. We know we need to allow the cart items to be changed by quantity updates, so right away we know we'll need a cart changeset. Fortunately, the context generator included a `ShoppingCart.change_cart/1` function, which we'll use. We pass it our cart struct which is already in the connection assigns thanks to the `fetch_current_cart` plug we defined in the router.
+
+Next, we can implement the view and template. Create a new view file at `lib/hello_web/controllers/cart_html.ex` with the following content:
+
+```elixir
+defmodule HelloWeb.CartHTML do
+  use HelloWeb, :html
+
+  alias Hello.ShoppingCart
+
+  embed_templates "cart_html/*"
+
+  def currency_to_str(%Decimal{} = val), do: "$#{Decimal.round(val, 2)}"
+end
+```
+
+We created a view to render our `show.html` template and aliased our `ShoppingCart` context so it will be in scope for our template. We'll need to display the cart prices like product item price, cart total, etc, so we defined a `currency_to_str/1` which takes our decimal struct, rounds it properly for display, and prepends a USD dollar sign.
+
+Next we can create the template at `lib/hello_web/controllers/cart_html/show.html.heex`:
+
+```heex
+<.header>
+  My Cart
+  <:subtitle :if={@cart.items == []}>Your cart is empty</:subtitle>
+</.header>
+
+<div :if={@cart.items !== []}>
+  <.form :let={f} for={@changeset} action={~p"/cart"}>
+    <.inputs_for :let={%{data: item} = item_form} field={f[:items]}>
+      <.input field={item_form[:quantity]} type="number" label={item.product.title} />
+      {currency_to_str(ShoppingCart.total_item_price(item))}
+    </.inputs_for>
+    <.button>Update cart</.button>
+  </.form>
+  <b>Total</b>: {currency_to_str(ShoppingCart.total_cart_price(@cart))}
+</div>
+
+<.back navigate={~p"/products"}>Back to products</.back>
+```
+
+We started by showing the empty cart message if our preloaded `cart.items` is empty. If we have items, we use the `form` component provided by our `HelloWeb.CoreComponents` to take our cart changeset that we assigned in the `CartController.show/2` action and create a form which maps to our cart controller `update/2` action. Within the form, we use the [`inputs_for`](https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#inputs_for/1) component to render inputs for the nested cart items. This will allow us to map item inputs back together when the form is submitted. Next, we display a number input for the item quantity and label it with the product title. We finish the item form by converting the item price to string. We haven't written the `ShoppingCart.total_item_price/1` function yet, but again we employed the idea of clear, descriptive public interfaces for our contexts. After rendering inputs for all the cart items, we show an "update cart" submit button, along with the total price of the entire cart. This is accomplished with another new `ShoppingCart.total_cart_price/1` function which we'll implement in a moment. Finally, we added a `back` component to go back to our products page.
+
+We're almost ready to try out our cart page, but first we need to implement our new currency calculation functions. Open up your shopping cart context at `lib/hello/shopping_cart.ex` and add these new functions:
+
+```elixir
+  def total_item_price(%CartItem{} = item) do
+    Decimal.mult(item.product.price, item.quantity)
+  end
+
+  def total_cart_price(%Cart{} = cart) do
+    Enum.reduce(cart.items, 0, fn item, acc ->
+      item
+      |> total_item_price()
+      |> Decimal.add(acc)
+    end)
+  end
+```
+
+We implemented `total_item_price/1` which accepts a `%CartItem{}` struct. To calculate the total price, we simply take the preloaded product's price and multiply it by the item's quantity. We used `Decimal.mult/2` to take our decimal currency struct and multiply it with proper precision. Similarly for calculating the total cart price, we implemented a `total_cart_price/1` function which accepts the cart and sums the preloaded product prices for items in the cart. We again make use of the `Decimal` functions to add our decimal structs together.
+
+Now that we can calculate price totals, let's try it out! Visit [`http://localhost:4000/cart`](http://localhost:4000/cart) and you should already see your first item in the cart. Going back to the same product and clicking "add to cart" will show our upsert in action. Your quantity should now be two. Nice work!
+
+Our cart page is almost complete, but submitting the form will yield yet another error.
+
+```text
+[info] POST /cart
+...
+[error] ** (UndefinedFunctionError) function HelloWeb.CartController.update/2 is undefined or private
+```
+
+Let's head back to our `CartController` at `lib/hello_web/controllers/cart_controller.ex` and implement the update action:
+
+```elixir
+  def update(conn, %{"cart" => cart_params}) do
+    case ShoppingCart.update_cart(conn.assigns.current_scope, conn.assigns.cart, cart_params) do
+      {:ok, _cart} ->
+        redirect(conn, to: ~p"/cart")
+
+      {:error, _changeset} ->
+        conn
+        |> put_flash(:error, "There was an error updating your cart")
+        |> redirect(to: ~p"/cart")
+    end
+  end
+```
+
+We started by plucking out the cart params from the form submit. Next, we call our existing `ShoppingCart.update_cart/2` function which was added by the context generator. We'll need to make some changes to this function, but the interface is good as is. If the update is successful, we redirect back to the cart page, otherwise we show a flash error message and send the user back to the cart page to fix any mistakes. Out-of-the-box, our `ShoppingCart.update_cart/2` function only concerned itself with casting the cart params into a changeset and updates it against our repo. For our purposes, we now need it to handle nested cart item associations, and most importantly, business logic for how to handle quantity updates like zero-quantity items being removed from the cart.
+
+Head back over to your shopping cart context in `lib/hello/shopping_cart.ex` and replace your `update_cart/2` function with the following implementation:
+
+```elixir
+  def update_cart(%Scope{} = scope, %Cart{} = cart, attrs) do
+    true = cart.user_id == scope.user.id
+
+    changeset =
+      cart
+      |> Cart.changeset(attrs, scope)
+      |> Ecto.Changeset.cast_assoc(:items, with: &CartItem.changeset/2)
+
+    Ecto.Multi.new()
+    |> Ecto.Multi.update(:cart, changeset)
+    |> Ecto.Multi.delete_all(:discarded_items, fn %{cart: cart} ->
+      from(i in CartItem, where: i.cart_id == ^cart.id and i.quantity == 0)
+    end)
+    |> Repo.transaction()
+    |> case do
+      {:ok, %{cart: cart}} ->
+        broadcast(scope, {:updated, cart})
+        {:ok, cart}
+
+      {:error, :cart, changeset, _changes_so_far} ->
+        {:error, changeset}
+    end
+  end
+```
+
+We started much like how our out-of-the-box code started – we take the cart struct and cast the user input to a cart changeset, except this time we use `Ecto.Changeset.cast_assoc/3` to cast the nested item data into `CartItem` changesets. Remember the [`<.inputs_for />`](https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#inputs_for/1) call in our cart form template? That hidden ID data is what allows Ecto's `cast_assoc` to map item data back to existing item associations in the cart. Next we use `Ecto.Multi.new/0`, which you may not have seen before. Ecto's `Multi` is a feature that allows lazily defining a chain of named operations to eventually execute inside a database transaction. Each operation in the multi chain receives the values from the previous steps and executes until a failed step is encountered. When an operation fails, the transaction is rolled back and an error is returned, otherwise the transaction is committed.
+
+For our multi operations, we start by issuing an update of our cart, which we named `:cart`. After the cart update is issued, we perform a multi `delete_all` operation, which takes the updated cart and applies our zero-quantity logic. We prune any items in the cart with zero quantity by returning an ecto query that finds all cart items for this cart with an empty quantity. Calling `Repo.transaction/1` with our multi will execute the operations in a new transaction and we return the success or failure result to the caller just like the original function.
+
+Let's head back to the browser and try it out. Add a few products to your cart, update the quantities, and watch the values changes along with the price calculations. Setting any quantity to 0 will also remove the item. You can also try logging out and registering a new user to see how the carts are scoped to the current user. Pretty neat!
diff --git a/guides/data_modelling/faq.md b/guides/data_modelling/faq.md
new file mode 100644
index 000000000..5f69d7f70
--- /dev/null
+++ b/guides/data_modelling/faq.md
@@ -0,0 +1,31 @@
+# 6. FAQ
+
+Here we list frequently asked questions about contexts.
+
+## When to use code generators?
+
+In this guide, we have used code generators for schemas, contexts, controllers, and more. If you are happy to move forward with Phoenix defaults, feel free to rely on generators to scaffold large parts of your application. When using Phoenix generators, the main question you need to answer is: does this new functionality (with its schema, table, and fields) belong to one of the existing contexts or a new one?
+
+This way, Phoenix generators guide you to use contexts to group related functionality, instead of having several dozens of schemas laying around without any structure. And remember: if you're stuck when trying to come up with a context name, you can simply use the plural form of the resource you're creating.
+
+## How do I structure code inside contexts?
+
+You may wonder how to organize the code inside contexts. For example, should you define a module for changesets (such as ProductChangesets) and another module for queries (such as ProductQueries)?
+
+One important benefit of contexts is that this decision does not matter much. The context is your public API, the other modules are private. Contexts isolate these modules into small groups so the surface area of your application is the context and not _all of your code_.
+
+So while you and your team could establish patterns for organizing these private modules, it is also our opinion it is completely fine for them to be different. The major focus should be on how the contexts are defined and how they interact with each other (and with your web application).
+
+Think about it as a well-kept neighbourhood. Your contexts are houses, you want to keep them well-preserved, well-connected, etc. Inside the houses, they may all be a little bit different, and that's fine.
+
+## Returning Ecto structures from context APIs
+
+As we explored the context API, you might have wondered:
+
+> If one of the goals of our context is to encapsulate Ecto Repo access, why does `create_user/1` return an `Ecto.Changeset` struct when we fail to create a user?
+
+Although Changesets are part of Ecto, they are not tied to the database, and they can be used to map data from and to any source, which makes it a general and useful data structure for tracking field changes, perform validations, and generate error messages.
+
+For those reasons, `%Ecto.Changeset{}` is a good choice to model the data changes between your contexts and your web layer - regardless if you are talking to an API or the database.
+
+Finally, note that your controllers and views are not hardcoded to work exclusively with Ecto either. Instead, Phoenix defines protocols such as `Phoenix.Param` and `Phoenix.HTML.FormData`, which allow any library to extend how Phoenix generates URL parameters or renders forms. Conveniently for us, the `phoenix_ecto` project implements those protocols, but you could as well bring your own data structures and implement them yourself.
diff --git a/guides/data_modelling/in_context_relationships.md b/guides/data_modelling/in_context_relationships.md
new file mode 100644
index 000000000..28e45fad4
--- /dev/null
+++ b/guides/data_modelling/in_context_relationships.md
@@ -0,0 +1,219 @@
+# 3. In-context Relationships
+
+Our basic catalog features are nice, but let's take it up a notch by categorizing products. Many ecommerce solutions allow products to be categorized in different ways, such as a product being marked for fashion, power tools, and so on. Starting with a one-to-one relationship between product and categories will cause major code changes later if we need to start supporting multiple categories. Let's set up a category association that will allow us to start off tracking a single category per product, but easily support more later as we grow our features.
+
+For now, categories will contain only textual information. Our first order of business is to decide where categories live in the application. We have our `Catalog` context, which manages the exhibition of our products. Product categorization is a natural fit here. Phoenix is also smart enough to generate code inside an existing context, which makes adding new resources to a context a breeze. Run the following command at your project root:
+
+> Sometimes it may be tricky to determine if two resources belong to the same context or not. In those cases, prefer distinct contexts per resource and refactor later if necessary. Otherwise you can easily end up with large contexts of loosely related entities. Also keep in mind that the fact two resources are related does not necessarily mean they belong to the same context, otherwise you would quickly end up with one large context, as the majority of resources in an application are connected to each other. To sum it up: if you are unsure, you should prefer separate modules (contexts).
+
+```console
+$ mix phx.gen.context Catalog Category categories \
+title:string:unique --no-scope
+
+You are generating into an existing context.
+...
+Would you like to proceed? [Yn] y
+* creating lib/hello/catalog/category.ex
+* creating priv/repo/migrations/20250203192325_create_categories.exs
+* injecting lib/hello/catalog.ex
+* injecting test/hello/catalog_test.exs
+* injecting test/support/fixtures/catalog_fixtures.ex
+
+Remember to update your repository by running migrations:
+
+    $ mix ecto.migrate
+```
+
+This time around, we used `mix phx.gen.context`, which is just like `mix phx.gen.html`, except it doesn't generate the web files for us. Since we already have controllers and templates for managing products, we can integrate the new category features into our existing web form and product show page. We can see we now have a new `Category` schema alongside our product schema at `lib/hello/catalog/category.ex`, and Phoenix told us it was *injecting* new functions in our existing Catalog context for the category functionality. The injected functions will look very familiar to our product functions, with new functions like `create_category`, `list_categories`, and so on. Before we migrate up, we need to do a second bit of code generation. Our category schema is great for representing an individual category in the system, but we need to support a many-to-many relationship between products and categories. Fortunately, ecto allows us to do this simply with a join table, so let's generate that now with the `ecto.gen.migration` command:
+
+```console
+$ mix ecto.gen.migration create_product_categories
+
+* creating priv/repo/migrations/20250203192958_create_product_categories.exs
+```
+
+Next, let's open up the new migration file and add the following code to the `change` function:
+
+```elixir
+defmodule Hello.Repo.Migrations.CreateProductCategories do
+  use Ecto.Migration
+
+  def change do
+    create table(:product_categories, primary_key: false) do
+      add :product_id, references(:products, on_delete: :delete_all)
+      add :category_id, references(:categories, on_delete: :delete_all)
+    end
+
+    create index(:product_categories, [:product_id])
+    create unique_index(:product_categories, [:category_id, :product_id])
+  end
+end
+```
+
+We created a `product_categories` table and used the `primary_key: false` option since our join table does not need a primary key. Next we defined our `:product_id` and `:category_id` foreign key fields, and passed `on_delete: :delete_all` to ensure the database prunes our join table records if a linked product or category is deleted. By using a database constraint, we enforce data integrity at the database level, rather than relying on ad-hoc and error-prone application logic.
+
+Next, we created indexes for our foreign keys, one of which is a unique index to ensure a product cannot have duplicate categories. Note that we do not necessarily need single-column index for `category_id` because it is in the leftmost prefix of multicolumn index, which is enough for the database optimizer. Adding a redundant index, on the other hand, only adds overhead on write.
+
+With our migrations in place, we can migrate up.
+
+```console
+$ mix ecto.migrate
+
+18:20:36.489 [info] == Running 20250222231834 Hello.Repo.Migrations.CreateCategories.change/0 forward
+
+18:20:36.493 [info] create table categories
+
+18:20:36.508 [info] create index categories_title_index
+
+18:20:36.512 [info] == Migrated 20250222231834 in 0.0s
+
+18:20:36.547 [info] == Running 20250222231930 Hello.Repo.Migrations.CreateProductCategories.change/0 forward
+
+18:20:36.547 [info] create table product_categories
+
+18:20:36.557 [info] create index product_categories_product_id_index
+
+18:20:36.560 [info]  create index product_categories_category_id_product_id_index
+
+18:20:36.562 [info] == Migrated 20250222231930 in 0.0s
+```
+
+Now that we have a `Catalog.Product` schema and a join table to associate products and categories, we're nearly ready to start wiring up our new features. Before we dive in, we first need real categories to select in our web UI. Let's quickly seed some new categories in the application. Add the following code to your seeds file in `priv/repo/seeds.exs`:
+
+```elixir
+for title <- ["Home Improvement", "Power Tools", "Gardening", "Books", "Education"] do
+  {:ok, _} = Hello.Catalog.create_category(%{title: title})
+end
+```
+
+We simply enumerate over a list of category titles and use the generated `create_category/1` function of our catalog context to persist the new records. We can run the seeds with `mix run`:
+
+```console
+$ mix run priv/repo/seeds.exs
+
+[debug] QUERY OK db=3.1ms decode=1.1ms queue=0.7ms idle=2.2ms
+INSERT INTO "categories" ("title","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Home Improvement", ~N[2025-02-03 19:39:53], ~N[2025-02-03 19:39:53]]
+[debug] QUERY OK db=1.2ms queue=1.3ms idle=12.3ms
+INSERT INTO "categories" ("title","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Power Tools", ~N[2025-02-03 19:39:53], ~N[2025-02-03 19:39:53]]
+[debug] QUERY OK db=1.1ms queue=1.1ms idle=15.1ms
+INSERT INTO "categories" ("title","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Gardening", ~N[2025-02-03 19:39:53], ~N[2025-02-03 19:39:53]]
+[debug] QUERY OK db=2.4ms queue=1.0ms idle=17.6ms
+INSERT INTO "categories" ("title","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Books", ~N[2025-02-03 19:39:53], ~N[2025-02-03 19:39:53]]
+```
+
+Perfect. Before we integrate categories in the web layer, we need to let our context know how to associate products and categories. First, open up `lib/hello/catalog/product.ex` and add the following association:
+
+```diff
++ alias Hello.Catalog.Category
+
+  schema "products" do
+    field :description, :string
+    field :price, :decimal
+    field :title, :string
+    field :views, :integer
+
++   many_to_many :categories, Category, join_through: "product_categories", on_replace: :delete
+
+    timestamps()
+  end
+
+```
+
+We used `Ecto.Schema`'s `many_to_many` macro to let Ecto know how to associate our product to multiple categories through the `"product_categories"` join table. We also used the `on_replace: :delete` option to declare that any existing join records should be deleted when we are changing our categories.
+
+With our schema associations set up, we can implement the selection of categories in our product form. To do so, we need to translate the user input of catalog IDs from the front-end to our many-to-many association. Fortunately Ecto makes this a breeze now that our schema is set up. Open up your catalog context and make the following changes:
+
+```diff
++ alias Hello.Catalog.Category
+
+- def get_product!(id), do: Repo.get!(Product, id)
++ def get_product!(id) do
++   Product |> Repo.get!(id) |> Repo.preload(:categories)
++ end
+
+  def create_product(attrs \\ %{}) do
+    %Product{}
+-   |> Product.changeset(attrs)
++   |> change_product(attrs)
+    |> Repo.insert()
+  end
+
+  def update_product(%Product{} = product, attrs) do
+    product
+-   |> Product.changeset(attrs)
++   |> change_product(attrs)
+    |> Repo.update()
+  end
+
+  def change_product(%Product{} = product, attrs \\ %{}) do
+-   Product.changeset(product, attrs)
++   categories = list_categories_by_id(attrs["category_ids"])
+
++   product
++   |> Repo.preload(:categories)
++   |> Product.changeset(attrs)
++   |> Ecto.Changeset.put_assoc(:categories, categories)
+  end
+
++ def list_categories_by_id(nil), do: []
++ def list_categories_by_id(category_ids) do
++   Repo.all(from c in Category, where: c.id in ^category_ids)
++ end
+```
+
+First, we added `Repo.preload` to preload our categories when we fetch a product. This will allow us to reference `product.categories` in our controllers, templates, and anywhere else we want to make use of category information. Next, we modified our `create_product` and `update_product` functions to call into our existing `change_product` function to produce a changeset. Within `change_product` we added a lookup to find all categories if the `"category_ids"` attribute is present. Then we preloaded categories and called `Ecto.Changeset.put_assoc` to place the fetched categories into the changeset. Finally, we implemented the `list_categories_by_id/1` function to query the categories matching the category IDs, or return an empty list if no `"category_ids"` attribute is present. Now our `create_product` and `update_product` functions receive a changeset with the category associations all ready to go once we attempt an insert or update against our repo.
+
+Next, let's expose our new feature to the web by adding the category input to our product form. To keep our form template tidy, let's write a new function to wrap up the details of rendering a category select input for our product. Open up your `ProductHTML` view in `lib/hello_web/controllers/product_html.ex` and key this in:
+
+```elixir
+  def category_opts(changeset) do
+    existing_ids =
+      changeset
+      |> Ecto.Changeset.get_change(:categories, [])
+      |> Enum.map(& &1.data.id)
+
+    for cat <- Hello.Catalog.list_categories() do
+      [key: cat.title, value: cat.id, selected: cat.id in existing_ids]
+    end
+  end
+```
+
+We added a new `category_opts/1` function which generates the select options for a multiple select tag we will add soon. We calculated the existing category IDs from our changeset, then used those values when we generate the select options for the input tag. We did this by enumerating over all of our categories and returning the appropriate `key`, `value`, and `selected` values. We marked an option as selected if the category ID was found in those category IDs in our changeset.
+
+With our `category_opts` function in place, we can open up `lib/hello_web/controllers/product_html/product_form.html.heex` and add:
+
+```diff
+  ...
+  <.input field={f[:views]} type="number" label="Views" />
+
++ <.input field={f[:category_ids]} type="select" multiple options={category_opts(@changeset)} />
+
+  <.button>Save Product</.button>
+```
+
+We added a `category_select` above our save button. Now let's try it out. Next, let's show the product's categories in the product show template. Add the following code to the list in `lib/hello_web/controllers/product_html/show.html.heex`:
+
+```diff
+<.list>
+  ...
++ <:item title="Categories">
++   <ul>
++     <li :for={cat <- @product.categories}>{cat.title}</li>
++   </ul>
++ </:item>
+</.list>
+```
+
+Now if we start the server with `mix phx.server` and visit [http://localhost:4000/products/new](http://localhost:4000/products/new), we'll see the new category multiple select input. Enter some valid product details, select a category or two, and click save.
+
+```text
+Title: Elixir Flashcards
+Description: Flash card set for the Elixir programming language
+Price: 5.000000
+Views: 0
+Categories:
+Education
+Books
+```
+
+It's not much to look at yet, but it works! We added relationships within our context complete with data integrity enforced by the database. Not bad. Let's keep building!
\ No newline at end of file
diff --git a/guides/data_modelling/more_examples.md b/guides/data_modelling/more_examples.md
new file mode 100644
index 000000000..8d6fcbf44
--- /dev/null
+++ b/guides/data_modelling/more_examples.md
@@ -0,0 +1,319 @@
+# 5. Bringing It Home
+
+With our `Catalog` and `ShoppingCart` contexts, we're seeing first-hand how our well-considered modules and function names are yielding clear and maintainable code. Our last order of business is to allow the user to initiate the checkout process. We won't go as far as integrating payment processing or order fulfillment, but we'll get you started in that direction. This will be a great opportunity to put what we have learned so far in practice.
+
+Like before, we need to decide where code for completing an order should live. Is it part of the catalog? Clearly not, but what about the shopping cart? Shopping carts are related to orders – after all, the user has to add items in order to purchase any products – but should the order checkout process be grouped here?
+
+If we stop and consider the order process, we'll see that orders involve related, but distinctly different data from the cart contents. Also, business rules around the checkout process are much different than carting. For example, we may allow a user to add a back-ordered item to their cart, but we could not allow an order with no inventory to be completed. Additionally, we need to capture point-in-time product information when an order is completed, such as the price of the items *at payment transaction time*. This is essential because a product price may change in the future, but the line items in our order must always record and display what we charged at time of purchase. For these reasons, we can start to see that ordering can stand on its own with its own data concerns and business rules.
+
+Naming wise, `Orders` clearly defines our context, so let's get started by again taking advantage of the context generators. Note that the `user` scope generated by `mix phx.gen.auth` is marked as default scope (in your `config/config.exs`), therefore we don't need to specify it in our command. There can be different scopes in an application, in which case the `--scope` option can be used when running the generators. Run the following command in your console:
+
+```console
+$ mix phx.gen.context Orders Order orders total_price:decimal
+
+* creating lib/hello/orders/order.ex
+* creating priv/repo/migrations/20250209214612_create_orders.exs
+* creating lib/hello/orders.ex
+* injecting lib/hello/orders.ex
+* creating test/hello/orders_test.exs
+* injecting test/hello/orders_test.exs
+* creating test/support/fixtures/orders_fixtures.ex
+* injecting test/support/fixtures/orders_fixtures.ex
+
+Remember to update your repository by running migrations:
+
+    $ mix ecto.migrate
+```
+
+We generated an `Orders` context. The order is automatically scoped to the current user and added a `total_price` column. With our starting point in place, let's open up the newly created migration in `priv/repo/migrations/*_create_orders.exs` and make the following changes:
+
+```diff
+  def change do
+    create table(:orders) do
+-     add :total_price, :decimal
++     add :total_price, :decimal, precision: 15, scale: 6, null: false
+      add :user_id, references(:user, type: :id, on_delete: :delete_all)
+
+      timestamps()
+    end
+  end
+```
+
+Like we did previously, we gave appropriate precision and scale options for our decimal column which will allow us to store currency without precision loss. We also added a not-null constraint to enforce all orders to have a price.
+
+The orders table alone doesn't hold much information, but we know we'll need to store point-in-time product price information of all the items in the order. For that, we'll add an additional struct for this context named `LineItem`. Line items will capture the price of the product *at payment transaction time*. Please run the following command:
+
+```console
+$ mix phx.gen.context Orders LineItem order_line_items \
+price:decimal quantity:integer \
+order_id:references:orders product_id:references:products --no-scope
+
+You are generating into an existing context.
+...
+Would you like to proceed? [Yn] y
+* creating lib/hello/orders/line_item.ex
+* creating priv/repo/migrations/20250209215050_create_order_line_items.exs
+* injecting lib/hello/orders.ex
+* injecting test/hello/orders_test.exs
+* injecting test/support/fixtures/orders_fixtures.ex
+
+Remember to update your repository by running migrations:
+
+    $ mix ecto.migrate
+```
+
+We used the `phx.gen.context` command to generate the `LineItem` Ecto schema and inject supporting functions into our orders context. Like before, let's modify the migration in `priv/repo/migrations/*_create_order_line_items.exs` and make the following decimal field changes:
+
+```diff
+  def change do
+    create table(:order_line_items) do
+-     add :price, :decimal
++     add :price, :decimal, precision: 15, scale: 6, null: false
+      add :quantity, :integer
+      add :order_id, references(:orders, on_delete: :nothing)
+      add :product_id, references(:products, on_delete: :nothing)
+
+      timestamps()
+    end
+
+    create index(:order_line_items, [:order_id])
+    create index(:order_line_items, [:product_id])
+  end
+```
+
+With our migration in place, let's wire up our orders and line items associations in `lib/hello/orders/order.ex`:
+
+```diff
+  schema "orders" do
+    field :total_price, :decimal
+-   field :user_id, :id
+
++   belongs_to :user, Hello.Accounts.User
++   has_many :line_items, Hello.Orders.LineItem
++   has_many :products, through: [:line_items, :product]
+
+    timestamps()
+  end
+```
+
+We used `has_many :line_items` to associate orders and line items, just like we've seen before. Next, we used the `:through` feature of `has_many`, which allows us to instruct ecto how to associate resources across another relationship. In this case, we can associate products of an order by finding all products through associated line items. Next, let's wire up the association in the other direction in `lib/hello/orders/line_item.ex`:
+
+```diff
+  schema "order_line_items" do
+    field :price, :decimal
+    field :quantity, :integer
+-   field :order_id, :id
+-   field :product_id, :id
+
++   belongs_to :order, Hello.Orders.Order
++   belongs_to :product, Hello.Catalog.Product
+
+    timestamps()
+  end
+```
+
+We used `belongs_to` to associate line items to orders and products. With our associations in place, we can start integrating the web interface into our order process. Open up your router `lib/hello_web/router.ex` and add the following line:
+
+```diff
+  scope "/", HelloWeb do
+    pipe_through [:browser, :require_authenticated_user]
+
+    resources "/cart_items", CartItemController, only: [:create, :delete]
+
+    get "/cart", CartController, :show
+    put "/cart", CartController, :update
+
++   resources "/orders", OrderController, only: [:create, :show]
+  end
+```
+
+We wired up `create` and `show` routes for our generated `OrderController`, since these are the only actions we need at the moment. With our routes in place, we can now migrate up:
+
+```console
+$ mix ecto.migrate
+
+17:14:37.715 [info] == Running 20250209214612 Hello.Repo.Migrations.CreateOrders.change/0 forward
+
+17:14:37.720 [info] create table orders
+
+17:14:37.755 [info] == Migrated 20250209214612 in 0.0s
+
+17:14:37.784 [info] == Running 20250209215050 Hello.Repo.Migrations.CreateOrderLineItems.change/0 forward
+
+17:14:37.785 [info] create table order_line_items
+
+17:14:37.795 [info] create index order_line_items_order_id_index
+
+17:14:37.796 [info] create index order_line_items_product_id_index
+
+17:14:37.798 [info] == Migrated 20250209215050 in 0.0s
+```
+
+Before we render information about our orders, we need to ensure our order data is fully populated and can be looked up by a current user. Open up your orders context in `lib/hello/orders.ex` and adjust your `get_order!/2` to include a preload:
+
+```diff
+   def get_order!(%Scope{} = scope, id) do
+-    Repo.get_by!(Order, id: id, user_id: scope.user.id)
++    Order
++    |> Repo.get_by!(id: id, user_id: scope.user.id)
++    |> Repo.preload([line_items: [:product]])
+   end
+```
+
+To complete an order, our cart page can issue a POST to the `OrderController.create` action, but we need to implement the operations and logic to actually complete an order. Like before, we'll start at the web interface. Create a new file at `lib/hello_web/controllers/order_controller.ex` and key this in:
+
+```elixir
+defmodule HelloWeb.OrderController do
+  use HelloWeb, :controller
+
+  alias Hello.Orders
+
+  def create(conn, _) do
+    case Orders.complete_order(conn.assigns.current_scope, conn.assigns.cart) do
+      {:ok, order} ->
+        conn
+        |> put_flash(:info, "Order created successfully.")
+        |> redirect(to: ~p"/orders/#{order}")
+
+      {:error, _reason} ->
+        conn
+        |> put_flash(:error, "There was an error processing your order")
+        |> redirect(to: ~p"/cart")
+    end
+  end
+end
+```
+
+We wrote the `create` action to call an as-yet-implemented `Orders.complete_order/2` function. Our code is technically "creating" an order, but it's important to step back and consider the naming of your interfaces. The act of *completing* an order is extremely important in our system. Money changes hands in a transaction, physical goods could be automatically shipped, etc. Such an operation deserves a better, more obvious function name, such as `complete_order`. If the order is completed successfully we redirect to the show page, otherwise a flash error is shown as we redirect back to the cart page.
+
+Here is also a good opportunity to highlight that contexts can naturally work with data defined by other contexts too. This will be especially common with data that is used throughout the application, such as the cart here (but it can also be the current user or the current project, and so forth, depending on your project).
+
+Now we can implement our `Orders.complete_order/2` function. To complete an order, our job will require a few operations:
+
+  1. A new order record must be persisted with the total price of the order
+  2. All items in the cart must be transformed into new order line items records
+    with quantity and point-in-time product price information
+  3. After successful order insert (and eventual payment), items must be pruned
+    from the cart
+
+From our requirements alone, we can start to see why a generic `create_order` function doesn't cut it. Let's implement this new function in `lib/hello/orders.ex`:
+
+```elixir
+  alias Hello.Orders.LineItem
+  alias Hello.ShoppingCart
+
+  def complete_order(%Scope{} = scope, %ShoppingCart.Cart{} = cart) do
+    true = cart.user_id == scope.user.id
+
+    line_items =
+      Enum.map(cart.items, fn item ->
+        %{product_id: item.product_id, price: item.product.price, quantity: item.quantity}
+      end)
+
+    order =
+      Ecto.Changeset.change(%Order{},
+        user_id: scope.user.id,
+        total_price: ShoppingCart.total_cart_price(cart),
+        line_items: line_items
+      )
+
+    Ecto.Multi.new()
+    |> Ecto.Multi.insert(:order, order)
+    |> Ecto.Multi.run(:prune_cart, fn _repo, _changes ->
+      ShoppingCart.prune_cart_items(scope, cart)
+    end)
+    |> Repo.transaction()
+    |> case do
+      {:ok, %{order: order}} ->
+        broadcast(scope, {:created, order})
+        {:ok, order}
+
+      {:error, name, value, _changes_so_far} ->
+        {:error, {name, value}}
+    end
+  end
+```
+
+We started by mapping the `%ShoppingCart.CartItem{}`'s in our shopping cart into a map of order line items structs. The job of the order line item record is to capture the price of the product *at payment transaction time*, so we reference the product's price here. Next, we create a bare order changeset with `Ecto.Changeset.change/2` and associate our user UUID, set our total price calculation, and place our order line items in the changeset. With a fresh order changeset ready to be inserted, we can again make use of `Ecto.Multi` to execute our operations in a database transaction. We start by inserting the order, followed by a `run` operation. The `Ecto.Multi.run/3` function allows us to run any code in the function which must either succeed with `{:ok, result}` or error, which halts and rolls back the transaction. Here, we simply call into our shopping cart context and ask it to prune all items in a cart. Running the transaction will execute the multi as before and we return the result to the caller.
+
+To close out our order completion, we need to implement the `ShoppingCart.prune_cart_items/1` function in `lib/hello/shopping_cart.ex`:
+
+```elixir
+  def prune_cart_items(%Scope{} = scope, %Cart{} = cart) do
+    {_, _} = Repo.delete_all(from(i in CartItem, where: i.cart_id == ^cart.id))
+    {:ok, get_cart(scope)}
+  end
+```
+
+Our new function accepts the cart struct and issues a `Repo.delete_all` which accepts a query of all items for the provided cart. We return a success result by simply reloading the pruned cart to the caller. With our context complete, we now need to show the user their completed order. Head back to your order controller and add the `show/2` action:
+
+```elixir
+  def show(conn, %{"id" => id}) do
+    order = Orders.get_order!(conn.assigns.current_scope, id)
+    render(conn, :show, order: order)
+  end
+```
+
+We added the show action to pass our `conn.assigns.current_scope` to `get_order!` which authorizes orders to be viewable only by the owner of the order. Next, we can implement the view and template. Create a new view file at `lib/hello_web/controllers/order_html.ex` with the following content:
+
+```elixir
+defmodule HelloWeb.OrderHTML do
+  use HelloWeb, :html
+
+  embed_templates "order_html/*"
+end
+```
+Next we can create the template at `lib/hello_web/controllers/order_html/show.html.heex`:
+
+```heex
+<.header>
+  Thank you for your order!
+  <:subtitle>
+     <strong>Email: </strong>{@current_scope.user.email}
+  </:subtitle>
+</.header>
+
+<.table id="items" rows={@order.line_items}>
+  <:col :let={item} label="Title">{item.product.title}</:col>
+  <:col :let={item} label="Quantity">{item.quantity}</:col>
+  <:col :let={item} label="Price">
+    {HelloWeb.CartHTML.currency_to_str(item.price)}
+  </:col>
+</.table>
+
+<strong>Total price:</strong>
+{HelloWeb.CartHTML.currency_to_str(@order.total_price)}
+
+<.back navigate={~p"/products"}>Back to products</.back>
+```
+
+To show our completed order, we displayed the order's user, followed by the line item listing with product title, quantity, and the price we "transacted" when completing the order, along with the total price.
+
+Our last addition will be to add the "complete order" button to our cart page to allow completing an order. Add the following button to the <.header> of the cart show template in `lib/hello_web/controllers/cart_html/show.html.heex`:
+
+```diff
+  <.header>
+    My Cart
++   <:actions>
++     <.link class="btn" href={~p"/orders"} method="post">
++       Complete order
++     </.link>
++   </:actions>
+  </.header>
+```
+
+We added a link with `method="post"` to send a POST request to our `OrderController.create` action. If we head back to our cart page at [`http://localhost:4000/cart`](http://localhost:4000/cart) and complete an order, we'll be greeted by our rendered template:
+
+```text
+Thank you for your order!
+
+User uuid: 08964c7c-908c-4a55-bcd3-9811ad8b0b9d
+Title                   Quantity Price
+Metaprogramming Elixir  2        $15.00
+
+Total price: $30.00
+```
+
+We haven't added payments, but we can already see how our `ShoppingCart` and `Orders` context splitting is driving us towards a maintainable solution. With our cart items separated from our order line items, we are well equipped in the future to add payment transactions, cart price detection, and more.
+
+Great work!
\ No newline at end of file
diff --git a/guides/data_modelling/your_first_context.md b/guides/data_modelling/your_first_context.md
new file mode 100644
index 000000000..dc63e4d92
--- /dev/null
+++ b/guides/data_modelling/your_first_context.md
@@ -0,0 +1,299 @@
+# 2. Your First Context
+
+An ecommerce platform has wide-reaching coupling across a codebase so it's important to think about writing well-defined modules. With that in mind, our goal is to build a product catalog API that handles creating, updating, and deleting the products available in our system. We'll start off with the basic features of showcasing our products, and we will add shopping cart features later. We'll see how starting with a solid foundation with isolated boundaries allows us to grow our application naturally as we add functionality.
+
+Phoenix includes the `mix phx.gen.html`, `mix phx.gen.json`, `mix phx.gen.live`, and `mix phx.gen.context` generators that apply the ideas of isolating functionality in our applications into contexts. These generators are a great way to hit the ground running while Phoenix nudges you in the right direction to grow your application. Let's put these tools to use for our new product catalog context.
+
+In order to run the context generators, we need to come up with a module name that groups the related functionality that we're building. In the [Ecto guide](ecto.html), we saw how we can use Changesets and Repos to validate and persist user schemas, but we didn't integrate this with our application at large. In fact, we didn't think about where a "user" in our application should live at all. Let's take a step back and think about the different parts of our system. We know that we'll have products to showcase on pages for sale, along with descriptions, pricing, etc. Along with selling products, we know we'll need to support carting, order checkout, and so on. While the products being purchased are related to the cart and checkout processes, showcasing a product and managing the *exhibition* of our products is distinctly different than tracking what a user has placed in their cart or how an order is placed. A `Catalog` context is a natural place for the management of our product details and the showcasing of those products we have for sale.
+
+## Starting with generators
+
+To jump-start our catalog context, we'll use `mix phx.gen.html` which creates a context module that wraps up Ecto access for creating, updating, and deleting products, along with web files like controllers and templates for the web interface into our context. Run the following command at your project root:
+
+```console
+$ mix phx.gen.html Catalog Product products title:string \
+description:string price:decimal views:integer
+
+* creating lib/hello_web/controllers/product_controller.ex
+* creating lib/hello_web/controllers/product_html/edit.html.heex
+* creating lib/hello_web/controllers/product_html/index.html.heex
+* creating lib/hello_web/controllers/product_html/new.html.heex
+* creating lib/hello_web/controllers/product_html/show.html.heex
+* creating lib/hello_web/controllers/product_html/product_form.html.heex
+* creating lib/hello_web/controllers/product_html.ex
+* creating test/hello_web/controllers/product_controller_test.exs
+* creating lib/hello/catalog/product.ex
+* creating priv/repo/migrations/20250201185747_create_products.exs
+* creating lib/hello/catalog.ex
+* injecting lib/hello/catalog.ex
+* creating test/hello/catalog_test.exs
+* injecting test/hello/catalog_test.exs
+* creating test/support/fixtures/catalog_fixtures.ex
+* injecting test/support/fixtures/catalog_fixtures.ex
+
+Add the resource to your browser scope in lib/hello_web/router.ex:
+
+    resources "/products", ProductController
+
+Remember to update your repository by running migrations:
+
+    $ mix ecto.migrate
+```
+
+Phoenix generated the web files as expected in `lib/hello_web/`. We can also see our context files were generated inside a `lib/hello/catalog.ex` file and our product schema in the directory of the same name. Note the difference between `lib/hello` and `lib/hello_web`. We have a `Catalog` module to serve as the public API for product catalog functionality, as well as a `Catalog.Product` struct, which is an Ecto schema for casting and validating product data. Phoenix also provided web and context tests for us, it also included test helpers for creating entities via the `Hello.Catalog` context, which we'll look at later. For now, let's follow the instructions and add the route according to the console instructions, in `lib/hello_web/router.ex`:
+
+```diff
+  scope "/", HelloWeb do
+    pipe_through :browser
+
+    get "/", PageController, :index
++   resources "/products", ProductController
+  end
+```
+
+With the new route in place, Phoenix reminds us to update our repo by running `mix ecto.migrate`, but first we need to make a few tweaks to the generated migration in `priv/repo/migrations/*_create_products.exs`:
+
+```diff
+  def change do
+    create table(:products) do
+      add :title, :string
+      add :description, :string
+-     add :price, :decimal
++     add :price, :decimal, precision: 15, scale: 6, null: false
+-     add :views, :integer
++     add :views, :integer, default: 0, null: false
+
+      timestamps()
+    end
+```
+
+We modified our price column to a specific precision of 15, scale of 6, along with a not-null constraint. This ensures we store currency with proper precision for any mathematical operations we may perform. Next, we added a default value and not-null constraint to our views count. With our changes in place, we're ready to migrate up our database. Let's do that now:
+
+```console
+$ mix ecto.migrate
+14:09:02.260 [info] == Running 20250201185747 Hello.Repo.Migrations.CreateProducts.change/0 forward
+
+14:09:02.262 [info] create table products
+
+14:09:02.273 [info] == Migrated 20250201185747 in 0.0s
+```
+
+Before we jump into the generated code, let's start the server with `mix phx.server` and visit [http://localhost:4000/products](http://localhost:4000/products). Let's follow the "New Product" link and click the "Save" button without providing any input. When we submit the form, we can see all the validation errors inline with the inputs. Nice! Out of the box, the context generator included the schema fields in our form template and we can see our default validations for required inputs are in effect. Let's enter some example product data and resubmit the form:
+
+```text
+Product created successfully.
+
+Title: Metaprogramming Elixir
+Description: Write Less Code, Get More Done (and Have Fun!)
+Price: 15.000000
+Views: 0
+```
+
+If we follow the "Back" link, we get a list of all products, which should contain the one we just created. Likewise, we can update this record or delete it. Now that we've seen how it works in the browser, it's time to take a look at the generated code.
+
+> #### Naming things is hard {: .tip}
+>
+> When starting a web application, it may be hard to draw lines or name its different contexts, especially when the domain you are working with is not as well established as e-commerce.
+>
+> If you're stuck when defining or naming a context, you can simply create a new context using the plural form of the resource you're creating. For example, a `Products` context for managing products. You will find that, even in such cases, you will organically discover other resources that belong to the `Products` context, such as categories or image galleries.
+>
+> As your applications grows and the different parts of your system become clear, you can simply rename the context or move resources around. The beauty of Elixir modules is moving them around should be simply a matter of renaming the module names and their callers (and renaming the files for consistency).
+
+## Grokking generated code
+
+That little `mix phx.gen.html` command packed a surprising punch. We got a lot of functionality out-of-the-box for creating, updating, and deleting products in our catalog. This is far from a full-featured app, but remember, generators are first and foremost learning tools and a starting point for you to begin building real features. Code generation can't solve all your problems, but it will teach you the ins and outs of Phoenix and nudge you towards the proper mindset when designing your application.
+
+Let's first check out the `ProductController` that was generated in `lib/hello_web/controllers/product_controller.ex`:
+
+```elixir
+defmodule HelloWeb.ProductController do
+  use HelloWeb, :controller
+
+  alias Hello.Catalog
+  alias Hello.Catalog.Product
+
+  def index(conn, _params) do
+    products = Catalog.list_products()
+    render(conn, :index, products: products)
+  end
+
+  def new(conn, _params) do
+    changeset = Catalog.change_product(%Product{})
+    render(conn, :new, changeset: changeset)
+  end
+
+  def create(conn, %{"product" => product_params}) do
+    case Catalog.create_product(product_params) do
+      {:ok, product} ->
+        conn
+        |> put_flash(:info, "Product created successfully.")
+        |> redirect(to: ~p"/products/#{product}")
+
+      {:error, %Ecto.Changeset{} = changeset} ->
+        render(conn, :new, changeset: changeset)
+    end
+  end
+
+  def show(conn, %{"id" => id}) do
+    product = Catalog.get_product!(id)
+    render(conn, :show, product: product)
+  end
+  ...
+end
+```
+
+We've seen how controllers work in our [controller guide](controllers.html), so the code probably isn't too surprising. What is worth noticing is how our controller calls into the `Catalog` context. We can see that the `index` action fetches a list of products with `Catalog.list_products/0`, and how products are persisted in the `create` action with `Catalog.create_product/1`. We haven't yet looked at the catalog context, so we don't yet know how product fetching and creation is happening under the hood – *but that's the point*. Our Phoenix controller is the web interface into our greater application. It shouldn't be concerned with the details of how products are fetched from the database or persisted into storage. We only care about telling our application to perform some work for us. This is great because our business logic and storage details are decoupled from the web layer of our application. If we move to a full-text storage engine later for fetching products instead of a SQL query, our controller doesn't need to be changed. Likewise, we can reuse our context code from any other interface in our application, be it a channel, mix task, or long-running process importing CSV data.
+
+In the case of our `create` action, when we successfully create a product, we use `Phoenix.Controller.put_flash/3` to show a success message, and then we redirect to the router's product show page. Conversely, if `Catalog.create_product/1` fails, we render our `"new.html"` template and pass along the Ecto changeset for the template to lift error messages from.
+
+Next, let's dig deeper and check out our `Catalog` context in `lib/hello/catalog.ex`:
+
+```elixir
+defmodule Hello.Catalog do
+  @moduledoc """
+  The Catalog context.
+  """
+
+  import Ecto.Query, warn: false
+  alias Hello.Repo
+
+  alias Hello.Catalog.Product
+
+  @doc """
+  Returns the list of products.
+
+  ## Examples
+
+      iex> list_products()
+      [%Product{}, ...]
+
+  """
+  def list_products do
+    Repo.all(Product)
+  end
+  ...
+end
+```
+
+This module will be the public API for all product catalog functionality in our system. For example, in addition to product detail management, we may also handle product category classification and product variants for things like optional sizing, trims, etc. If we look at the `list_products/0` function, we can see the private details of product fetching. And it's super simple. We have a call to `Repo.all(Product)`. We saw how Ecto repo queries worked in the [Ecto guide](ecto.html), so this call should look familiar. Our `list_products` function is a generalized function name specifying the *intent* of our code – namely to list products. The details of that intent where we use our Repo to fetch the products from our PostgreSQL database is hidden from our callers. This is a common theme we'll see re-iterated as we use the Phoenix generators. Phoenix will push us to think about where we have different responsibilities in our application, and then to wrap up those different areas behind well-named modules and functions that make the intent of our code clear, while encapsulating the details.
+
+Now we know how data is fetched, but how are products persisted? Let's take a look at the `Catalog.create_product/1` function:
+
+```elixir
+  @doc """
+  Creates a product.
+
+  ## Examples
+
+      iex> create_product(%{field: value})
+      {:ok, %Product{}}
+
+      iex> create_product(%{field: bad_value})
+      {:error, %Ecto.Changeset{}}
+
+  """
+  def create_product(attrs \\ %{}) do
+    %Product{}
+    |> Product.changeset(attrs)
+    |> Repo.insert()
+  end
+```
+
+There's more documentation than code here, but a couple of things are important to highlight. First, we can see again that our Ecto Repo is used under the hood for database access. You probably also noticed the call to `Product.changeset/2`. We talked about changesets before, and now we see them in action in our context.
+
+If we open up the `Product` schema in `lib/hello/catalog/product.ex`, it will look immediately familiar:
+
+```elixir
+defmodule Hello.Catalog.Product do
+  use Ecto.Schema
+  import Ecto.Changeset
+
+  schema "products" do
+    field :description, :string
+    field :price, :decimal
+    field :title, :string
+    field :views, :integer
+
+    timestamps()
+  end
+
+  @doc false
+  def changeset(product, attrs) do
+    product
+    |> cast(attrs, [:title, :description, :price, :views])
+    |> validate_required([:title, :description, :price, :views])
+  end
+end
+```
+
+This is just what we saw before when we ran `mix phx.gen.schema`, except here we see a `@doc false` above our `changeset/2` function. This tells us that while this function is publicly callable, it's not part of the public context API. Callers that build changesets do so via the context API. For example, `Catalog.create_product/1` calls into our `Product.changeset/2` to build the changeset from user input. Callers, such as our controller actions, do not access `Product.changeset/2` directly. All interaction with our product changesets is done through the public `Catalog` context.
+
+## Adding Catalog functions
+
+As we've seen, your context modules are dedicated modules that expose and group related functionality. Phoenix generates generic functions, such as `list_products` and `update_product`, but they only serve as a basis for you to grow your business logic and application from. Let's add one of the basic features of our catalog by tracking product page view count.
+
+For any ecommerce system, the ability to track how many times a product page has been viewed is essential for marketing, suggestions, ranking, etc. While we could try to use the existing `Catalog.update_product` function, along the lines of `Catalog.update_product(product, %{views: product.views + 1})`, this would not only be prone to race conditions, but it would also require the caller to know too much about our Catalog system. To see why the race condition exists, let's walk through the possible execution of events:
+
+Intuitively, you would assume the following events:
+
+  1. User 1 loads the product page with count of 13
+  2. User 1 saves the product page with count of 14
+  3. User 2 loads the product page with count of 14
+  4. User 2 saves the product page with count of 15
+
+While in practice this would happen:
+
+  1. User 1 loads the product page with count of 13
+  2. User 2 loads the product page with count of 13
+  3. User 1 saves the product page with count of 14
+  4. User 2 saves the product page with count of 14
+
+The race conditions would make this an unreliable way to update the existing table since multiple callers may be updating out of date view values. There's a better way.
+
+Let's think of a function that describes what we want to accomplish. Here's how we would like to use it:
+
+```elixir
+product = Catalog.inc_page_views(product)
+```
+
+That looks great. Our callers will have no confusion over what this function does, and we can wrap up the increment in an atomic operation to prevent race conditions.
+
+Open up your catalog context (`lib/hello/catalog.ex`), and add this new function:
+
+```elixir
+  def inc_page_views(%Product{} = product) do
+    {1, [%Product{views: views}]} =
+      from(p in Product, where: p.id == ^product.id, select: [:views])
+      |> Repo.update_all(inc: [views: 1])
+
+    put_in(product.views, views)
+  end
+```
+
+We built a query for fetching the current product given its ID which we pass to `Repo.update_all`. Ecto's `Repo.update_all` allows us to perform batch updates against the database, and is perfect for atomically updating values, such as incrementing our views count. The result of the repo operation returns the number of updated records, along with the selected schema values specified by the `select` option. When we receive the new product views, we use `put_in(product.views, views)` to place the new view count within the product struct.
+
+With our context function in place, let's make use of it in our product controller. Update your `show` action in `lib/hello_web/controllers/product_controller.ex` to call our new function:
+
+```elixir
+  def show(conn, %{"id" => id}) do
+    product =
+      id
+      |> Catalog.get_product!()
+      |> Catalog.inc_page_views()
+
+    render(conn, :show, product: product)
+  end
+```
+
+We modified our `show` action to pipe our fetched product into `Catalog.inc_page_views/1`, which will return the updated product. Then we rendered our template just as before. Let's try it out. Refresh one of your product pages a few times and watch the view count increase.
+
+We can also see our atomic update in action in the ecto debug logs:
+
+```text
+[debug] QUERY OK source="products" db=0.5ms idle=834.5ms
+UPDATE "products" AS p0 SET "views" = p0."views" + $1 WHERE (p0."id" = $2) RETURNING p0."views" [1, 1]
+```
+
+Good work!
+
+As we've seen, designing with contexts gives you a solid foundation to grow your application from. Using discrete, well-defined APIs that expose the intent of your system allows you to write more maintainable applications with reusable code. Now that we know how to start extending our context API, lets explore handling relationships within a context.
diff --git a/guides/directory_structure.md b/guides/directory_structure.md
index bf8fb1c2b..4e8ed6537 100644
--- a/guides/directory_structure.md
+++ b/guides/directory_structure.md
@@ -121,7 +121,7 @@ The `assets` directory contains source files related to front-end assets, such a
 
 Your other static assets are placed in the `priv/static` folder, where `priv/static/assets` is kept for generated assets. Everything in `priv/static` is served by the `Plug.Static` plug configured in `lib/hello_web/endpoint.ex`.  When running in dev mode (`MIX_ENV=dev`), Phoenix watches for any changes you make in the `assets` directory, and then takes care of updating your front end application in your browser as you work.
 
-Note that when you first create your Phoenix app using `mix phx.new` it is possible to specify options that will affect the presence and layout of the `assets` directory.  In fact, Phoenix apps can bring their own front end tools or not have a front-end at all (handy if you're writing an API for example).  For more information you can run `mix help phx.new` or see the documentation in [Mix tasks](mix_tasks.html).
+Note that when you first create your Phoenix app using `mix phx.new` it is possible to specify options that will affect the presence and layout of the `assets` directory.  In fact, Phoenix apps can bring their own front end tools or not have a front-end at all (handy if you're writing an API for example).  For more information you can run `mix help phx.new`.
 
 If the default esbuild integration does not cover your needs, for example because you want to use another build tool, you can switch to a [custom assets build](asset_management.html#custom_builds).
 
diff --git a/guides/ecto.md b/guides/ecto.md
index 777a271cd..78a62f7e2 100644
--- a/guides/ecto.md
+++ b/guides/ecto.md
@@ -18,7 +18,7 @@ Ecto also provides support for other databases and it has many learning resource
 
 This guide assumes that we have generated our new application with Ecto integration and that we will be using PostgreSQL. The introductory guides cover how to get your first application up and running. For using other databases, see the [Using other databases](#using-other-databases) section.
 
-## Using the schema and migration generator
+## Using `phx.gen.schema`
 
 Once we have Ecto and PostgreSQL installed and configured, the easiest way to use Ecto is to generate an Ecto *schema* through the `phx.gen.schema` task. Ecto schemas are a way for us to specify how Elixir data types map to and from external sources, such as database tables. Let's generate a `User` schema with `name`, `email`, `bio`, and `number_of_pets` fields.
 
@@ -478,7 +478,7 @@ SELECT u0."email" FROM "users" AS u0 []
 ["user1@example.com", "user2@example.com"]
 ```
 
-First, we imported [`Ecto.Query`], which imports the [`from/2`] macro of Ecto's Query DSL. Next, we built a query which selects all the email addresses in our users table. Let's try another example.
+First, we imported `Ecto.Query`, which imports the [`from/2`] macro of Ecto's Query DSL. Next, we built a query which selects all the email addresses in our users table. Let's try another example.
 
 ```elixir
 iex> Repo.one(from u in User, where: ilike(u.email, "%1%"),
@@ -505,86 +505,202 @@ In addition to inserts, we can also perform updates and deletes with [`Repo.upda
 
 There is quite a bit more that Ecto can do and we've only barely scratched the surface. With a solid Ecto foundation in place, we're now ready to continue building our app and integrate the web-facing application with our backend persistence. Along the way, we'll expand our Ecto knowledge and learn how to properly isolate our web interface from the underlying details of our system. Please take a look at the [Ecto documentation](https://hexdocs.pm/ecto/) for the rest of the story.
 
-In our [contexts guide](contexts.html), we'll find out how to wrap up our Ecto access and business logic behind modules that group related functionality. We'll see how Phoenix helps us design maintainable applications, and we'll find out about other neat Ecto features along the way.
+In our [Data modelling guides](contexts.html), we'll find out how to wrap up our Ecto access and business logic behind modules that group related functionality. We'll see how Phoenix helps us design maintainable applications, and we'll find out about other neat Ecto features along the way.
 
-## Using other databases
+## Mix tasks
 
-Phoenix applications are configured to use PostgreSQL by default, but what if we want to use another database, such as MySQL? In this section, we'll walk through changing that default whether we are about to create a new application, or whether we have an existing one configured for PostgreSQL.
+Ecto comes with a collection of Mix tasks to make it easier to manage your database and your application. Here is a quick look into the most important ones.
 
-If we are about to create a new application, configuring our application to use MySQL is easy. We can simply pass the `--database mysql` flag to `phx.new` and everything will be configured correctly.
+### `mix ecto.create`
+
+This task will create the database specified by our application repositories, but we can pass in another repo if we want.
+
+Here's what it looks like in action.
+
+```console
+$ mix ecto.create
+The database for Hello.Repo has been created.
+```
+
+There are a few things that can go wrong with `ecto.create`. If our Postgres database doesn't have a "postgres" role (user), we'll get an error like this one.
+
+```console
+$ mix ecto.create
+** (Mix) The database for Hello.Repo couldn't be created, reason given: psql: FATAL:  role "postgres" does not exist
+```
+
+We can fix this by creating the "postgres" role in the `psql` console with the permissions needed to log in and create a database.
+
+```console
+=# CREATE ROLE postgres LOGIN CREATEDB;
+CREATE ROLE
+```
+
+If the "postgres" role does not have permission to log in to the application, we'll get this error.
+
+```console
+$ mix ecto.create
+** (Mix) The database for Hello.Repo couldn't be created, reason given: psql: FATAL:  role "postgres" is not permitted to log in
+```
+
+To fix this, we need to change the permissions on our "postgres" user to allow login.
+
+```console
+=# ALTER ROLE postgres LOGIN;
+ALTER ROLE
+```
+
+If the "postgres" role does not have permission to create a database, we'll get this error.
+
+```console
+$ mix ecto.create
+** (Mix) The database for Hello.Repo couldn't be created, reason given: ERROR:  permission denied to create database
+```
+
+To fix this, we need to change the permissions on our "postgres" user in the `psql` console  to allow database creation.
+
+```console
+=# ALTER ROLE postgres CREATEDB;
+ALTER ROLE
+```
+
+If the "postgres" role is using a password different from the default "postgres", we'll get this error.
+
+```console
+$ mix ecto.create
+** (Mix) The database for Hello.Repo couldn't be created, reason given: psql: FATAL:  password authentication failed for user "postgres"
+```
+
+To fix this, we can change the password in the environment specific configuration file. For the development environment the password used can be found at the bottom of the `config/dev.exs` file.
+
+Finally, if we happen to have another repo called `OurCustom.Repo` that we want to create the database for, we can run this.
 
 ```console
-$ mix phx.new hello_phoenix --database mysql
+$ mix ecto.create -r OurCustom.Repo
+The database for OurCustom.Repo has been created.
 ```
 
-This will set up all the correct dependencies and configuration for us automatically. Once we install those dependencies with `mix deps.get`, we'll be ready to begin working with Ecto in our application.
+### `mix ecto.drop`
 
-If we have an existing application, all we need to do is switch adapters and make some small configuration changes.
+This task will drop the database specified in our repo. By default it will look for the repo named after our application (the one generated with our app unless we opted out of Ecto). It will not prompt us to check if we're sure we want to drop the database, so do exercise caution.
 
-To switch adapters, we need to remove the Postgrex dependency and add a new one for MyXQL instead.
+```console
+$ mix ecto.drop
+The database for Hello.Repo has been dropped.
+```
+
+### `mix ecto.gen.migration`
+
+Migrations are a programmatic, repeatable way to affect changes to a database schema. Phoenix generators take care of generating migrations for us whenever we create a new context or schema, but if you want to generate a migration from scratch, `mix ecto.gen.migration` has our back. Let's see an example.
+
+We simply need to invoke the task with a `snake_case` version of the module name that we want. Preferably, the name will describe what we want the migration to do.
 
-Let's open up our `mix.exs` file and do that now.
+```console
+$ mix ecto.gen.migration add_comments_table
+* creating priv/repo/migrations
+* creating priv/repo/migrations/20150318001628_add_comments_table.exs
+```
+
+Notice that the migration's filename begins with a string representation of the date and time the file was created.
+
+Let's take a look at the file `ecto.gen.migration` has generated for us at `priv/repo/migrations/20150318001628_add_comments_table.exs`.
 
 ```elixir
-defmodule HelloPhoenix.MixProject do
-  use Mix.Project
-
-  ...
-  # Specifies your project dependencies.
-  #
-  # Type `mix help deps` for examples and options.
-  defp deps do
-    [
-      {:phoenix, "~> 1.4.0"},
-      {:phoenix_ecto, "~> 4.4"},
-      {:ecto_sql, "~> 3.10"},
-      {:myxql, ">= 0.0.0"},
-      ...
-    ]
+defmodule Hello.Repo.Migrations.AddCommentsTable do
+  use Ecto.Migration
+
+  def change do
   end
 end
 ```
 
-Next, we need to configure our adapter to use the default MySQL credentials by updating `config/dev.exs`:
+Notice that there is a single function `change/0` which will handle both forward migrations and rollbacks. We'll define the schema changes that we want using Ecto's handy DSL, and Ecto will figure out what to do depending on whether we are rolling forward or rolling back. Very nice indeed.
+
+What we want to do is create a `comments` table with a `body` column, a `word_count` column, and timestamp columns for `inserted_at` and `updated_at`.
 
 ```elixir
-config :hello_phoenix, HelloPhoenix.Repo,
-  username: "root",
-  password: "",
-  database: "hello_phoenix_dev"
+...
+def change do
+  create table(:comments) do
+    add :body, :string
+    add :word_count, :integer
+    timestamps()
+  end
+end
+...
+```
+
+For more information on how to modify your database schema please refer to the
+[Ecto's migration DSL docs](https://hexdocs.pm/ecto_sql/Ecto.Migration.html).
+For example, to alter an existing schema see the documentation on Ecto’s
+[`alter/2`](`Ecto.Migration.alter/2`) function.
+
+That's it! We're ready to run our migration.
+
+### `mix ecto.migrate`
+
+Once we have our migration module ready, we can simply run `mix ecto.migrate` to have our changes applied to the database. We have already used it earlier in this chapter, but let's take it for a spin once more for our newly generated migration.
+
+```console
+$ mix ecto.migrate
+[info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 forward
+[info] create table comments
+[info] == Migrated in 0.1s
+```
+
+When we first run `ecto.migrate`, it will create a table for us called `schema_migrations`. This will keep track of all the migrations which we run by storing the timestamp portion of the migration's filename.
+
+Here's what the `schema_migrations` table looks like.
+
+```console
+hello_dev=# select * from schema_migrations;
+version        |     inserted_at
+---------------+---------------------
+20250317170448 | 2025-03-17 21:07:26
+20250318001628 | 2025-03-18 01:45:00
+(2 rows)
 ```
 
-If we have an existing configuration block for our `HelloPhoenix.Repo`, we can simply change the values to match our new ones. You also need to configure the correct values in the `config/test.exs` and `config/runtime.exs` (formerly `config/prod.secret.exs`) files as well.
+When we roll back a migration, `mix ecto.rollback`, to be discussed next, we will remove the record representing this migration from `schema_migrations`.
 
-The last change is to open up `lib/hello_phoenix/repo.ex` and make sure to set the `:adapter` to `Ecto.Adapters.MyXQL`.
+By default, `ecto.migrate` will execute all pending migrations. We can exercise more control over which migrations we run by specifying some options when we run the task.
 
-Now all we need to do is fetch our new dependency, and we'll be ready to go.
+We can specify the number of pending migrations we would like to run with the `-n` or `--step` options.
 
 ```console
-$ mix deps.get
+$ mix ecto.migrate -n 2
+[info] == Running Hello.Repo.Migrations.CreatePost.change/0 forward
+[info] create table posts
+[info] == Migrated in 0.0s
+[info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 forward
+[info] create table comments
+[info] == Migrated in 0.0s
 ```
 
-With our new adapter installed and configured, we're ready to create our database.
+The `--step` option will behave the same way.
 
 ```console
-$ mix ecto.create
+$ mix ecto.migrate --step 2
 ```
 
-The database for HelloPhoenix.Repo has been created.
-We're also ready to run any migrations, or do anything else with Ecto that we may choose.
+The `--to` option will run all migrations up to and including given version.
 
 ```console
-$ mix ecto.migrate
-[info] == Running HelloPhoenix.Repo.Migrations.CreateUser.change/0 forward
-[info] create table users
-[info] == Migrated in 0.2s
+$ mix ecto.migrate --to 20150317170448
 ```
 
-## Other options
+### `mix ecto.rollback`
+
+The `mix ecto.rollback` task will reverse the last migration we have run, undoing the schema changes. `ecto.migrate` and `ecto.rollback` are mirror images of each other.
 
-While Phoenix uses the `Ecto` project to interact with the data access layer, there are many other data access options, some even built into the Erlang standard library. [ETS](https://www.erlang.org/doc/man/ets.html) – available in Ecto via [`etso`](https://hexdocs.pm/etso/) – and [DETS](https://www.erlang.org/doc/man/dets.html) are key-value data stores built into [OTP](https://www.erlang.org/doc/). OTP also provides a relational database called [Mnesia](https://www.erlang.org/doc/man/mnesia.html) with its own query language called QLC. Both Elixir and Erlang also have a number of libraries for working with a wide range of popular data stores.
+```console
+$ mix ecto.rollback
+[info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 backward
+[info] drop table comments
+[info] == Migrated in 0.0s
+```
 
-The data world is your oyster, but we won't be covering these options in these guides.
+`ecto.rollback` will handle the same options as `ecto.migrate`, so `-n`, `--step`, `-v`, and `--to` will behave as they do for `ecto.migrate`.
 
 [`cast/3`]: `Ecto.Changeset.cast/3`
 [`from/2`]: `Ecto.Query.from/2`
diff --git a/guides/howto/swapping_databases.md b/guides/howto/swapping_databases.md
new file mode 100644
index 000000000..dd514f572
--- /dev/null
+++ b/guides/howto/swapping_databases.md
@@ -0,0 +1,82 @@
+# Swapping Databases
+
+Phoenix applications are configured to use PostgreSQL by default, but what if we want to use another database, such as MySQL? In this guide, we'll walk through changing that default whether we are about to create a new application, or whether we have an existing one configured for PostgreSQL.
+
+## Using `phx.new`
+
+If we are about to create a new application, configuring our application to use MySQL is easy. We can simply pass the `--database mysql` flag to `phx.new` and everything will be configured correctly.
+
+```console
+$ mix phx.new hello_phoenix --database mysql
+```
+
+This will set up all the correct dependencies and configuration for us automatically. Once we install those dependencies with `mix deps.get`, we'll be ready to begin working with Ecto in our application.
+
+## Within an existing application
+
+If we have an existing application, all we need to do is switch adapters and make some small configuration changes.
+
+To switch adapters, we need to remove the Postgrex dependency and add a new one for MyXQL instead.
+
+Let's open up our `mix.exs` file and do that now.
+
+```elixir
+defmodule HelloPhoenix.MixProject do
+  use Mix.Project
+
+  ...
+  # Specifies your project dependencies.
+  #
+  # Type `mix help deps` for examples and options.
+  defp deps do
+    [
+      {:phoenix, "~> 1.4.0"},
+      {:phoenix_ecto, "~> 4.4"},
+      {:ecto_sql, "~> 3.10"},
+      {:myxql, ">= 0.0.0"},
+      ...
+    ]
+  end
+end
+```
+
+Next, we need to configure our adapter to use the default MySQL credentials by updating `config/dev.exs`:
+
+```elixir
+config :hello_phoenix, HelloPhoenix.Repo,
+  username: "root",
+  password: "",
+  database: "hello_phoenix_dev"
+```
+
+If we have an existing configuration block for our `HelloPhoenix.Repo`, we can simply change the values to match our new ones. You also need to configure the correct values in the `config/test.exs` and `config/runtime.exs` (formerly `config/prod.secret.exs`) files as well.
+
+The last change is to open up `lib/hello_phoenix/repo.ex` and make sure to set the `:adapter` to `Ecto.Adapters.MyXQL`.
+
+Now all we need to do is fetch our new dependency, and we'll be ready to go.
+
+```console
+$ mix deps.get
+```
+
+With our new adapter installed and configured, we're ready to create our database.
+
+```console
+$ mix ecto.create
+```
+
+The database for HelloPhoenix.Repo has been created.
+We're also ready to run any migrations, or do anything else with Ecto that we may choose.
+
+```console
+$ mix ecto.migrate
+[info] == Running HelloPhoenix.Repo.Migrations.CreateUser.change/0 forward
+[info] create table users
+[info] == Migrated in 0.2s
+```
+
+## Other options
+
+While Phoenix uses the `Ecto` project to interact with the data access layer, there are many other data access options, some even built into the Erlang standard library. [ETS](https://www.erlang.org/doc/man/ets.html) – available in Ecto via [`etso`](https://hexdocs.pm/etso/) – and [DETS](https://www.erlang.org/doc/man/dets.html) are key-value data stores built into [Erlang/OTP](https://www.erlang.org/doc/). Both Elixir and Erlang also have a number of libraries for working with a wide range of popular data stores.
+
+The data world is your oyster, but we won't be covering these options in these guides.
diff --git a/guides/introduction/overview.md b/guides/introduction/overview.md
index 35ef681b4..bfa011ca1 100644
--- a/guides/introduction/overview.md
+++ b/guides/introduction/overview.md
@@ -12,7 +12,9 @@ The guides that you are currently looking at provide an overview of all parts th
 
   * Guides - in-depth guides covering the main components in Phoenix and Phoenix applications
 
-  * Authentication - in-depth guide covering how to use `mix phx.gen.auth`
+  * Data modelling - building the initial features of an e-commerce application to learn about more data modelling with Phoenix
+
+  * Authn and Authz - learn how to use the tools Phoenix provides for authentication and authorization
 
   * Real-time components - in-depth guides covering Phoenix's built-in real-time components
 
diff --git a/guides/introduction/up_and_running.md b/guides/introduction/up_and_running.md
index 49445cb5b..ff49af148 100644
--- a/guides/introduction/up_and_running.md
+++ b/guides/introduction/up_and_running.md
@@ -42,7 +42,7 @@ $ mix phx.new hello
 >
 > - [Phoenix.LiveView](https://hexdocs.pm/phoenix_live_view/) for building realtime and interactive web applications. You can skip this with `--no-live`.
 >
-> Read the [Mix Tasks Guide](mix_tasks.html#phoenix-specific-mix-tasks) for the full list of things that can be excluded, among other options.
+> Run `mix help phx.new` to learn all options.
 
 ```console
 mix phx.new hello
@@ -85,9 +85,9 @@ You can also run your app inside IEx (Interactive Elixir) as:
 
 Once our dependencies are installed, the task will prompt us to change into our project directory and start our application.
 
-Phoenix assumes that our PostgreSQL database will have a `postgres` user account with the correct permissions and a password of "postgres". If that isn't the case, please see the [Mix Tasks Guide](mix_tasks.html#ecto-specific-mix-tasks) to learn more about the `mix ecto.create` task.
+Phoenix assumes that our PostgreSQL database will have a `postgres` user account with the correct permissions and a password of "postgres". Let's give it a try.
 
-Ok, let's give it a try. First, we'll `cd` into the `hello/` directory we've just created:
+First, we'll `cd` into the `hello/` directory we've just created:
 
 ```console
 $ cd hello
@@ -102,9 +102,7 @@ Generated hello app
 The database for Hello.Repo has been created
 ```
 
-In case the database could not be created, see the guides for the [`mix ecto.create`](mix_tasks.html#mix-ecto-create) for general troubleshooting.
-
-> Note: if this is the first time you are running this command, Phoenix may also ask to install Rebar. Go ahead with the installation as Rebar is used to build Erlang packages.
+In case the database could not be created, see [our Ecto section on Mix tasks](ecto.html#mix-tasks) or run `mix help ecto.create`.
 
 And finally, we'll start the Phoenix server:
 
diff --git a/guides/mix_tasks.md b/guides/mix_tasks.md
deleted file mode 100644
index 4a98d337b..000000000
--- a/guides/mix_tasks.md
+++ /dev/null
@@ -1,815 +0,0 @@
-# Mix tasks
-
-There are currently a number of built-in Phoenix-specific and Ecto-specific [Mix tasks](`Mix.Task`) available to us within a newly-generated application. We can also create our own application specific tasks.
-
-> Note to learn more about `mix`, you can read Elixir's official [Introduction to Mix](https://hexdocs.pm/elixir/introduction-to-mix.html).
-
-## Phoenix tasks
-
-```console
-$ mix help --search "phx"
-mix local.phx          # Updates the Phoenix project generator locally
-mix phx                # Prints Phoenix help information
-mix phx.digest         # Digests and compresses static files
-mix phx.digest.clean   # Removes old versions of static assets.
-mix phx.gen.auth       # Generates authentication logic for a resource
-mix phx.gen.cert       # Generates a self-signed certificate for HTTPS testing
-mix phx.gen.channel    # Generates a Phoenix channel
-mix phx.gen.context    # Generates a context with functions around an Ecto schema
-mix phx.gen.embedded   # Generates an embedded Ecto schema file
-mix phx.gen.html       # Generates controller, views, and context for an HTML resource
-mix phx.gen.json       # Generates controller, views, and context for a JSON resource
-mix phx.gen.live       # Generates LiveView, templates, and context for a resource
-mix phx.gen.notifier   # Generates a notifier that delivers emails by default
-mix phx.gen.presence   # Generates a Presence tracker
-mix phx.gen.schema     # Generates an Ecto schema and migration file
-mix phx.gen.secret     # Generates a secret
-mix phx.gen.socket     # Generates a Phoenix socket handler
-mix phx.new            # Creates a new Phoenix application
-mix phx.new.ecto       # Creates a new Ecto project within an umbrella project
-mix phx.new.web        # Creates a new Phoenix web project within an umbrella project
-mix phx.routes         # Prints all routes
-mix phx.server         # Starts applications and their servers
-```
-
-We have seen all of these at one point or another in the guides, but having all the information about them in one place seems like a good idea.
-
-We will cover all Phoenix Mix tasks, except `phx.new`, `phx.new.ecto`, and `phx.new.web`, which are part of the Phoenix installer. You can learn more about them or any other task by calling `mix help TASK`.
-
-### `mix phx.gen.html`
-
-Phoenix offers the ability to generate all the code to stand up a complete HTML resource — Ecto migration, Ecto context, controller with all the necessary actions, view, and templates. This can be a tremendous time saver. Let's take a look at how to make this happen.
-
-The `mix phx.gen.html` task takes the following arguments: the module name of the context, the module name of the schema, the resource name, and a list of column_name:type attributes. The module name we pass in must conform to the Elixir rules of module naming, following proper capitalization.
-
-```console
-$ mix phx.gen.html Blog Post posts body:string word_count:integer
-* creating lib/hello_web/controllers/post_controller.ex
-* creating lib/hello_web/controllers/post_html/edit.html.heex
-* creating lib/hello_web/controllers/post_html/post_form.html.heex
-* creating lib/hello_web/controllers/post_html/index.html.heex
-* creating lib/hello_web/controllers/post_html/new.html.heex
-* creating lib/hello_web/controllers/post_html/show.html.heex
-* creating lib/hello_web/controllers/post_html.ex
-* creating test/hello_web/controllers/post_controller_test.exs
-* creating lib/hello/blog/post.ex
-* creating priv/repo/migrations/20211001233016_create_posts.exs
-* creating lib/hello/blog.ex
-* injecting lib/hello/blog.ex
-* creating test/hello/blog_test.exs
-* injecting test/hello/blog_test.exs
-* creating test/support/fixtures/blog_fixtures.ex
-* injecting test/support/fixtures/blog_fixtures.ex
-```
-
-When `mix phx.gen.html` is done creating files, it helpfully tells us that we need to add a line to our router file as well as run our Ecto migrations.
-
-```console
-Add the resource to your browser scope in lib/hello_web/router.ex:
-
-    resources "/posts", PostController
-
-Remember to update your repository by running migrations:
-
-    $ mix ecto.migrate
-```
-
-Important: If we don't do this, we will see the following warnings in our logs, and our application will error when compiling.
-
-```console
-$ mix phx.server
-Compiling 17 files (.ex)
-
-warning: no route path for HelloWeb.Router matches \"/posts\"
-  lib/hello_web/controllers/post_controller.ex:22: HelloWeb.PostController.index/2
-```
-
-If we don't want to create a context or schema for our resource we can use the `--no-context` flag. Note that this still requires a context module name as a parameter.
-
-```console
-$ mix phx.gen.html Blog Post posts body:string word_count:integer --no-context
-* creating lib/hello_web/controllers/post_controller.ex
-* creating lib/hello_web/controllers/post_html/edit.html.heex
-* creating lib/hello_web/controllers/post_html/post_form.html.heex
-* creating lib/hello_web/controllers/post_html/index.html.heex
-* creating lib/hello_web/controllers/post_html/new.html.heex
-* creating lib/hello_web/controllers/post_html/show.html.heex
-* creating lib/hello_web/controllers/post_html.ex
-* creating test/hello_web/controllers/post_controller_test.exs
-```
-
-It will tell us we need to add a line to our router file, but since we skipped the context, it won't mention anything about `ecto.migrate`.
-
-```console
-Add the resource to your browser scope in lib/hello_web/router.ex:
-
-    resources "/posts", PostController
-```
-
-Similarly, if we want a context created without a schema for our resource we can use the `--no-schema` flag.
-
-```console
-$ mix phx.gen.html Blog Post posts body:string word_count:integer --no-schema
-* creating lib/hello_web/controllers/post_controller.ex
-* creating lib/hello_web/controllers/post_html/edit.html.heex
-* creating lib/hello_web/controllers/post_html/post_form.html.heex
-* creating lib/hello_web/controllers/post_html/index.html.heex
-* creating lib/hello_web/controllers/post_html/new.html.heex
-* creating lib/hello_web/controllers/post_html/show.html.heex
-* creating lib/hello_web/controllers/post_html.ex
-* creating test/hello_web/controllers/post_controller_test.exs
-* creating lib/hello/blog.ex
-* injecting lib/hello/blog.ex
-* creating test/hello/blog_test.exs
-* injecting test/hello/blog_test.exs
-* creating test/support/fixtures/blog_fixtures.ex
-* injecting test/support/fixtures/blog_fixtures.ex
-```
-
-It will tell us we need to add a line to our router file, but since we skipped the schema, it won't mention anything about `ecto.migrate`.
-
-### `mix phx.gen.json`
-
-Phoenix also offers the ability to generate all the code to stand up a complete JSON resource — Ecto migration, Ecto schema, controller with all the necessary actions and view. This command will not create any template for the app.
-
-The `mix phx.gen.json` task takes the following arguments: the module name of the context, the module name of the schema, the resource name, and a list of column_name:type attributes. The module name we pass in must conform to the Elixir rules of module naming, following proper capitalization.
-
-```console
-$ mix phx.gen.json Blog Post posts title:string content:string
-* creating lib/hello_web/controllers/post_controller.ex
-* creating lib/hello_web/controllers/post_json.ex
-* creating test/hello_web/controllers/post_controller_test.exs
-* creating lib/hello_web/controllers/changeset_json.ex
-* creating lib/hello_web/controllers/fallback_controller.ex
-* creating lib/hello/blog/post.ex
-* creating priv/repo/migrations/20170906153323_create_posts.exs
-* creating lib/hello/blog.ex
-* injecting lib/hello/blog.ex
-* creating test/hello/blog/blog_test.exs
-* injecting test/hello/blog/blog_test.exs
-* creating test/support/fixtures/blog_fixtures.ex
-* injecting test/support/fixtures/blog_fixtures.ex
-```
-
-When `mix phx.gen.json` is done creating files, it helpfully tells us that we need to add a line to our router file as well as run our Ecto migrations.
-
-```console
-Add the resource to the "/api" scope in lib/hello_web/router.ex:
-
-    resources "/posts", PostController, except: [:new, :edit]
-
-Remember to update your repository by running migrations:
-
-    $ mix ecto.migrate
-```
-
-Important: If we don't do this, we'll get the following warning in our logs and the application will error when attempting to compile:
-
-```console
-$ mix phx.server
-Compiling 19 files (.ex)
-
-warning: no route path for HelloWeb.Router matches \"/posts\"
-  lib/hello_web/controllers/post_controller.ex:22: HelloWeb.PostController.index/2
-```
-
-`mix phx.gen.json` also supports `--no-context`, `--no-schema`, and others, as in `mix phx.gen.html`.
-
-### `mix phx.gen.context`
-
-If we don't need a complete HTML/JSON resource and only need a context, we can use the `mix phx.gen.context` task. It will generate a context, a schema, a migration and a test case.
-
-The `mix phx.gen.context` task takes the following arguments: the module name of the context, the module name of the schema, the resource name, and a list of column_name:type attributes.
-
-```console
-$ mix phx.gen.context Accounts User users name:string age:integer
-* creating lib/hello/accounts/user.ex
-* creating priv/repo/migrations/20170906161158_create_users.exs
-* creating lib/hello/accounts.ex
-* injecting lib/hello/accounts.ex
-* creating test/hello/accounts/accounts_test.exs
-* injecting test/hello/accounts/accounts_test.exs
-* creating test/support/fixtures/accounts_fixtures.ex
-* injecting test/support/fixtures/accounts_fixtures.ex
-```
-
-> Note: If we need to namespace our resource we can simply namespace the first argument of the generator.
-
-```console
-$ mix phx.gen.context Admin.Accounts User users name:string age:integer
-* creating lib/hello/admin/accounts/user.ex
-* creating priv/repo/migrations/20170906161246_create_users.exs
-* creating lib/hello/admin/accounts.ex
-* injecting lib/hello/admin/accounts.ex
-* creating test/hello/admin/accounts_test.exs
-* injecting test/hello/admin/accounts_test.exs
-* creating test/support/fixtures/admin/accounts_fixtures.ex
-* injecting test/support/fixtures/admin/accounts_fixtures.ex
-```
-
-### `mix phx.gen.schema`
-
-If we don't need a complete HTML/JSON resource and are not interested in generating or altering a context we can use the `mix phx.gen.schema` task. It will generate a schema, and a migration.
-
-The `mix phx.gen.schema` task takes the following arguments: the module name of the schema (which may be namespaced), the resource name, and a list of column_name:type attributes.
-
-```console
-$ mix phx.gen.schema Accounts.Credential credentials email:string:unique user_id:references:users
-* creating lib/hello/accounts/credential.ex
-* creating priv/repo/migrations/20170906162013_create_credentials.exs
-```
-
-### `mix phx.gen.auth`
-
-Phoenix also offers the ability to generate all of the code to stand up a complete authentication system — Ecto migration, phoenix context, controllers, templates, etc. This can be a huge time saver, allowing you to quickly add authentication to your system and shift your focus back to the primary problems your application is trying to solve.
-
-The `mix phx.gen.auth` task takes the following arguments: the module name of the context, the module name of the schema, and a plural version of the schema name used to generate database tables and route paths.
-
-Here is an example version of the command:
-
-```console
-$ mix phx.gen.auth Accounts User users
-* creating priv/repo/migrations/20201205184926_create_users_auth_tables.exs
-* creating lib/hello/accounts/user_notifier.ex
-* creating lib/hello/accounts/user.ex
-* creating lib/hello/accounts/user_token.ex
-* creating lib/hello_web/controllers/user_auth.ex
-* creating test/hello_web/controllers/user_auth_test.exs
-* creating lib/hello_web/controllers/user_confirmation_html.ex
-* creating lib/hello_web/templates/user_confirmation/new.html.heex
-* creating lib/hello_web/templates/user_confirmation/edit.html.heex
-* creating lib/hello_web/controllers/user_confirmation_controller.ex
-* creating test/hello_web/controllers/user_confirmation_controller_test.exs
-* creating lib/hello_web/templates/user_registration/new.html.heex
-* creating lib/hello_web/controllers/user_registration_controller.ex
-* creating test/hello_web/controllers/user_registration_controller_test.exs
-* creating lib/hello_web/controllers/user_registration_html.ex
-* creating lib/hello_web/controllers/user_reset_password_html.ex
-* creating lib/hello_web/controllers/user_reset_password_controller.ex
-* creating test/hello_web/controllers/user_reset_password_controller_test.exs
-* creating lib/hello_web/templates/user_reset_password/edit.html.heex
-* creating lib/hello_web/templates/user_reset_password/new.html.heex
-* creating lib/hello_web/controllers/user_session_html.ex
-* creating lib/hello_web/controllers/user_session_controller.ex
-* creating test/hello_web/controllers/user_session_controller_test.exs
-* creating lib/hello_web/templates/user_session/new.html.heex
-* creating lib/hello_web/controllers/user_settings_html.ex
-* creating lib/hello_web/templates/user_settings/edit.html.heex
-* creating lib/hello_web/controllers/user_settings_controller.ex
-* creating test/hello_web/controllers/user_settings_controller_test.exs
-* creating lib/hello/accounts.ex
-* injecting lib/hello/accounts.ex
-* creating test/hello/accounts_test.exs
-* injecting test/hello/accounts_test.exs
-* creating test/support/fixtures/accounts_fixtures.ex
-* injecting test/support/fixtures/accounts_fixtures.ex
-* injecting test/support/conn_case.ex
-* injecting config/test.exs
-* injecting mix.exs
-* injecting lib/hello_web/router.ex
-* injecting lib/hello_web/router.ex - imports
-* injecting lib/hello_web/router.ex - plug
-* injecting lib/hello_web/templates/layout/root.html.heex
-```
-
-When `mix phx.gen.auth` is done creating files, it helpfully tells us that we need to re-fetch our dependencies as well as run our Ecto migrations.
-
-```console
-Please re-fetch your dependencies with the following command:
-
-    mix deps.get
-
-Remember to update your repository by running migrations:
-
-  $ mix ecto.migrate
-
-Once you are ready, visit "/users/register"
-to create your account and then access to "/dev/mailbox" to
-see the account confirmation email.
-```
-
-A more complete walk-through of how to get started with this generator is available in the [`mix phx.gen.auth` authentication guide](mix_phx_gen_auth.html).
-
-### `mix phx.gen.channel` and `mix phx.gen.socket`
-
-This task will generate a basic Phoenix channel, the socket to power the channel (if you haven't created one yet), as well a test case for it. It takes the module name for the channel as the only argument:
-
-```console
-$ mix phx.gen.channel Room
-* creating lib/hello_web/channels/room_channel.ex
-* creating test/hello_web/channels/room_channel_test.exs
-```
-
-If your application does not have a `UserSocket` yet, it will ask if you want to create one:
-
-```console
-The default socket handler - HelloWeb.UserSocket - was not found
-in its default location.
-
-Do you want to create it? [Y/n]
-```
-
-By confirming, a channel will be created, then you need to connect the socket in your endpoint:
-
-```console
-Add the socket handler to your `lib/hello_web/endpoint.ex`, for example:
-
-    socket "/socket", HelloWeb.UserSocket,
-      websocket: true,
-      longpoll: false
-
-For the front-end integration, you need to import the `user_socket.js`
-in your `assets/js/app.js` file:
-
-    import "./user_socket.js"
-```
-
-In case a `UserSocket` already exists or you decide to not create one, the `channel` generator will tell you to add it to the Socket manually:
-
-```console
-Add the channel to your `lib/hello_web/channels/user_socket.ex` handler, for example:
-
-    channel "rooms:lobby", HelloWeb.RoomChannel
-```
-
-You can also create a socket any time by invoking `mix phx.gen.socket`.
-
-### `mix phx.gen.presence`
-
-This task will generate a presence tracker. The module name can be passed as an argument,
-`Presence` is used if no module name is passed.
-
-```console
-$ mix phx.gen.presence Presence
-* lib/hello_web/channels/presence.ex
-
-Add your new module to your supervision tree,
-in lib/hello/application.ex:
-
-    children = [
-      ...
-      HelloWeb.Presence
-    ]
-```
-
-### `mix phx.routes`
-
-This task has a single purpose, to show us all the routes defined for a given router. We saw it used extensively in the [routing guide](routing.html).
-
-If we don't specify a router for this task, it will default to the router Phoenix generated for us.
-
-```console
-$ mix phx.routes
-GET  /  TaskTester.PageController.index/2
-```
-
-We can also specify an individual router if we have more than one for our application.
-
-```console
-$ mix phx.routes TaskTesterWeb.Router
-GET  /  TaskTesterWeb.PageController.index/2
-```
-
-### `mix phx.server`
-
-This is the task we use to get our application running. It takes no arguments at all. If we pass any in, they will be silently ignored.
-
-```console
-$ mix phx.server
-[info] Running TaskTesterWeb.Endpoint with Cowboy on port 4000 (http)
-```
-
-It will silently ignore our `DoesNotExist` argument:
-
-```console
-$ mix phx.server DoesNotExist
-[info] Running TaskTesterWeb.Endpoint with Cowboy on port 4000 (http)
-```
-
-If we would like to start our application and also have an `IEx` session open to it, we can run the Mix task within `iex` like this, `iex -S mix phx.server`.
-
-```console
-$ iex -S mix phx.server
-Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
-
-[info] Running TaskTesterWeb.Endpoint with Cowboy on port 4000 (http)
-Interactive Elixir (1.0.4) - press Ctrl+C to exit (type h() ENTER for help)
-iex(1)>
-```
-
-### `mix phx.digest`
-
-This task does two things, it creates a digest for our static assets and then compresses them.
-
-"Digest" here refers to an MD5 digest of the contents of an asset which gets added to the filename of that asset. This creates a sort of fingerprint for it. If the digest doesn't change, browsers and CDNs will use a cached version. If it does change, they will re-fetch the new version.
-
-Before we run this task let's inspect the contents of two directories in our hello application.
-
-First `priv/static/` which should look similar to this:
-
-```console
-├── assets
-│   ├── app.css
-│   └── app.js
-├── favicon.ico
-└── robots.txt
-```
-
-And then `assets/` which should look similar to this:
-
-```console
-├── css
-│   └── app.css
-├── js
-│   └── app.js
-└── vendor
-    ├── topbar.js
-    ├── heroicons.js
-    └── daisyui.js
-```
-
-All of these files are our static assets. Now let's run the `mix phx.digest` task.
-
-```console
-$ mix phx.digest
-Check your digested files at 'priv/static'.
-```
-
-We can now do as the task suggests and inspect the contents of `priv/static/` directory. We'll see that all files from `assets/` have been copied over to `priv/static/` and also each file now has a couple of versions. Those versions are:
-
-* the original file
-* a compressed file with gzip
-* a file containing the original file name and its digest
-* a compressed file containing the file name and its digest
-
-We can optionally determine which files should be gzipped by using the `:gzippable_exts` option in the config file:
-
-```elixir
-config :phoenix, :gzippable_exts, ~w(.js .css)
-```
-
-> Note: We can specify a different output folder where `mix phx.digest` will put processed files. The first argument is the path where the static files are located.
-
-```console
-$ mix phx.digest priv/static/ -o www/public/
-Check your digested files at 'www/public/'
-```
-
-> Note: You can use `mix phx.digest.clean` to prune stale versions of the assets. If you want to remove all produced files, run `mix phx.digest.clean --all`.
-
-## Ecto tasks
-
-Newly generated Phoenix applications now include Ecto and Postgrex as dependencies by default (which is to say, unless we use `mix phx.new` with the `--no-ecto` flag). With those dependencies come Mix tasks to take care of common Ecto operations. Let's see which tasks we get out of the box.
-
-```console
-$ mix help --search "ecto"
-mix ecto               # Prints Ecto help information
-mix ecto.create        # Creates the repository storage
-mix ecto.drop          # Drops the repository storage
-mix ecto.dump          # Dumps the repository database structure
-mix ecto.gen.migration # Generates a new migration for the repo
-mix ecto.gen.repo      # Generates a new repository
-mix ecto.load          # Loads previously dumped database structure
-mix ecto.migrate       # Runs the repository migrations
-mix ecto.migrations    # Displays the repository migration status
-mix ecto.reset         # Alias defined in mix.exs
-mix ecto.rollback      # Rolls back the repository migrations
-mix ecto.setup         # Alias defined in mix.exs
-```
-
-Note: We can run any of the tasks above with the `--no-start` flag to execute the task without starting the application.
-
-### `mix ecto.create`
-
-This task will create the database specified in our repo. By default it will look for the repo named after our application (the one generated with our app unless we opted out of Ecto), but we can pass in another repo if we want.
-
-Here's what it looks like in action.
-
-```console
-$ mix ecto.create
-The database for Hello.Repo has been created.
-```
-
-There are a few things that can go wrong with `ecto.create`. If our Postgres database doesn't have a "postgres" role (user), we'll get an error like this one.
-
-```console
-$ mix ecto.create
-** (Mix) The database for Hello.Repo couldn't be created, reason given: psql: FATAL:  role "postgres" does not exist
-```
-
-We can fix this by creating the "postgres" role in the `psql` console with the permissions needed to log in and create a database.
-
-```console
-=# CREATE ROLE postgres LOGIN CREATEDB;
-CREATE ROLE
-```
-
-If the "postgres" role does not have permission to log in to the application, we'll get this error.
-
-```console
-$ mix ecto.create
-** (Mix) The database for Hello.Repo couldn't be created, reason given: psql: FATAL:  role "postgres" is not permitted to log in
-```
-
-To fix this, we need to change the permissions on our "postgres" user to allow login.
-
-```console
-=# ALTER ROLE postgres LOGIN;
-ALTER ROLE
-```
-
-If the "postgres" role does not have permission to create a database, we'll get this error.
-
-```console
-$ mix ecto.create
-** (Mix) The database for Hello.Repo couldn't be created, reason given: ERROR:  permission denied to create database
-```
-
-To fix this, we need to change the permissions on our "postgres" user in the `psql` console  to allow database creation.
-
-```console
-=# ALTER ROLE postgres CREATEDB;
-ALTER ROLE
-```
-
-If the "postgres" role is using a password different from the default "postgres", we'll get this error.
-
-```console
-$ mix ecto.create
-** (Mix) The database for Hello.Repo couldn't be created, reason given: psql: FATAL:  password authentication failed for user "postgres"
-```
-
-To fix this, we can change the password in the environment specific configuration file. For the development environment the password used can be found at the bottom of the `config/dev.exs` file.
-
-Finally, if we happen to have another repo called `OurCustom.Repo` that we want to create the database for, we can run this.
-
-```console
-$ mix ecto.create -r OurCustom.Repo
-The database for OurCustom.Repo has been created.
-```
-
-### `mix ecto.drop`
-
-This task will drop the database specified in our repo. By default it will look for the repo named after our application (the one generated with our app unless we opted out of Ecto). It will not prompt us to check if we're sure we want to drop the database, so do exercise caution.
-
-```console
-$ mix ecto.drop
-The database for Hello.Repo has been dropped.
-```
-
-If we happen to have another repo that we want to drop the database for, we can specify it with the `-r` flag.
-
-```console
-$ mix ecto.drop -r OurCustom.Repo
-The database for OurCustom.Repo has been dropped.
-```
-
-### `mix ecto.gen.repo`
-
-Many applications require more than one data store. For each data store, we'll need a new repo, and we can generate them automatically with `ecto.gen.repo`.
-
-If we name our repo `OurCustom.Repo`, this task will create it here `lib/our_custom/repo.ex`.
-
-```console
-$ mix ecto.gen.repo -r OurCustom.Repo
-* creating lib/our_custom
-* creating lib/our_custom/repo.ex
-* updating config/config.exs
-Don't forget to add your new repo to your supervision tree
-(typically in lib/hello/application.ex):
-
-    {OurCustom.Repo, []}
-```
-
-Notice that this task has updated `config/config.exs`. If we take a look, we'll see this extra configuration block for our new repo.
-
-```elixir
-...
-config :hello, OurCustom.Repo,
-  username: "user",
-  password: "pass",
-  hostname: "localhost",
-  database: "hello_repo",
-...
-```
-
-Of course, we'll need to change the login credentials to match what our database expects. We'll also need to change the config for other environments.
-
-We certainly should follow the instructions and add our new repo to our supervision tree. In our `Hello` application, we would open up `lib/hello/application.ex`, and add our repo as a worker to the `children` list.
-
-```elixir
-...
-children = [
-  Hello.Repo,
-  # Our custom repo
-  OurCustom.Repo,
-  # Start the endpoint when the application starts
-  HelloWeb.Endpoint,
-]
-...
-```
-
-### `mix ecto.gen.migration`
-
-Migrations are a programmatic, repeatable way to affect changes to a database schema. Migrations are also just modules, and we can create them with the [`ecto.gen.migration`](`mix ecto.gen.migration`) task. Let's walk through the steps to create a migration for a new comments table.
-
-We simply need to invoke the task with a `snake_case` version of the module name that we want. Preferably, the name will describe what we want the migration to do.
-
-```console
-$ mix ecto.gen.migration add_comments_table
-* creating priv/repo/migrations
-* creating priv/repo/migrations/20150318001628_add_comments_table.exs
-```
-
-Notice that the migration's filename begins with a string representation of the date and time the file was created.
-
-Let's take a look at the file `ecto.gen.migration` has generated for us at `priv/repo/migrations/20150318001628_add_comments_table.exs`.
-
-```elixir
-defmodule Hello.Repo.Migrations.AddCommentsTable do
-  use Ecto.Migration
-
-  def change do
-  end
-end
-```
-
-Notice that there is a single function `change/0` which will handle both forward migrations and rollbacks. We'll define the schema changes that we want using Ecto's handy DSL, and Ecto will figure out what to do depending on whether we are rolling forward or rolling back. Very nice indeed.
-
-What we want to do is create a `comments` table with a `body` column, a `word_count` column, and timestamp columns for `inserted_at` and `updated_at`.
-
-```elixir
-...
-def change do
-  create table(:comments) do
-    add :body, :string
-    add :word_count, :integer
-    timestamps()
-  end
-end
-...
-```
-
-Again, we can run this task with the `-r` flag and another repo if we need to.
-
-```console
-$ mix ecto.gen.migration -r OurCustom.Repo add_users
-* creating priv/repo/migrations
-* creating priv/repo/migrations/20150318172927_add_users.exs
-```
-
-For more information on how to modify your database schema please refer to the
-[Ecto's migration DSL docs](https://hexdocs.pm/ecto_sql/Ecto.Migration.html).
-For example, to alter an existing schema see the documentation on Ecto’s
-[`alter/2`](`Ecto.Migration.alter/2`) function.
-
-That's it! We're ready to run our migration.
-
-### `mix ecto.migrate`
-
-Once we have our migration module ready, we can simply run `mix ecto.migrate` to have our changes applied to the database.
-
-```console
-$ mix ecto.migrate
-[info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 forward
-[info] create table comments
-[info] == Migrated in 0.1s
-```
-
-When we first run `ecto.migrate`, it will create a table for us called `schema_migrations`. This will keep track of all the migrations which we run by storing the timestamp portion of the migration's filename.
-
-Here's what the `schema_migrations` table looks like.
-
-```console
-hello_dev=# select * from schema_migrations;
-version        |     inserted_at
----------------+---------------------
-20150317170448 | 2015-03-17 21:07:26
-20150318001628 | 2015-03-18 01:45:00
-(2 rows)
-```
-
-When we roll back a migration, [`ecto.rollback`](#mix-ecto-rollback) will remove the record representing this migration from `schema_migrations`.
-
-By default, `ecto.migrate` will execute all pending migrations. We can exercise more control over which migrations we run by specifying some options when we run the task.
-
-We can specify the number of pending migrations we would like to run with the `-n` or `--step` options.
-
-```console
-$ mix ecto.migrate -n 2
-[info] == Running Hello.Repo.Migrations.CreatePost.change/0 forward
-[info] create table posts
-[info] == Migrated in 0.0s
-[info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 forward
-[info] create table comments
-[info] == Migrated in 0.0s
-```
-
-The `--step` option will behave the same way.
-
-```console
-$ mix ecto.migrate --step 2
-```
-
-The `--to` option will run all migrations up to and including given version.
-
-```console
-$ mix ecto.migrate --to 20150317170448
-```
-
-### `mix ecto.rollback`
-
-The [`ecto.rollback`](`mix ecto.rollback`) task will reverse the last migration we have run, undoing the schema changes. [`ecto.migrate`](#mix-ecto-migrate) and `ecto.rollback` are mirror images of each other.
-
-```console
-$ mix ecto.rollback
-[info] == Running Hello.Repo.Migrations.AddCommentsTable.change/0 backward
-[info] drop table comments
-[info] == Migrated in 0.0s
-```
-
-`ecto.rollback` will handle the same options as `ecto.migrate`, so `-n`, `--step`, `-v`, and `--to` will behave as they do for `ecto.migrate`.
-
-## Creating our own Mix task
-
-As we've seen throughout this guide, both Mix itself and the dependencies we bring in to our application provide a number of really useful tasks for free. Since neither of these could possibly anticipate all our individual application's needs, Mix allows us to create our own custom tasks. That's exactly what we are going to do now.
-
-The first thing we need to do is create a `mix/tasks/` directory inside of `lib/`. This is where any of our application specific Mix tasks will go.
-
-```console
-$ mkdir -p lib/mix/tasks/
-```
-
-Inside that directory, let's create a new file, `hello.greeting.ex`, that looks like this.
-
-```elixir
-defmodule Mix.Tasks.Hello.Greeting do
-  use Mix.Task
-
-  @shortdoc "Sends a greeting to us from Hello Phoenix"
-
-  @moduledoc """
-  This is where we would put any long form documentation and doctests.
-  """
-
-  @impl Mix.Task
-  def run(_args) do
-    Mix.shell().info("Greetings from the Hello Phoenix Application!")
-  end
-
-  # We can define other functions as needed here.
-end
-```
-
-Let's take a quick look at the moving parts involved in a working Mix task.
-
-The first thing we need to do is name our module. All tasks must be defined in the `Mix.Tasks` namespace. We'd like to invoke this as `mix hello.greeting`, so we complete the module name with
-`Hello.Greeting`.
-
-The `use Mix.Task` line brings in functionality from Mix that makes this module [behave as a Mix task](`Mix.Task`).
-
-The `@shortdoc` module attribute holds a string which will describe our task when users invoke `mix help`.
-
-`@moduledoc` serves the same function that it does in any module. It's where we can put long-form documentation and doctests, if we have any.
-
-The [`run/1`](`c:Mix.Task.run/1`) function is the critical heart of any Mix task. It's the function that does all the work when users invoke our task. In ours, all we do is send a greeting from our app, but we can implement our `run/1` function to do whatever we need it to. Note that [`Mix.shell().info/1`](`Mix.shell/0`) is the preferred way to print text back out to the user.
-
-Of course, our task is just a module, so we can define other private functions as needed to support our `run/1` function.
-
-Now that we have our task module defined, our next step is to compile the application.
-
-```console
-$ mix compile
-Compiled lib/tasks/hello.greeting.ex
-Generated hello.app
-```
-
-Now our new task should be visible to `mix help`.
-
-```console
-$ mix help --search hello
-mix hello.greeting # Sends a greeting to us from Hello Phoenix
-```
-
-Notice that `mix help` displays the text we put into the `@shortdoc` along with the name of our task.
-
-So far, so good, but does it work?
-
-```console
-$ mix hello.greeting
-Greetings from the Hello Phoenix Application!
-```
-
-Indeed it does.
-
-If you want to make your new Mix task to use your application's infrastructure, you need to make sure the application is started and configure when Mix task is being executed. This is particularly useful if you need to access your database from within the Mix task. Thankfully, Mix makes it really easy for us via the `@requirements` module attribute:
-
-```elixir
-  @requirements ["app.start"]
-
-  @impl Mix.Task
-  def run(_args) do
-    Mix.shell().info("Now I have access to Repo and other goodies!")
-    Mix.shell().info("Greetings from the Hello Phoenix Application!")
-  end
-```
diff --git a/guides/telemetry.md b/guides/telemetry.md
index 617f93287..3fb04137c 100644
--- a/guides/telemetry.md
+++ b/guides/telemetry.md
@@ -15,7 +15,7 @@ a closer look at how Telemetry works in your application.
 
 ## Overview
 
-The `[:telemetry]` library allows you to emit events at various stages of an application's lifecycle. You can then respond to these events by, among other things, aggregating them as metrics and sending the metrics data to a reporting destination.
+The `:telemetry` library allows you to emit events at various stages of an application's lifecycle. You can then respond to these events by, among other things, aggregating them as metrics and sending the metrics data to a reporting destination.
 
 Telemetry stores events by their name in an ETS table, along with the handler for each event. Then, when a given event is executed, Telemetry looks up its handler and invokes it.
 
diff --git a/installer/templates/phx_single/mix.exs b/installer/templates/phx_single/mix.exs
index 3a40f3829..1f383c28f 100644
--- a/installer/templates/phx_single/mix.exs
+++ b/installer/templates/phx_single/mix.exs
@@ -56,7 +56,7 @@ defmodule <%= @app_module %>.MixProject do
        compile: false,
        depth: 1},<% end %><%= if @mailer do %>
       {:swoosh, "~> 1.16"},
-      {:req, "~> 0.5.4"},<% end %>
+      {:req, "~> 0.5"},<% end %>
       {:telemetry_metrics, "~> 1.0"},
       {:telemetry_poller, "~> 1.0"},<%= if @gettext do %>
       {:gettext, "~> 0.26"},<% end %>
diff --git a/installer/templates/phx_umbrella/apps/app_name/mix.exs b/installer/templates/phx_umbrella/apps/app_name/mix.exs
index 5ba7b9323..614c78153 100644
--- a/installer/templates/phx_umbrella/apps/app_name/mix.exs
+++ b/installer/templates/phx_umbrella/apps/app_name/mix.exs
@@ -42,7 +42,7 @@ defmodule <%= @app_module %>.MixProject do
       {:<%= @adapter_app %>, ">= 0.0.0"},
       {:jason, "~> 1.2"}<% end %><%= if @mailer do %>,
       {:swoosh, "~> 1.16"},
-      {:req, "~> 0.5.4"}<% end %>
+      {:req, "~> 0.5"}<% end %>
     ]
   end
 
diff --git a/lib/phoenix/controller.ex b/lib/phoenix/controller.ex
index a4f720a83..d4f010d03 100644
--- a/lib/phoenix/controller.ex
+++ b/lib/phoenix/controller.ex
@@ -8,12 +8,12 @@ defmodule Phoenix.Controller do
   @unsent [:unset, :set, :set_chunked, :set_file]
 
   # View/Layout deprecation plan
-  # 1. Deprecate :namespace option in favor of :layouts on use
+  # 1. DONE! Deprecate :namespace option in favor of :layouts on use
   # 2. Deprecate setting a non-format view/layout on put_*
   # 3. Deprecate rendering a view/layout from :_
 
   @type view :: atom()
-  @type layout :: {module(), layout_name :: atom()} | atom() | false
+  @type layout :: {module(), layout_name :: atom()} | false
 
   @moduledoc """
   Controllers are used to group common functionality in the same
@@ -102,17 +102,7 @@ defmodule Phoenix.Controller do
       |> put_view(html: MyAppWeb.UserHTML, json: MyAppWeb.UserJSON)
       |> put_layout(html: MyAppWeb.Layouts)
 
-  ### Backwards compatibility
-
-  In previous Phoenix versions, a controller you always render
-  `MyApp.UserView`. This behaviour can be explicitly retained by
-  passing a suffix to the formats options:
-
-      use Phoenix.Controller,
-        formats: [html: "View", json: "View"],
-        layouts: [html: MyAppWeb.Layouts]
-
-  ### Options
+  ## Options
 
   When used, the controller supports the following options to customize
   template rendering:
@@ -121,23 +111,23 @@ defmodule Phoenix.Controller do
       by default. For example, specifying `formats: [:html, :json]`
       for a controller named `MyAppWeb.UserController` will
       invoke `MyAppWeb.UserHTML` and `MyAppWeb.UserJSON` when
-      respectively rendering each format. If `:formats` is not
-      set, the default view is set to `MyAppWeb.UserView`
+      respectively rendering each format.
 
     * `:layouts` - which layouts to render for each format,
       for example: `[html: DemoWeb.Layouts]`. The value for each
       format can be a tuple with the layout module and the layout name,
-      or just a module. When the name is omitted, the default layout
+      or just a module. When just the module is given, the default layout
       name `:app` is used.
 
-  Deprecated options:
-
-    * `:namespace` - sets the namespace for the layout. Use
-      `:layouts` instead
+  The `:formats` option is required. You may set it to an empty list
+  if you don't expect to render any format upfront. If `:formats` is not
+  set, the default view is set to `MyAppWeb.UserView` for backwards
+  compatibility. This behaviour can be explicitly retained by passing a
+  suffix to the `:formats` option:
 
-    * `:put_default_views` - controls whether the default view
-      and layout should be set or not. Set `formats: []` and
-      `layouts: []` instead
+      use Phoenix.Controller,
+        formats: [html: "View", json: "View"],
+        layouts: [html: MyAppWeb.Layouts]
 
   ## Plug pipeline
 
@@ -248,9 +238,9 @@ defmodule Phoenix.Controller do
 
       use Phoenix.Controller.Pipeline
 
-      if Keyword.get(opts, :put_default_views, true) do
-        plug :put_new_layout, Phoenix.Controller.__layout__(__MODULE__, opts)
-        plug :put_new_view, Phoenix.Controller.__view__(__MODULE__, opts)
+      with {layout, view} <- Phoenix.Controller.__plugs__(__MODULE__, opts) do
+        plug :put_new_layout, layout
+        plug :put_new_view, view
       end
     end
   end
@@ -532,10 +522,6 @@ defmodule Phoenix.Controller do
 
   ## Examples
 
-      # Use single view module
-      iex> put_view(conn, AppView)
-
-      # Use multiple view module for content negotiation
       iex> put_view(conn, html: AppHTML, json: AppJSON)
 
   """
@@ -546,13 +532,13 @@ defmodule Phoenix.Controller do
 
   def put_view(%Plug.Conn{} = conn, module) do
     raise(AlreadySentError, """
-      the response was already sent.
+    the response was already sent.
 
-          Status code: #{conn.status}
-          Request path: #{conn.request_path}
-          Method: #{conn.method}
-          View module: #{inspect(module)}
-      """)
+        Status code: #{conn.status}
+        Request path: #{conn.request_path}
+        Method: #{conn.method}
+        View module: #{inspect(module)}
+    """)
   end
 
   defp put_private_view(conn, priv_key, kind, formats) when is_list(formats) do
@@ -560,14 +546,14 @@ defmodule Phoenix.Controller do
     put_private_formats(conn, priv_key, kind, formats)
   end
 
-  # TODO: Deprecate this whole branch
+  # TODO: Deprecate this whole branch on Phoenix v1.9
   defp put_private_view(conn, priv_key, kind, value) do
     put_private_formats(conn, priv_key, kind, %{_: value})
   end
 
   defp put_private_formats(conn, priv_key, kind, formats) when kind in [:new, :replace] do
     update_in(conn.private, fn private ->
-      existing = private[priv_key] || %{}
+      existing = Map.get(private, priv_key, %{})
 
       new_formats =
         case kind do
@@ -584,7 +570,7 @@ defmodule Phoenix.Controller do
 
   Raises `Plug.Conn.AlreadySentError` if `conn` is already sent.
   """
-  # TODO: Remove | layout from the spec once we deprecate put_new_view on controllers
+  # TODO: Remove | view from the spec once we deprecate put_new_view on controllers on v1.9
   @spec put_new_view(Plug.Conn.t(), [{format :: atom, view}] | view) :: Plug.Conn.t()
   def put_new_view(%Plug.Conn{state: state} = conn, formats) when state in @unsent do
     put_private_view(conn, :phoenix_view, :new, formats)
@@ -592,13 +578,13 @@ defmodule Phoenix.Controller do
 
   def put_new_view(%Plug.Conn{} = conn, module) do
     raise(AlreadySentError, """
-      the response was already sent.
+    the response was already sent.
 
-          Status code: #{conn.status}
-          Request path: #{conn.request_path}
-          Method: #{conn.method}
-          View module: #{inspect(module)}
-      """)
+        Status code: #{conn.status}
+        Request path: #{conn.request_path}
+        Method: #{conn.method}
+        View module: #{inspect(module)}
+    """)
   end
 
   @doc """
@@ -610,8 +596,7 @@ defmodule Phoenix.Controller do
   def view_module(conn, format \\ nil) do
     format = format || get_safe_format(conn)
 
-    # TODO: Deprecate if we fall on the first branch
-    # But we should only deprecate this after non-format is deprecated on put_*
+    # TODO: Remove the first branch once code paths are deprecated and then removed
     case conn.private[:phoenix_view] do
       %{_: value} when value != nil ->
         value
@@ -635,10 +620,6 @@ defmodule Phoenix.Controller do
     * `{module, layout}` with the `module` the layout is defined and
       the name of the `layout` as an atom
 
-    * `layout` when the name of the layout. This requires a layout for
-      the given format in the shape of `{module, layout}` to be previously
-      given
-
     * `false` which disables the layout
 
   If `false` is given without a format, all layouts are disabled.
@@ -652,7 +633,7 @@ defmodule Phoenix.Controller do
       iex> layout(conn)
       {AppView, :application}
 
-      iex> conn = put_layout(conn, html: :print)
+      iex> conn = put_layout(conn, html: {AppView, :print})
       iex> layout(conn)
       {AppView, :print}
 
@@ -685,6 +666,10 @@ defmodule Phoenix.Controller do
 
           case conn.private[private_key] do
             %{^format => {mod, _}} ->
+              IO.warn("""
+              specifying a layout without module is deprecated, use #{format}: #{inspect({mod, layout})} instead\
+              """)
+
               {format, {mod, layout}}
 
             %{} ->
@@ -715,14 +700,18 @@ defmodule Phoenix.Controller do
       false ->
         put_private_formats(conn, private_key, kind, %{_: false})
 
-      # TODO: Deprecate this branch
+      # TODO: Deprecate this branch on Phoenix v1.9
       {mod, layout} when is_atom(mod) ->
         put_private_formats(conn, private_key, kind, %{_: {mod, layout}})
 
-      # TODO: Deprecate this branch
       layout when is_binary(layout) or is_atom(layout) ->
         case Map.get(conn.private, private_key, %{_: false}) do
           %{_: {mod, _}} ->
+            IO.warn("""
+            specifying put_layout(conn, template) or put_new_layout(conn, template) is deprecated, \
+            specify the layout with the format instead: put_layout(conn, html: #{inspect({mod, layout})})
+            """)
+
             put_private_formats(conn, private_key, kind, %{_: {mod, layout}})
 
           %{_: false} ->
@@ -756,6 +745,7 @@ defmodule Phoenix.Controller do
           Layout: #{inspect(layout)}
       """)
     end
+
     put_private_layout(conn, :phoenix_layout, :new, layout)
   end
 
@@ -807,20 +797,7 @@ defmodule Phoenix.Controller do
     end
   end
 
-  @doc """
-  Sets which formats have a layout when rendering.
-
-  ## Examples
-
-      iex> layout_formats(conn)
-      ["html"]
-
-      iex> put_layout_formats(conn, ["html", "mobile"])
-      iex> layout_formats(conn)
-      ["html", "mobile"]
-
-  Raises `Plug.Conn.AlreadySentError` if `conn` is already sent.
-  """
+  @doc false
   @deprecated "put_layout_formats/2 is deprecated, pass a keyword list to put_layout/put_root_layout instead"
   @spec put_layout_formats(Plug.Conn.t(), [String.t()]) :: Plug.Conn.t()
   def put_layout_formats(%Plug.Conn{state: state} = conn, formats)
@@ -830,19 +807,17 @@ defmodule Phoenix.Controller do
 
   def put_layout_formats(%Plug.Conn{} = conn, _formats) do
     raise(AlreadySentError, """
-      the response was already sent.
+    the response was already sent.
 
-          Status code: #{conn.status}
-          Request path: #{conn.request_path}
-          Method: #{conn.method}
-      """)
+        Status code: #{conn.status}
+        Request path: #{conn.request_path}
+        Method: #{conn.method}
+    """)
   end
 
-  @doc """
-  Retrieves current layout formats.
-  """
-  @spec layout_formats(Plug.Conn.t()) :: [String.t()]
+  @doc false
   @deprecated "layout_formats/1 is deprecated, pass a keyword list to put_layout/put_root_layout instead"
+  @spec layout_formats(Plug.Conn.t()) :: [String.t()]
   def layout_formats(conn) do
     Map.get(conn.private, :phoenix_layout_formats, ~w(html))
   end
@@ -870,6 +845,7 @@ defmodule Phoenix.Controller do
   defp get_private_layout(conn, priv_key, format) do
     format = format || get_safe_format(conn)
 
+    # TODO: Remove _ handling once layouts(false) is set to remove all formats
     case conn.private[priv_key] do
       %{_: value} -> if format in [nil | layout_formats(conn)], do: value, else: false
       %{^format => value} -> value
@@ -917,73 +893,45 @@ defmodule Phoenix.Controller do
 
   ## Examples
 
-      defmodule MyAppWeb.UserController do
-        use Phoenix.Controller
+  To render a template, you must configure your controller with the formats
+  to render. You can do so on `use`, which will infer the modules based on
+  the controller name:
 
-        def show(conn, _params) do
-          render(conn, "show.html", message: "Hello")
-        end
+      defmodule MyAppWeb.UserController do
+        # Will use MyAppWeb.UserHTML and MyAppWeb.UserJSON
+        use Phoenix.Controller, formats: [:html, :json]
       end
 
-  The example above renders a template "show.html" from the `MyAppWeb.UserView`
-  and sets the response content type to "text/html".
-
-  In many cases, you may want the template format to be set dynamically based
-  on the request. To do so, you can pass the template name as an atom (without
-  the extension):
+  With the formats set, you can render in two ways, either passing a string
+  with the template name and explicit format:
 
       def show(conn, _params) do
-        render(conn, :show, message: "Hello")
+        render(conn, "show.html", message: "Hello")
       end
 
-  In order for the example above to work, we need to do content negotiation with
-  the accepts plug before rendering. You can do so by adding the following to your
-  pipeline (in the router):
-
-      plug :accepts, ["html"]
-
-  ## Views
+  The example above renders a template "show.html" from the `MyAppWeb.UserHTML`
+  and sets the response content type to "text/html".
 
-  By default, Controllers render templates in a view with a similar name to the
-  controller. For example, `MyAppWeb.UserController` will render templates inside
-  the `MyAppWeb.UserView`. This information can be changed any time by using the
-  `put_view/2` function:
+  Or, if you want the template format to be set dynamically based on the request,
+  you can pass an atom instead (without the extension):
 
       def show(conn, _params) do
-        conn
-        |> put_view(MyAppWeb.SpecialView)
-        |> render(:show, message: "Hello")
-      end
-
-  `put_view/2` can also be used as a plug:
-
-      defmodule MyAppWeb.UserController do
-        use Phoenix.Controller
-
-        plug :put_view, html: MyAppWeb.SpecialView
-
-        def show(conn, _params) do
-          render(conn, :show, message: "Hello")
-        end
+        render(conn, :show.html, message: "Hello")
       end
 
-  ## Layouts
-
-  Templates are often rendered inside layouts. By default, Phoenix
-  will render layouts for html requests. For example:
+  If the formats are not known at compile-time, you can call `put_view/2`
+  at runtime:
 
       defmodule MyAppWeb.UserController do
         use Phoenix.Controller
 
         def show(conn, _params) do
+          conn
+          |> put_view(html: MyAppWeb.UserHTML)
           render(conn, "show.html", message: "Hello")
         end
       end
 
-  will render the  "show.html" template inside an "app.html"
-  template specified in `MyAppWeb.LayoutView`. `put_layout/2` can be used
-  to change the layout, similar to how `put_view/2` can be used to change
-  the view.
   """
   @spec render(Plug.Conn.t(), binary | atom, Keyword.t() | map) :: Plug.Conn.t()
   def render(conn, template, assigns)
@@ -1005,7 +953,7 @@ defmodule Phoenix.Controller do
   def render(conn, view, template)
       when is_atom(view) and (is_binary(template) or is_atom(template)) do
     IO.warn(
-      "#{__MODULE__}.render/3 with a view is deprecated, see the documentation for render/3 for an alternative"
+      "Phoenix.Controller.render/3 with a view is deprecated, see the documentation for render/3 for an alternative"
     )
 
     render(conn, view, template, [])
@@ -1083,6 +1031,7 @@ defmodule Phoenix.Controller do
   defp assigns_layout(_conn, %{layout: layout}, _format), do: layout
 
   defp assigns_layout(conn, _assigns, format) do
+    # TODO: Remove _ handling once layouts(false) is set to remove all formats
     case conn.private[:phoenix_layout] do
       %{^format => bad_value, _: good_value} when good_value != false ->
         IO.warn("""
@@ -1930,60 +1879,100 @@ defmodule Phoenix.Controller do
   end
 
   @doc false
-  def __view__(controller_module, opts) do
-    view_base = Phoenix.Naming.unsuffix(controller_module, "Controller")
-
-    case Keyword.fetch(opts, :formats) do
-      {:ok, formats} when is_list(formats) ->
-        for format <- formats do
-          case format do
-            format when is_atom(format) ->
-              {format, :"#{view_base}#{String.upcase(to_string(format))}"}
-
-            {format, suffix} ->
-              {format, :"#{view_base}#{suffix}"}
-          end
+  def __plugs__(controller_module, opts) do
+    if Keyword.get(opts, :put_default_views, true) do
+      base = Phoenix.Naming.unsuffix(controller_module, "Controller")
+
+      view =
+        case Keyword.fetch(opts, :formats) do
+          {:ok, formats} when is_list(formats) ->
+            Enum.map(formats, fn
+              format when is_atom(format) ->
+                {format, :"#{base}#{String.upcase(to_string(format))}"}
+
+              {format, suffix} ->
+                {format, :"#{base}#{suffix}"}
+            end)
+
+          :error ->
+            IO.warn(
+              """
+              use #{inspect(controller_module)} must receive the :formats option with \
+              the formats you intend to render. To keep compatibility within your app, \
+              you can list it as:
+
+                  formats: [html: "View", json: "View", ...]
+
+              Listing all formats your application renders.
+              """,
+              []
+            )
+
+            :"#{base}View"
         end
 
-      :error ->
-        :"#{view_base}View"
-    end
-  end
-
-  @doc false
-  def __layout__(controller_module, opts) do
-    case Keyword.fetch(opts, :layouts) do
-      {:ok, formats} when is_list(formats) ->
-        Enum.map(formats, fn
-          {format, mod} when is_atom(mod) ->
-            {format, {mod, :app}}
-
-          {format, {mod, template}} when is_atom(mod) and is_atom(template) ->
-            {format, {mod, template}}
-
-          other ->
-            raise ArgumentError, """
-            expected :layouts to be a list of format module pairs of the form: [html: DemoWeb.Layouts] or [html: {DemoWeb.Layouts, :app}]
-
-            Got: #{inspect(other)}
-            """
-        end)
+      layouts =
+        case Keyword.fetch(opts, :layouts) do
+          {:ok, formats} when is_list(formats) ->
+            Enum.map(formats, fn
+              {format, mod} when is_atom(mod) ->
+                {format, {mod, :app}}
+
+              {format, {mod, template}} when is_atom(mod) and is_atom(template) ->
+                {format, {mod, template}}
+
+              other ->
+                raise ArgumentError, """
+                expected :layouts to be a list of format module pairs of the form: [html: DemoWeb.Layouts] or [html: {DemoWeb.Layouts, :app}]
+
+                Got: #{inspect(other)}
+                """
+            end)
+
+          :error ->
+            # TODO: This whole code branch is deprecated and should return [] in the future
+            cond do
+              namespace = Keyword.get(opts, :namespace) ->
+                layout = Module.concat(namespace, "LayoutView")
+
+                IO.warn(
+                  """
+                  the :namespace option given to #{inspect(controller_module)} is deprecated.
+                  Set layouts: [html: #{inspect(layout)}] instead\
+                  """,
+                  []
+                )
+
+                {layout, :app}
+
+              Keyword.has_key?(opts, :formats) ->
+                []
+
+              true ->
+                layout =
+                  controller_module
+                  |> Atom.to_string()
+                  |> String.split(".")
+                  |> Enum.drop(-1)
+                  |> Enum.take(2)
+                  |> Kernel.++(["LayoutView"])
+                  |> Module.concat()
+
+                {layout, :app}
+            end
+        end
 
-      :error ->
-        # TODO: Deprecate :namespace option in favor of :layouts
-        namespace =
-          if given = Keyword.get(opts, :namespace) do
-            given
-          else
-            controller_module
-            |> Atom.to_string()
-            |> String.split(".")
-            |> Enum.drop(-1)
-            |> Enum.take(2)
-            |> Module.concat()
-          end
+      {layouts, view}
+    else
+      IO.warn(
+        """
+        the :put_default_views option given to #{inspect(controller_module)} is deprecated.
+        Set formats: [] instead\
+        """,
+        []
+      )
 
-        {Module.concat(namespace, "LayoutView"), :app}
+      false
     end
   end
 end
diff --git a/lib/phoenix/endpoint/render_errors.ex b/lib/phoenix/endpoint/render_errors.ex
index 0e0d494bf..2ee87cdb3 100644
--- a/lib/phoenix/endpoint/render_errors.ex
+++ b/lib/phoenix/endpoint/render_errors.ex
@@ -118,10 +118,9 @@ defmodule Phoenix.Endpoint.RenderErrors do
       |> Controller.put_layout(opts[:layout] || false)
 
     format = Controller.get_format(conn)
-
     reason = Exception.normalize(kind, reason, stack)
     template = "#{conn.status}.#{format}"
-    assigns = %{kind: kind, reason: reason, stack: stack, status: conn.status}
+    assigns = %{kind: kind, reason: reason, stack: stack, status: conn.status, __changed__: nil}
 
     Controller.render(conn, template, assigns)
   end
diff --git a/mix.exs b/mix.exs
index 1200d8090..cb165c669 100644
--- a/mix.exs
+++ b/mix.exs
@@ -157,15 +157,19 @@ defmodule Phoenix.MixProject do
       "guides/controllers.md",
       "guides/components.md",
       "guides/ecto.md",
-      "guides/contexts.md",
       "guides/json_and_apis.md",
-      "guides/mix_tasks.md",
-      "guides/telemetry.md",
       "guides/asset_management.md",
+      "guides/telemetry.md",
       "guides/authn_authz/authn_authz.md",
       "guides/authn_authz/mix_phx_gen_auth.md",
       "guides/authn_authz/scopes.md",
       "guides/authn_authz/api_authentication.md",
+      "guides/data_modelling/contexts.md",
+      "guides/data_modelling/your_first_context.md",
+      "guides/data_modelling/in_context_relationships.md",
+      "guides/data_modelling/cross_context_boundaries.md",
+      "guides/data_modelling/more_examples.md",
+      "guides/data_modelling/faq.md",
       "guides/real_time/channels.md",
       "guides/real_time/presence.md",
       "guides/testing/testing.md",
@@ -179,6 +183,7 @@ defmodule Phoenix.MixProject do
       "guides/deployment/heroku.md",
       "guides/howto/custom_error_pages.md",
       "guides/howto/file_uploads.md",
+      "guides/howto/swapping_databases.md",
       "guides/howto/using_ssl.md",
       "guides/howto/writing_a_channels_client.md",
       "guides/cheatsheets/router.cheatmd",
@@ -190,6 +195,7 @@ defmodule Phoenix.MixProject do
     [
       Introduction: ~r/guides\/introduction\/.?/,
       Guides: ~r/guides\/[^\/]+\.md/,
+      "Data modelling": ~r/guides\/data_modelling\/.?/,
       "Authn and Authz": ~r/guides\/authn_authz\/.?/,
       "Real-time": ~r/guides\/real_time\/.?/,
       Testing: ~r/guides\/testing\/.?/,


Test patch:
diff --git a/guides/testing/testing_contexts.md b/guides/testing/testing_contexts.md
index c2b330c21..71345b93e 100644
--- a/guides/testing/testing_contexts.md
+++ b/guides/testing/testing_contexts.md
@@ -4,7 +4,7 @@
 
 > **Requirement**: This guide expects that you have gone through the [Introduction to Testing guide](testing.html).
 
-> **Requirement**: This guide expects that you have gone through the [Contexts guide](contexts.html).
+> **Requirement**: This guide expects that you have gone through the [Contexts guides](contexts.html).
 
 At the end of the Introduction to Testing guide, we generated an HTML resource for posts using the following command:
 
diff --git a/installer/test/phx_new_test.exs b/installer/test/phx_new_test.exs
index 470bfcead..8cb432eba 100644
--- a/installer/test/phx_new_test.exs
+++ b/installer/test/phx_new_test.exs
@@ -256,7 +256,7 @@ defmodule Mix.Tasks.Phx.NewTest do
       # Mailer
       assert_file("phx_blog/mix.exs", fn file ->
         assert file =~ "{:swoosh, \"~> 1.16\"}"
-        assert file =~ "{:req, \"~> 0.5.4\"}"
+        assert file =~ "{:req, \"~> 0.5\"}"
       end)
 
       assert_file("phx_blog/lib/phx_blog/mailer.ex", fn file ->
diff --git a/installer/test/phx_new_umbrella_test.exs b/installer/test/phx_new_umbrella_test.exs
index 5229903dc..1c5542e40 100644
--- a/installer/test/phx_new_umbrella_test.exs
+++ b/installer/test/phx_new_umbrella_test.exs
@@ -275,7 +275,7 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
       # Mailer
       assert_file(app_path(@app, "mix.exs"), fn file ->
         assert file =~ "{:swoosh, \"~> 1.16\"}"
-        assert file =~ "{:req, \"~> 0.5.4\"}"
+        assert file =~ "{:req, \"~> 0.5\"}"
       end)
 
       assert_file(app_path(@app, "lib/#{@app}/mailer.ex"), fn file ->
diff --git a/integration_test/mix.exs b/integration_test/mix.exs
index d17d3bd13..68b32c362 100644
--- a/integration_test/mix.exs
+++ b/integration_test/mix.exs
@@ -63,7 +63,7 @@ defmodule Phoenix.Integration.MixProject do
        app: false,
        compile: false,
        depth: 1},
-      {:req, "~> 0.5.4"}
+      {:req, "~> 0.5"}
     ]
   end
 end
diff --git a/test/phoenix/controller/controller_test.exs b/test/phoenix/controller/controller_test.exs
index 6411565d6..fe43dc7ff 100644
--- a/test/phoenix/controller/controller_test.exs
+++ b/test/phoenix/controller/controller_test.exs
@@ -2,6 +2,7 @@ defmodule Phoenix.Controller.ControllerTest do
   use ExUnit.Case, async: true
   use RouterHelper
 
+  import ExUnit.CaptureIO
   import Phoenix.Controller
   alias Plug.Conn
 
@@ -53,11 +54,17 @@ defmodule Phoenix.Controller.ControllerTest do
     conn = put_layout(conn, {AppView, "app.html"})
     assert layout(conn) == {AppView, "app.html"}
 
-    conn = put_layout(conn, "print.html")
-    assert layout(conn) == {AppView, "print.html"}
+    assert capture_io(:stderr, fn ->
+             conn = put_layout(conn, "print.html")
+             assert layout(conn) == {AppView, "print.html"}
+           end) =~
+             "specifying put_layout(conn, template) or put_new_layout(conn, template) is deprecated"
 
-    conn = put_layout(conn, :print)
-    assert layout(conn) == {AppView, :print}
+    assert capture_io(:stderr, fn ->
+             conn = put_layout(conn, :print)
+             assert layout(conn) == {AppView, :print}
+           end) =~
+             "specifying put_layout(conn, template) or put_new_layout(conn, template) is deprecated"
 
     conn = put_layout(conn, false)
     assert layout(conn) == false
@@ -78,8 +85,11 @@ defmodule Phoenix.Controller.ControllerTest do
     conn = put_layout(conn, html: {AppView, :app})
     assert layout(conn) == {AppView, :app}
 
-    conn = put_layout(conn, html: :print)
-    assert layout(conn) == {AppView, :print}
+    assert capture_io(:stderr, fn ->
+             conn = put_layout(conn, html: :print)
+             assert layout(conn) == {AppView, :print}
+           end) =~
+             "specifying a layout without module is deprecated, use html: {AppView, :print} instead"
 
     conn = put_layout(conn, html: {AppView, :app}, print: {AppView, :print})
 
@@ -110,11 +120,17 @@ defmodule Phoenix.Controller.ControllerTest do
     conn = put_root_layout(conn, {AppView, "root.html"})
     assert root_layout(conn) == {AppView, "root.html"}
 
-    conn = put_root_layout(conn, "bare.html")
-    assert root_layout(conn) == {AppView, "bare.html"}
+    assert capture_io(:stderr, fn ->
+             conn = put_root_layout(conn, "bare.html")
+             assert root_layout(conn) == {AppView, "bare.html"}
+           end) =~
+             "specifying put_layout(conn, template) or put_new_layout(conn, template) is deprecated"
 
-    conn = put_root_layout(conn, :print)
-    assert root_layout(conn) == {AppView, :print}
+    assert capture_io(:stderr, fn ->
+             conn = put_root_layout(conn, :print)
+             assert root_layout(conn) == {AppView, :print}
+           end) =~
+             "specifying put_layout(conn, template) or put_new_layout(conn, template) is deprecated"
 
     conn = put_root_layout(conn, false)
     assert root_layout(conn) == false
@@ -135,8 +151,11 @@ defmodule Phoenix.Controller.ControllerTest do
     conn = put_root_layout(conn, html: {AppView, :app})
     assert root_layout(conn) == {AppView, :app}
 
-    conn = put_root_layout(conn, html: :print)
-    assert root_layout(conn) == {AppView, :print}
+    assert capture_io(:stderr, fn ->
+             conn = put_root_layout(conn, html: :print)
+             assert root_layout(conn) == {AppView, :print}
+           end) =~
+             "specifying a layout without module is deprecated, use html: {AppView, :print} instead"
 
     conn = put_root_layout(conn, html: {AppView, :app}, print: {AppView, :print})
 
@@ -854,39 +873,60 @@ defmodule Phoenix.Controller.ControllerTest do
     end
   end
 
-  test "__view__ returns the view module based on controller module" do
-    assert Phoenix.Controller.__view__(MyApp.UserController, []) == MyApp.UserView
-    assert Phoenix.Controller.__view__(MyApp.Admin.UserController, []) == MyApp.Admin.UserView
-
-    assert Phoenix.Controller.__view__(MyApp.Admin.UserController, formats: [:html, :json]) ==
-             [html: MyApp.Admin.UserHTML, json: MyApp.Admin.UserJSON]
+  describe "__using__" do
+    defp new_layout(module, opts), do: Phoenix.Controller.__plugs__(module, opts) |> elem(0)
+    defp new_view(module, opts), do: Phoenix.Controller.__plugs__(module, opts) |> elem(1)
 
-    assert Phoenix.Controller.__view__(MyApp.Admin.UserController, formats: [:html, json: "View"]) ==
-             [html: MyApp.Admin.UserHTML, json: MyApp.Admin.UserView]
-  end
+    test "deprecated when lacking formats" do
+      assert capture_io(:stderr, fn ->
+               assert Phoenix.Controller.__plugs__(UserController, []) ==
+                        {{LayoutView, :app}, UserView}
+             end) =~
+               "use UserController must receive the :formats option with the formats you intend to render"
 
-  test "__layout__ returns the layout module based on controller module" do
-    assert Phoenix.Controller.__layout__(UserController, []) ==
-             {LayoutView, :app}
+      assert capture_io(:stderr, fn ->
+               assert Phoenix.Controller.__plugs__(MyApp.UserController, []) ==
+                        {{MyApp.LayoutView, :app}, MyApp.UserView}
+             end) =~
+               "use MyApp.UserController must receive the :formats option with the formats you intend to render"
 
-    assert Phoenix.Controller.__layout__(MyApp.UserController, []) ==
-             {MyApp.LayoutView, :app}
+      assert capture_io(:stderr, fn ->
+               assert Phoenix.Controller.__plugs__(MyApp.Admin.UserController, []) ==
+                        {{MyApp.LayoutView, :app}, MyApp.Admin.UserView}
+             end) =~
+               "use MyApp.Admin.UserController must receive the :formats option with the formats you intend to render"
+    end
 
-    assert Phoenix.Controller.__layout__(MyApp.Admin.UserController, []) ==
-             {MyApp.LayoutView, :app}
+    test "deprecated namespace" do
+      assert capture_io(:stderr, fn ->
+               assert new_layout(MyApp.Admin.UserController, namespace: MyApp.Admin, formats: []) ==
+                        {MyApp.Admin.LayoutView, :app}
+             end) =~ "the :namespace option given to MyApp.Admin.UserController is deprecated"
+    end
 
-    assert Phoenix.Controller.__layout__(MyApp.Admin.UserController, namespace: MyApp.Admin) ==
-             {MyApp.Admin.LayoutView, :app}
+    test "returns view modules based on format" do
+      assert new_view(MyApp.Admin.UserController, formats: [:html, :json]) ==
+               [html: MyApp.Admin.UserHTML, json: MyApp.Admin.UserJSON]
 
-    opts = [layouts: [html: MyApp.LayoutHTML]]
+      assert new_view(MyApp.Admin.UserController, formats: [:html, json: "View"]) ==
+               [html: MyApp.Admin.UserHTML, json: MyApp.Admin.UserView]
+    end
 
-    assert Phoenix.Controller.__layout__(MyApp.Admin.UserController, opts) ==
-             [html: {MyApp.LayoutHTML, :app}]
+    test "returns the layout module based on controller module" do
+      assert new_layout(MyApp.Admin.UserController, formats: []) == []
 
-    opts = [layouts: [html: {MyApp.LayoutHTML, :application}]]
+      assert new_layout(MyApp.Admin.UserController,
+               layouts: [html: MyApp.LayoutHTML],
+               formats: []
+             ) ==
+               [html: {MyApp.LayoutHTML, :app}]
 
-    assert Phoenix.Controller.__layout__(MyApp.Admin.UserController, opts) ==
-             [html: {MyApp.LayoutHTML, :application}]
+      assert new_layout(MyApp.Admin.UserController,
+               layouts: [html: {MyApp.LayoutHTML, :application}],
+               formats: []
+             ) ==
+               [html: {MyApp.LayoutHTML, :application}]
+    end
   end
 
   defp sent_conn do
diff --git a/test/phoenix/controller/pipeline_test.exs b/test/phoenix/controller/pipeline_test.exs
index dda1fec29..151027297 100644
--- a/test/phoenix/controller/pipeline_test.exs
+++ b/test/phoenix/controller/pipeline_test.exs
@@ -5,7 +5,7 @@ defmodule Phoenix.Controller.PipelineTest do
   import Phoenix.Controller
 
   defmodule MyController do
-    use Phoenix.Controller
+    use Phoenix.Controller, formats: []
 
     @secret_actions [:secret]
 
@@ -45,18 +45,18 @@ defmodule Phoenix.Controller.PipelineTest do
     defp do_halt(conn, _), do: halt(conn)
 
     defp prepend(conn, val) do
-      update_in conn.private.stack, &[val|&1]
+      update_in(conn.private.stack, &[val | &1])
     end
   end
 
   defmodule NoViewsController do
-    use Phoenix.Controller, put_default_views: false
+    use Phoenix.Controller, formats: []
 
     def show(conn, _), do: conn
   end
 
   defmodule FallbackFunctionController do
-    use Phoenix.Controller
+    use Phoenix.Controller, formats: []
 
     action_fallback :function_plug
 
@@ -69,21 +69,21 @@ defmodule Phoenix.Controller.PipelineTest do
     defp function_plug(%Plug.Conn{} = conn, :not_a_conn) do
       Plug.Conn.send_resp(conn, 200, "function fallback")
     end
+
     defp function_plug(%Plug.Conn{}, :bad_fallback), do: :bad_function_fallback
 
     defp put_assign(conn, _), do: assign(conn, :value_before_action, :a_value)
   end
 
   defmodule ActionController do
-    use Phoenix.Controller
+    use Phoenix.Controller, formats: []
 
     action_fallback Phoenix.Controller.PipelineTest
 
     plug :put_assign
 
     def action(conn, _) do
-      apply(__MODULE__, conn.private.phoenix_action, [conn, conn.body_params,
-                                                      conn.query_params])
+      apply(__MODULE__, conn.private.phoenix_action, [conn, conn.body_params, conn.query_params])
     end
 
     def show(conn, _, _), do: text(conn, "show")
@@ -113,36 +113,45 @@ defmodule Phoenix.Controller.PipelineTest do
   end
 
   test "invokes the plug stack" do
-    conn = stack_conn()
-           |> MyController.call(:show)
+    conn =
+      stack_conn()
+      |> MyController.call(:show)
+
     assert conn.private.stack == [:action, :before2, :before1]
   end
 
   test "invokes the plug stack with guards" do
-    conn = stack_conn()
-           |> MyController.call(:create)
+    conn =
+      stack_conn()
+      |> MyController.call(:create)
+
     assert conn.private.stack == [:action, :before2, :before1]
   end
 
   test "halts prevent action from running" do
-    conn = stack_conn()
-           |> MyController.call(:secret)
+    conn =
+      stack_conn()
+      |> MyController.call(:secret)
+
     assert conn.private.stack == [:before2, :before1]
   end
 
   test "does not override previous views/layouts" do
-    conn = stack_conn()
-           |> put_view(Hello)
-           |> put_layout(false)
-           |> MyController.call(:create)
+    conn =
+      stack_conn()
+      |> put_view(Hello)
+      |> put_layout(false)
+      |> MyController.call(:create)
+
     assert view_module(conn) == Hello
     assert layout(conn) == false
   end
 
   test "does not set default view/layout" do
-    conn = stack_conn()
-           |> NoViewsController.call(:show)
-           |> put_new_view(Hello)
+    conn =
+      stack_conn()
+      |> NoViewsController.call(:show)
+      |> put_new_view(Hello)
 
     assert view_module(conn) == Hello
     assert layout(conn) == false
@@ -215,7 +224,7 @@ defmodule Phoenix.Controller.PipelineTest do
     test "raises when calling more than once", config do
       assert_raise RuntimeError, ~r/can only be called a single time/, fn ->
         defmodule config.test do
-          use Phoenix.Controller
+          use Phoenix.Controller, formats: []
           action_fallback Ok
           action_fallback Boom
         end
diff --git a/test/phoenix/controller/render_test.exs b/test/phoenix/controller/render_test.exs
index 92506e2a1..fbe67d77d 100644
--- a/test/phoenix/controller/render_test.exs
+++ b/test/phoenix/controller/render_test.exs
@@ -1,4 +1,4 @@
-Code.require_file "../../fixtures/views.exs", __DIR__
+Code.require_file("../../fixtures/views.exs", __DIR__)
 
 defmodule Phoenix.Controller.RenderTest do
   use ExUnit.Case, async: true
@@ -48,7 +48,9 @@ defmodule Phoenix.Controller.RenderTest do
       |> put_root_layout({MyApp.LayoutView, "root.html"})
       |> render("index.html", title: "Hello")
 
-    assert conn.resp_body == "ROOTSTART[Hello]<html>\n  <title>Hello</title>\n  Hello\n\n</html>\nROOTEND\n"
+    assert conn.resp_body ==
+             "ROOTSTART[Hello]<html>\n  <title>Hello</title>\n  Hello\n\n</html>\nROOTEND\n"
+
     assert html_response?(conn)
   end
 
@@ -66,7 +68,9 @@ defmodule Phoenix.Controller.RenderTest do
       |> put_root_layout({MyApp.LayoutView, :root})
       |> render("index.html", title: "Hello")
 
-    assert conn.resp_body == "ROOTSTART[Hello]<html>\n  <title>Hello</title>\n  Hello\n\n</html>\nROOTEND\n"
+    assert conn.resp_body ==
+             "ROOTSTART[Hello]<html>\n  <title>Hello</title>\n  Hello\n\n</html>\nROOTEND\n"
+
     assert html_response?(conn)
   end
 
@@ -90,12 +94,16 @@ defmodule Phoenix.Controller.RenderTest do
 
   test "render with layout sets view_module/template for layout and inner view" do
     conn = render(conn(), "inner.html", title: "Hello", layout: {MyApp.LayoutView, :app})
-    assert conn.resp_body == "<html>\n  <title>Hello</title>\n  View module is Elixir.MyApp.UserView and view template is inner.html\n\n</html>\n"
+
+    assert conn.resp_body ==
+             "<html>\n  <title>Hello</title>\n  View module is Elixir.MyApp.UserView and view template is inner.html\n\n</html>\n"
   end
 
   test "render without layout sets inner view_module/template assigns" do
     conn = render(conn(), "inner.html", [])
-    assert conn.resp_body == "View module is Elixir.MyApp.UserView and view template is inner.html\n"
+
+    assert conn.resp_body ==
+             "View module is Elixir.MyApp.UserView and view template is inner.html\n"
   end
 
   test "renders with conn status code" do
@@ -126,14 +134,14 @@ defmodule Phoenix.Controller.RenderTest do
 
   test "uses action name" do
     conn = put_format(conn(), "html")
-    conn = put_in conn.private[:phoenix_action], :index
+    conn = put_in(conn.private[:phoenix_action], :index)
     conn = render(conn, title: "Hello")
     assert conn.resp_body == "Hello\n"
   end
 
   test "render/2 renders with View and Template with atom for template" do
     conn = put_format(conn(), "json")
-    conn = put_in conn.private[:phoenix_action], :show
+    conn = put_in(conn.private[:phoenix_action], :show)
     conn = put_view(conn, MyApp.UserView)
     conn = render(conn, :show)
     assert conn.resp_body == ~s({"foo":"bar"})
@@ -141,7 +149,7 @@ defmodule Phoenix.Controller.RenderTest do
 
   test "render/2 renders with View and Template" do
     conn = put_format(conn(), "json")
-    conn = put_in conn.private[:phoenix_action], :show
+    conn = put_in(conn.private[:phoenix_action], :show)
     conn = put_view(conn, MyApp.UserView)
     conn = render(conn, "show.json")
     assert conn.resp_body == ~s({"foo":"bar"})
diff --git a/test/phoenix/endpoint/render_errors_test.exs b/test/phoenix/endpoint/render_errors_test.exs
index cfd24fcb3..2aeea6db9 100644
--- a/test/phoenix/endpoint/render_errors_test.exs
+++ b/test/phoenix/endpoint/render_errors_test.exs
@@ -11,19 +11,47 @@ defmodule Phoenix.Endpoint.RenderErrorsTest do
     "Layout: " <> assigns.inner_content
   end
 
-  def render("404.html", %{kind: kind, reason: _reason, stack: _stack, status: 404, conn: conn}) do
+  def render("404.html", %{
+        kind: kind,
+        reason: _reason,
+        stack: _stack,
+        status: 404,
+        conn: conn,
+        __changed__: nil
+      }) do
     "Got 404 from #{kind} with #{conn.method}"
   end
 
-  def render("404.json", %{kind: kind, reason: _reason, stack: _stack, status: 404, conn: conn}) do
+  def render("404.json", %{
+        kind: kind,
+        reason: _reason,
+        stack: _stack,
+        status: 404,
+        conn: conn,
+        __changed__: nil
+      }) do
     %{error: "Got 404 from #{kind} with #{conn.method}"}
   end
 
-  def render("415.html", %{kind: kind, reason: _reason, stack: _stack, status: 415, conn: conn}) do
+  def render("415.html", %{
+        kind: kind,
+        reason: _reason,
+        stack: _stack,
+        status: 415,
+        conn: conn,
+        __changed__: nil
+      }) do
     "Got 415 from #{kind} with #{conn.method}"
   end
 
-  def render("500.html", %{kind: kind, reason: _reason, stack: _stack, status: 500, conn: conn}) do
+  def render("500.html", %{
+        kind: kind,
+        reason: _reason,
+        stack: _stack,
+        status: 500,
+        conn: conn,
+        __changed__: nil
+      }) do
     "Got 500 from #{kind} with #{conn.method}"
   end
 
diff --git a/test/phoenix/router/forward_test.exs b/test/phoenix/router/forward_test.exs
index a56605bdf..7abe49a3d 100644
--- a/test/phoenix/router/forward_test.exs
+++ b/test/phoenix/router/forward_test.exs
@@ -1,5 +1,5 @@
-defmodule Phoenix.Test.HealthController do
-  use Phoenix.Controller
+defmodule Phoenix.Router.HealthController do
+  use Phoenix.Controller, formats: []
   def health(conn, _params), do: text(conn, "health")
 end
 
@@ -8,8 +8,7 @@ defmodule Phoenix.Router.ForwardTest do
   use RouterHelper
 
   defmodule Controller do
-    use Phoenix.Controller
-
+    use Phoenix.Controller, formats: []
     plug :assign_fwd_script
 
     def index(conn, _params), do: text(conn, "admin index")
@@ -25,7 +24,7 @@ defmodule Phoenix.Router.ForwardTest do
     get "/", Controller, :api_root
     get "/users", Controller, :api_users
 
-    scope "/health", Phoenix.Test do
+    scope "/health", Phoenix.Router do
       forward "/", HealthController, :health
     end
   end
diff --git a/test/phoenix/router/pipeline_test.exs b/test/phoenix/router/pipeline_test.exs
index c3fd75b4c..d7bf73ccd 100644
--- a/test/phoenix/router/pipeline_test.exs
+++ b/test/phoenix/router/pipeline_test.exs
@@ -1,13 +1,7 @@
-# Define it at the top to guarantee there is no scope
-# leakage from the test case.
-
 defmodule Phoenix.Router.PipelineTest.SampleController do
-  use Phoenix.Controller
+  use Phoenix.Controller, formats: []
   def index(conn, _params), do: text(conn, "index")
-  def crash(_conn, _params), do: raise "crash!"
-
-  # Let's also define a custom plug that we will
-  # use in our router as part of a pipeline
+  def crash(_conn, _params), do: raise("crash!")
   def noop_plug(conn, _opts), do: conn
 end
 
@@ -135,8 +129,10 @@ defmodule Phoenix.Router.PipelineTest do
     assert_raise ArgumentError, ~r{duplicate pipe_through for :browser}, fn ->
       defmodule DupPipeThroughRouter do
         use Phoenix.Router, otp_app: :phoenix
+
         pipeline :browser do
         end
+
         scope "/" do
           pipe_through [:browser, :auth]
           pipe_through [:browser]
@@ -147,10 +143,13 @@ defmodule Phoenix.Router.PipelineTest do
     assert_raise ArgumentError, ~r{duplicate pipe_through for :browser}, fn ->
       defmodule DupScopedPipeThroughRouter do
         use Phoenix.Router, otp_app: :phoenix
+
         pipeline :browser do
         end
+
         scope "/" do
           pipe_through [:browser]
+
           scope "/nested" do
             pipe_through [:browser]
           end
@@ -163,9 +162,11 @@ defmodule Phoenix.Router.PipelineTest do
     assert_raise ArgumentError, ~r{there is an import from Kernel with the same name}, fn ->
       defmodule ConflictingPipeline do
         use Phoenix.Router, otp_app: :phoenix
+
         pipeline :raise do
           plug Plug.Head
         end
+
         scope "/" do
           pipe_through [:raise]
           get "/", UnknownController, :index
diff --git a/test/phoenix/router/resource_test.exs b/test/phoenix/router/resource_test.exs
index 8bcdca9b3..e962e03a1 100644
--- a/test/phoenix/router/resource_test.exs
+++ b/test/phoenix/router/resource_test.exs
@@ -3,7 +3,7 @@ defmodule Phoenix.Router.ResourceTest do
   use RouterHelper
 
   defmodule Api.GenericController do
-    use Phoenix.Controller
+    use Phoenix.Controller, formats: []
     def show(conn, _params), do: text(conn, "show")
     def new(conn, _params), do: text(conn, "new")
     def edit(conn, _params), do: text(conn, "edit")
diff --git a/test/phoenix/router/resources_test.exs b/test/phoenix/router/resources_test.exs
index 1d8e2c65b..964eada93 100644
--- a/test/phoenix/router/resources_test.exs
+++ b/test/phoenix/router/resources_test.exs
@@ -3,7 +3,7 @@ defmodule Phoenix.Router.ResourcesTest do
   use RouterHelper
 
   defmodule UserController do
-    use Phoenix.Controller
+    use Phoenix.Controller, formats: []
     def show(conn, _params), do: text(conn, "show users")
     def index(conn, _params), do: text(conn, "index users")
     def new(conn, _params), do: text(conn, "new users")
@@ -14,14 +14,14 @@ defmodule Phoenix.Router.ResourcesTest do
   end
 
   defmodule Api.FileController do
-    use Phoenix.Controller
+    use Phoenix.Controller, formats: []
     def show(conn, _params), do: text(conn, "show files")
     def index(conn, _params), do: text(conn, "index files")
     def new(conn, _params), do: text(conn, "new files")
   end
 
   defmodule Api.CommentController do
-    use Phoenix.Controller
+    use Phoenix.Controller, formats: []
     def show(conn, _params), do: text(conn, "show comments")
     def index(conn, _params), do: text(conn, "index comments")
     def new(conn, _params), do: text(conn, "new comments")
diff --git a/test/phoenix/router/routing_test.exs b/test/phoenix/router/routing_test.exs
index fa015fc8c..38801f7aa 100644
--- a/test/phoenix/router/routing_test.exs
+++ b/test/phoenix/router/routing_test.exs
@@ -10,7 +10,7 @@ defmodule Phoenix.Router.RoutingTest do
   end
 
   defmodule UserController do
-    use Phoenix.Controller
+    use Phoenix.Controller, formats: []
     def index(conn, _params), do: text(conn, "users index")
     def show(conn, _params), do: text(conn, "users show")
     def top(conn, _params), do: text(conn, "users top")
@@ -323,6 +323,7 @@ defmodule Phoenix.Router.RoutingTest do
         end,
         nil
       )
+
       on_exit(fn -> :telemetry.detach(test_name) end)
     end
 
diff --git a/test/phoenix/router/scope_test.exs b/test/phoenix/router/scope_test.exs
index 54bb47269..96f8ed367 100644
--- a/test/phoenix/router/scope_test.exs
+++ b/test/phoenix/router/scope_test.exs
@@ -5,7 +5,7 @@ defmodule Phoenix.Router.ScopedRoutingTest do
   # Path scoping
 
   defmodule Api.V1.UserController do
-    use Phoenix.Controller
+    use Phoenix.Controller, formats: []
     def show(conn, _params), do: text(conn, "api v1 users show")
     def delete(conn, _params), do: text(conn, "api v1 users delete")
     def edit(conn, _params), do: text(conn, "api v1 users edit")