Home Posts Tags Post Search Tag Search

Post 90

LiveView 05 - Chapter 3 Generators

Published on: 2025-12-04 Tags: elixir, Blog, Side Project, Testing, LiveView, Ecto, Html/CSS, Phoenix
Chapter 3 Generators: Contexts and Schemas
    For the next phase of the project we will be building a project catalog for our project using as many generators as possible. The Generators are great a building any CRUD feature that you want. Plus there is built in features to go beyond that. 

    Here is the plan for the first feature:
        Run the Generator 
        Focus on the Backend
            Schema
            API Layer
        Focus on the Frontend

    Get to Know the Phoenix Live Generator
        mix phx.gen.live is a script that generates code supporting full CRUD. It also will build routes that can take care of the frontend for templates.

        The biggest part of a generator is the use of macros, code that writes code. The next is the generic code that is created to take care of the basic part of the framework. However if this basic framework isn't done to standard and supports additions it can only be used for a specific task. 

    Run the Phoenix Live Generator
        Here we will build a product manager for our site. This will be a resource called Product the generator will create the CRUD for that resource. The frontend will reside in the lib/pento_web while the backend will reside in lib/pento. The way in which we handle all the interactions will be the context (API).

        Once we are done we will have a schema for a Product and a Catalog context, along with live views for managing a product. 

        The HTTP request through the route /products will be handled by the live view (Context) and then the schema will be called by that top layer to deal with the database.  

        With this out of the way let's fire up the generator!!

        Learn How To Use the Generator
            The syntax for a generator will need a context and a schema. We will also need to tell Phoenix which fields to support for the resource. Let's run the generator without options to see some helpful docs.
                $ mix phx.gen.live
                ...compiling...
                ** (Mix) Invalid arguments
                mix phx.gen.html, phx.gen.json, phx.gen.live, and phx.gen.context
                expect a context module name, followed by singular and plural names
                of the generated resource, ending with any number of attributes.
                For example:
                mix phx.gen.html Accounts User users name:string
                mix phx.gen.json Accounts User users name:string
                mix phx.gen.live Accounts User users name:string
                mix phx.gen.context Accounts User users name:string
                The context serves as the API boundary for the given resource.
                Multiple resources may belong to a context and a resource may be
                split over distinct contexts (such as Accounts.User and Payments.User).
                ----------

            Let's break down that example mix phx.gen.live Accounts User users name:string
                Accounts is the context
                User is the schema
                The pairs that follow are the fields that will be in our database

        Generate a Resource
            Let's run the actual syntax that we want for our new context
                mix phx.gen.live Catalog Product products name:string \
                description:string unit_price:float sku:integer:unique
                * creating lib/pento_web/live/product_live/show.ex
                * creating lib/pento_web/live/product_live/index.ex
                * creating lib/pento_web/live/product_live/form.ex
                * creating test/pento_web/live/product_live_test.exs
                * creating lib/pento/catalog/product.ex
                * creating priv/repo/migrations/20250724132712_create_products.exs
                * creating lib/pento/catalog.ex
                * injecting lib/pento/catalog.ex
                * creating test/pento/catalog_test.exs
                * injecting test/pento/catalog_test.exs
                * creating test/support/fixtures/catalog_fixtures.ex
                * injecting test/support/fixtures/catalog_fixtures.ex
                Add the live routes to your browser scope in lib/pento_web/router.ex:
                live "/products", ProductLive.Index, :index
                live "/products/new", ProductLive.Form, :new
                live "/products/:id", ProductLive.Show, :show
                live "/products/:id/edit", ProductLive.Form, :edit
                Ensure the routes are defined in a block that sets the :current_scope assign.
                -----
            
            Okays now that we have the files it tells us to be sure that we add in the new routes to a block we will add them into the scope that contains the :require_authenticated_user and the live session block pento/lib/pento_web/router.ex
                scope "/", PentoWeb do
                    pipe_through([:browser, :require_authenticated_user])

                    live_session :require_authenticated_user,
                        root_layout: {PentoWeb.Layouts, :root},
                        on_mount: [{PentoWeb.UserAuth, :require_authenticated}] do
                        live("/users/settings", UserLive.Settings, :edit)
                        live("/users/settings/confirm-email/:token", UserLive.Settings, :confirm_email)
                        live("/products", ProductLive.Index, :index)
                        live("/products/new", ProductLive.Form, :new)
                        live("/products/:id", ProductLive.Show, :show)
                        live("/products/:id/edit", ProductLive.Form, :edit)
                    end

                    post("/users/update-password", UserSessionController, :update_password)
                end

            live is a macro that will start a new live session and pass in 3 arguments,
                The route ("/products")
                The module (Product.Index)
                The live action (:new)
            
            
        Verify the Generated Code
            Let's test the code to be sure that the initial setup works as needed. Let's run the tests.
                [pento] ➔ mix test
                ..................................................................
                ..................................................................
                ...............
                Finished in 0.3 seconds (0.1s async, 0.1s sync)
                122 tests, 0 failures
                Randomized with seed 829064

            Now let's look at how the test deal with automatic authentication. 
                setup :register_and_log_in_user
            
                defp create_product(%{scope: scope}) do
                    product = product_fixture(scope)
                    %{product: product}
                end

            setup :register_and_log_in_user will create a user and log them in while making sure that we deal with the scope.

        Understanding Scope-Aware Fixtures
            With this understanding about the scope and how it functions within any call. We will see that fixtures will take the scope as a parameter and use that to do anything within the context of the context of the new resource. 
                def product_fixture(scope, attrs \\ %{}) do
                    attrs =
                        Enum.into(attrs, %{
                            description: "some description",
                            name: "some name",
                            sku: unique_product_sku(),
                            unit_price: 120.5
                        })
                    {:ok, product} = Pento.Catalog.create_product(scope, attrs)
                    product
                end

        The Job of Scopes in Testing
            Data Isolation
            Security Verification
            Realistic Testing
            Simplified Setup