Defining the User Schema and Migration

At its core, Ecto lets you specify a struct that ties individual fields to the fields in database tables through a DSL. Let’s use that now. To define our schema, let’s replace our bare user struct in lib/rumbl/accounts/user.ex with the following:

 defmodule​ Rumbl.Accounts.User ​do
 use​ Ecto.Schema
 import​ Ecto.Changeset
 
  schema ​"​​users"​ ​do
  field ​:name​, ​:string
  field ​:username​, ​:string
 
  timestamps()
 end
 end

This DSL is built with Elixir macros. The schema and field macros let us specify both the underlying database table and the Elixir struct at the same time. Each field corresponds to both a field in the database and a field in our local Accounts.User struct. By default, Ecto defines the primary key called :id automatically. From the schema definition, Ecto automatically defines an Elixir struct for us, which we can create by calling %Rumbl.Accounts.User{} as we did before.

Finally, our schema uses use Ecto.Schema at the top and it also imports Ecto.Changeset functions which will allow us to work more easily with changesets later on.

We’ve treated our code with care, and we should give our database at least the same level of respect. Now that we have our Repo and User schema configured, we need to make the database reflect the structure of our application. Ecto uses migrations for that purpose. A migration changes a database to match the structure our application needs. For our new feature, we need to add a migration to create our users table with columns matching our User schema. Let’s generate one:

 $ ​​mix​​ ​​ecto.gen.migration​​ ​​create_users
 * creating priv/repo/migrations
 * creating priv/repo/migrations/20180315023132_create_users.exs

The mix ecto.gen.migration command creates a migration file for us with a special timestamp to ensure ordering of our database migrations. For this reason, your migration filename will have a different prefix than ours. Key in these changes within your empty change function:

 defmodule​ Rumbl.Repo.Migrations.CreateUsers ​do
 use​ Ecto.Migration
 
 def​ change ​do
  create table(​:users​) ​do
  add ​:name​, ​:string
  add ​:username​, ​:string​, ​null:​ false
  add ​:password_hash​, ​:string
 
  timestamps()
 end
 
  create unique_index(​:users​, [​:username​])
 end
 end

In the dark days of persistence frameworks, before migrations were commonplace, changes to the database weren’t versioned with the source code. Often, those changes weren’t even automated. That strategy was fine if new code worked the first time, but it opened the door for problems:

  • When deploying new code, programmers often introduced errors when changing the database.

  • The high stress of code rollbacks led to frequent mistakes when changes were rolled back under pressure.

  • Building a fresh development environment was tough because the schema history was too fragmented.

In general, migrating a database, both up for a successful deploy and down for an unsuccessful deploy, should be an automated and repeatable process. The Ecto.Migration API[14] provides several functions to create, remove, and change database tables, fields, and indexes. These functions also have counterparts to do the reverse. Here, we used the create, add, and timestamps macros to build our users table and matched the fields with our User schema. For example, add creates a new field, and timestamps creates a couple of fields for us, inserted_at and updated_at.

Overall, we created a table with six fields: the auto-generated id, the inserted_at and updated_at timestamps, name, username, and password_hash. We added password_hash to the database but we didn’t list password_hash as a schema field for now. We will introduce it when it’s time to discuss authentication in Chapter 5, Authenticating Users. Finally, we add a unique index to guarantee that the username field is unique across the whole table.

Now all that’s left is to migrate up our database:

 $ ​​mix​​ ​​ecto.migrate
 
 [info] == Running Rumbl.Repo.Migrations.CreateUsers.change/0 forward
 [info] create table users
 [info] create index users_username_index
 [info] == Migrated in 0.0s

Be careful. The ecto.migrate task will migrate the database for your current environment. So far, we’ve been running the dev environment. To change the environment, you’d set the MIX_ENV operating-system environment variable.

We’ve configured our database and created the schema with a migration. We are ready to use our repository through the Accounts context. We just need to make sure our repository services are up and running.

Phoenix is built on top of OTP, a layer for reliably managing services. We can use OTP to start key services like Ecto repositories in a supervised process so that Ecto and Phoenix can do the right thing in case our repository crashes. To do so, we simply need to list the Rumbl.Repo as a child in our supervision tree. Phoenix already did that for us. Open up lib/rumbl/application.ex and you will find this:

 children = [
  ...
 # Start the Ecto repository
  Rumbl.Repo,
  ...
 ]

Now that our configuration is established, let’s take it for a spin.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.217.5