Today we are going to get build a simple Phoenix CRUD (Create Read Update Destroy) application. Ultimately, we will be deploying this on Vultr but for now lets focus on some Phoenix basics with a practical goal: Making the best darn photo archive out there, Pharchive.
I am an amateur photographer and I have hundreds of pages of photographic negatives which I need to painstakingly keep track of, and make search-able. To aid me in this arduous task I am going to draw on Phoenix.
Let’s start small, we can always add features later. Here is a list of things that I want Pharchive to do:
This is a pretty straightforward set of Parent Child relationships, so this should be quick!
The models are going to be as follows:
These are the type of models that Phoenix’s generators make easy, but first…
Move to where you would like to have the Pharchive project live in your directory structure, then run mix phoenix.new Pharchive
. Don’t forget to answer Y
to the question: “Fetch and install dependencies?”. cd Pharchive
and run mix ecto.create
, this makes sure that you have a database created for the Pharchive application.
At this point you have done all that is required to have a basic phoenix application which can respond to a web request. If you want to test this run mix phoenix.server -p 4000
(the -p 4000 is superfluous as mix phoenix.server
defaults to using 4000
, but I want to be sure not to lead you astray!) and visiting localhost:4000
in your browser or with curl
.
Neat.
Next up we are going to start at the top of our data model with Manufacturer
. Manufacturers are film producers, at this point I don’t need to know anything more about them than just a name. Let’s use Phoenix generators to build up a simple CRUD interface for working with Manufacturers.
mix phoenix.gen.html Manufacturer manufacturers name:string
Next we will create the Film
model. This is a fairly important piece, which has a few useful attributes.
mix phoenix.gen.html Film films speed:integer name:string short_name:string manufacturer_id:references:manufacturers
Finally we get to the real meaty model, which I am primarily concerned with: Collections
.Collections are actual physical objects which I keep track of.
mix phoenix.gen.html Collection collections physical_id:integer uuid:uuid location:string frame_count:integer description:string taken_at:date frame_size:string film_id:references:films
Now that everything is all generated, we have to make sure that our application router has the new pages in place.
file: router.ex
defmodule Pharchive.Router do
use Pharchive.Web, :router
pipeline :browser do
plug :accepts, ["html"]
plug :fetch_session
plug :fetch_flash
plug :protect_from_forgery
plug :put_secure_browser_headers
end
scope "/", Pharchive do
pipe_through :browser # Use the default browser stack
resources "/manufacturers", ManufacturerController
resources "/films", FilmController
resources "/collections", CollectionController
get "/", PageController, :index
end
end
Next, we must make sure that our associations are reflected in our models.
file: models/film.ex
file: models/manufacturer.ex
defmodule Pharchive.Manufacturer do
schema "manufacturers" do
...
has_many :films, Pharchive.Film
...
end
end
And finally, run a mix ecto.migrate
to ensure that the database schema is up to date! With that, Pharchive has been properly setup and is ready for some upgrades.
The next step is automating UUID creation on a collection. Having to type in a fresh UUID for every collection is just going to get tedious. Luckily for us, this is a quick and easy change in web/models/collections.ex
schema "collections" do
...
field :uuid, Ecto.UUID, autogenerate: true
...
end
# Remove uuid from the list of required fields
@required_fields ~w(physical_id location frame_count description taken_at frame_size)
And since we are relying on the application to generate these UUIDs, we can remove this whole form group from the collections form in web/templates/collection/form.eex
. Go ahead and do that now.
Neat.
In order to effectively manage our soon to arrive front end packages, we are going to need Bower installed. I like Bower; it is easy to understand, and it plays very nicely with phoenix.
Now that we have Bower installed, lets add some enhancements to our UI. Our form’s date pickers are atrocious. I think we should get something a bit more usable. I chose to use bootstrap-datepicker.
Since jQuery is a prerequisite, we are going to need to tell Bower to install both jquery
and bootstrap-datepicker
. We can do this by creating a bower.json
file at the top of the Pharchive project tree.
The next time you fire up the Phoenix server you will see that bower fetches jquery
and bootstrap-datepicker
and makes them available to Phoenix. Next, we will initialize the datepicker in a new file web/static/js/datepicker.js
export default function initializeDatepickers() {
let options = {}
$('.datepicker').datepicker(options);
}
Neat!
We only have a few more steps to see this bear fruit. Let’s import our new datepicker function in web/static/js/app.js
And finally, we need to add the datepicker
class onto the date input that we want to clean up. In web/templates/collection/form.html.eex
change the following line:
To
and boom, our datepicker is now working in the Collection form!
Thats all for today folks, you can review the repo and check out the working code here. Next time we will be working with forms!