#26: Moving to Elixir Part 2

Published December 13, 2017 4m 22s

Elixir 1.4.5

Phoenix 1.3


In part 1 we created our new Phoenix app and imported our existing data into it.

Now that we have some data, we need to decide how we want to structure it in our app.

Phoenix 1.3 introduced the idea of Contexts. Contexts are great, because they provide a way to organize modules that have related functionality.

For our application - a car dealership - we have different listings of cars that are for sale. So let’s create a context called “listings”, which we can use to organize functionality related to our car listings.

Let’s go to the command line and run the generator. Where ‘Listings’ is our context module. Car is our ‘schema’ module with ‘cars’ as the plural - which is used as the schemas table name.

We’ll also pass the ‘no-schema’ flag here since we’ve already imported our database schema we don’t need to generate any migrations

$ mix phx.gen.html Listings Car cars --no-schema

We can see the command created a ‘car controller’ for us as well as our templates, view, and our ‘Listings’ context and tests.

We’re also prompted to add a new resource to our router. Let’s open our ‘router’ and we’ll add:

lib/dealership_web/router.ex  scope "/", DealershipWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
    resources "/cars", CarController
  end

Because we passed “no-schema”, one thing that was not created was our “car” module. We’ll need to create it.

Inside the “listings” directory let’s create a file named car.ex

Then we’ll define the module and We’ll include use Ecto.Schema since this module will map to our existing cars table in the database.

This module will also hold our cars schema, so let’s go ahead and define a schema that maps to all columns in our new database. If we look at our schema - we notice there’s not an id column. When we create a table with a migration in Ecto it by default creates an id to use as our primary key.

However since we’re working with a legacy database, we can’t count on id being our primary key. In fact, for our ‘cars’ table, the primary key is the car_pk column.

We’ll need to tell Ecto that this is our primary key.

We’ll use the primary_key schema attribute, specifying the field_name of car_pk. Then the primary key field type, in this case :id. We’ll then use the option autogenerate: true, which means our database will handle creating the id. Then we can remove the field from our schema.

lib/dealership/listings/car.exdefmodule Dealership.Listings.Car do
  use Ecto.Schema

  @primary_key {:car_pk, :id, autogenerate: true}
  schema "cars" do
    field :car_Stock, :string
    field :car_VIN, :string
    field :car_Year, :integer
    field :car_Make, :string
    field :car_Model, :string
    field :car_Body, :string
    field :car_Trim, :string
    field :car_Doors, :string
    field :car_ExteriorColor, :string
    field :car_InteriorColor, :string
    field :car_EngineCylinders, :string
    field :car_EngineDisplacement, :string
    field :car_Transmission, :string
    field :car_Miles, :integer
    field :car_SellingPrice, :integer
    field :car_Certified, :string
    field :car_Description, :string
    field :car_Options, :string
    field :car_Engine_Description, :string
    field :car_Transmission_Speed, :string
    field :car_Transmission_Description, :string
    field :car_Drivetrain, :string
    field :car_Fuel_Type, :string
    field :car_CityMPG, :integer
    field :car_HighwayMPG, :integer
    field :car_PassengerCapacity, :string
    field :car_isFeatured, :string
    field :car_isReduced, :string
    field :car_isListed, :string
    field :car_Carfax_One_Owner, :string
    field :image_list, :string
  end
end

Now let’s go to the command line and start up our server.

$ mix phx.server

And if we go to our localhost:4000/cars - we see a ’TODO’ error in our listings.ex module.

Let’s go ahead and open our listings module. And our list_cars function hasn’t been implemented. Let’s do that now. We’ll use Repo.all to return all cars from the database.

Then let’s go down to our next function - get_car! and implement it as well with Repo.get to fetch the car by the id that’s passed in.

lib/dealership/listings/listings.exdefmodule Dealership.Listings do

  …

  def list_cars do
    Repo.all(Car)
  end

  def get_car!(id), do: Repo.get!(Car, id)

  …

end

With those functions updated, let’s go back to our browser and reload the page. And we see another error - structs expect an :id key when converting to_param.

This is because Phoenix assumes our car struct has a key of ‘id’ - but ours doesn’t. Let’s open our car.ex module and tell Phoenix to use our car_pk.

The Phoenix.Param protocol is derivable so we’ll derive Phoenix.Param and then specify our key of car_pk

lib/dealership/listings/car.exdefmodule Dealership.Listings.Car do
  use Ecto.Schema

  @derive {Phoenix.Param, key: :car_pk}
  …
end

Then let’s go back to the browser and if we refresh our localhost:4000/cars page - it loads.

While we do see the familiar Phoenix layout, info about our cars is missing. Let’s fix that by adding some basic info. We’ll open our car’s show template and update it to display the year, make, and model of the car.

Template path:lib/dealership_web/templates/car/show.html.eex

<ul>
  <%= "#{@car.car_Year} #{@car.car_Make} #{@car.car_Model}" %>
</ul>

Then we’ll open the index template and display the same info for each car.

Template path: lib/dealership_web/templates/car/index.html.eex

<td>
  <%= "#{car.car_Year} #{car.car_Make} #{car.car_Model}" %>
</td>

Going back to our browser we see some basic info about each car being displayed. And if we click through to another car - it’s displayed on the show page as well - perfect.

Ready to Learn More?

Subscribe to get access to all episodes and exclusive content.

Subscribe Now