Migrations

Migration CLI commands to allow you to version control your database schema.

With Migrations your team is able to easily modify and share the database schema to stay up to date. Migrations are typically paired with the Schema Builder which is inspired by the Laravel Schema Builder to easly manage your database’s schema.

Generating Migrations

Use the migration:make command to create a new Migration. This will create the following new Migration class in your migrations folder. In order to determine the order of the migrations, each migration file name contains a timestamp.

Arguments:

name Name of the migration

Migration Structure

Each Migration class contains two methods: Migration.up() and Migration.down(). In the Migration.up() method you add new tables, columns, or indexes to your database. In the Migration.down() method you should revert those changed made in the Migration.up() method.

Inside the Migration class you have access to the Schema Builder to easily create and modify tables. For more information check out its documentation: Schema

Running Migrations

To run the latest outstanding Migration, just use the migration:up command. To revert the last Migration operation, just use the migration:down command.

To roll back all migrations and then execute all migrations, just use the migration:refresh command. This command effectively re-creates your entire database.

To see the status of the migrations, just use the migration:status command. This would output something like this:

|-------------------------------------------+--------|
| Migration                                 | Ran?   |
|-------------------------------------------+--------|
| 20180814111005_create_users_table         | Yes    |
| 20180815101334_add_avater_to_users_table  | No     |
|-------------------------------------------+--------|

The Schema class provides a database agnostic way of manipulating tables.

Tables

Creating Tables

To create a new database table, the create() method is used. The create() method accepts a table name as its argument and returns a Blueprint instance that can be used to define the new table. When creating the table, you may use any of the Blueprint column methods to define the table’s columns:

with self.schema.create('users') as table:
    table.increments('id')

Checking Existence

To check if a table or column exist you can use the has_table() or has_column() methods respectively:

if self.schema.has_table('users'):
    # ...

if self.schema.has_column('users', 'email'):
    # ...

Renaming / Dropping Tables

To rename an existing database table, use the rename() method:

self.schema.rename('from', 'to')

To drop a table, you can use the drop() or drop_if_exists() methods:

self.schema.drop('users')
self.schema.drop_if_exists('users')

The Blueprint class lets you create, delete, or alter columns of a table.

Columns

Adding Columns

To update an existing table, you can use the Schema.table() method. The method accepts a table name as its argument and returns a Blueprint instance that can be used to add columns to the table:

with self.schema.table('users') as table:
    table.string('email')
    table.string('address').nullable()
    table.integer('age').nullable().unsigned()

The table builder contains a plethora of column types that you can use when building your tables:

Command Description
table.array('ratings', 'integer', 2) ARRAY column type. Only supported in postgresql!
table.big_increments('id') Auto-incrementing ID using a “big integer” (primary key) equivalent column.
table.big_integer('votes) BIGINT equivalent column.
table.binary('data') BLOB equivalent column.
table.boolean('confirmed') BOOLEAN equivalent column.
table.char('name', 4) CHAR equivalent column with a length.
table.date('created_on') DATE equivalent column.
table.datetime('created_at') DATETIME equivalent column.
table.decimal('amount', 5, 2) DECIMAL equivalent column with a precision (total digits) and a scale (decimal digits).
table.double('column', 15, 8) DOUBLE equivalent column with a precision (total digits) and a scale (decimal digits).
table.enum('choices', ['foo', 'bar']) ENUM equivalent column.
table.float('amount') FLOAT equivalent column.
table.increments('id') Auto-incrementing ID using a “integer” (primary key) equivalent column.
table.integer('votes') INTEGER equivalent column.
table.json('options') JSON equivalent column.
table.long_text('description') LONGTEXT equivalent column.
table.medium_integer('votes') MEDIUMINT equivalent column.
table.medium_text('description') MEDIUMTEXT equivalent column.
table.small_integer('votes') SMALLINT equivalent column.
table.string('email') VARCHAR equivalent column.
table.string('votes', 100) VARCHAR equivalent column with a length.
table.text('description') TEXT equivalent column.
table.time('sunrise') TIME equivalent column.
table.timestamp('added_at') TIMESTAMP equivalent column.

Column Modifiers

Each of the column methods above returns a Column instance, which lets you add several column “modifier” while adding a column to a table. For example, to make a column “nullable” you can use the nullable() method:

with self.schema.table('users') as table:
    table.string('email').nullable()

The column modifiers are the following (does not include index modifiers):

Modifier Description
.default('default_value') Specify a default value for the column.
.nullable() Designate that the column allows NULL values.
.unsigned() Set INTEGER column as UNSINGED (MySQL).

Dropping Columns

To drop a column from a table, you can use the drop_column() method.

Dropping a column from a database table:

with self.schema.table('users') as table:
    table.drop_column('votes')

Dropping multiple columns from a database table:

with self.schema.table('users') as table:
    table.drop_column('votes', 'avatar', 'location')

Indexes

The schema builder supports several types of indexes which you can add to and/or drop from columns. You can create the index after defining the column. For example:

table.string('email')
table.unique('email')

Available Index Types

Each index method accepts an optional second argument to specify the name of the index. If omitted, the name will be derived from the names of the table and column(s). Below is a list of all available index types:

Command Description
table.primary('id') Addys a primary key.
table.primary(['id', 'parent_id']) Adds composite keys.
table.unique('email) Adds a unique index.
table.index('state') Adds a basic index.

Note

In MySQL/MariaDB and PostgreSQL the length of indexes are limited. experimentum generates index names based on table and column names, therefore if your column names are too long you can pass the name keyword argument to specify your own index name:

table.index(
    ['some_field_with_a_really_long_name', 'another_really_long_field'],
    name='my_idx_name'
)

Dropping Indexes

To drop an index you must specify the index’s columns. experimentum assigns a reasonable name to the indexes by default. If you have specified a custom index name, you have to add it as the optional second argument.

Command Description
table.drop_primary('id') Drop a primary key from the “id” column.
table.drop_unique('email) Drop a unique index from the “email” column.
table.drop_index('state') Drop a basic index from the “state” column.
table.drop_foreign('user_id') Drop a foreign key from the “user_id” column.

Foreign Key Contraints

experimentum also provides support for adding foreign key constraints to your tables:

with self.schema.table('posts') as table:
    table.increments('user_id')
    table.foreign('user_id')\
        .references('id').on('users')\
        .on_delete('cascade')\
        .on_update('cascade')

In this example, we are stating that the user_id column references the id column on the users table and set the “on update” and “on delete” actions to cascade. Make sure to create the foreign key column first!

To drop a foreign key, you can use the drop_foreign() method. It works just like dropping an index.