DEV Community

Cover image for Database Migrations
Arman
Arman

Posted on • Edited on

Database Migrations

Suppose we are starting a new big project and of course first thing to do is to compose our database architecture. And we want to use some sort of relational database system such as MySQL.

Surely we can go ahead and create our database and the tables through some GUI or via terminal. And then we can start codding our project business logic. If on the project works more people than us, then we should share our DB schema with other developers. We can do it by exporting the SQL dump and sending to them to import.

Sounds good so far…

Now suppose some developers are adding new columns on some of the tables on their branch. Other developers are removing some columns (as per requirement of their task). Some other developers may change some columns (like changing type or names or indexes, etc.).

This quickly becomes a mess, as nobody can tell exactly which operations should be taken in the first place, which are in the second and so on…

Migrations comes to help

Database migrations or simply migrations, are controlled sets of changes developed to modify the structure of the objects within a relational database. Migrations help transition database schemas from their current state to a new desired state. Whether that involves adding tables and columns, removing elements, splitting fields, or changing types and constraints.

The goals of database migrations are to make database changes repeatable, shareable, and testable.
In general, migration systems create files that can be shared, applied to multiple database systems, and stored in version control.

Migrations in Quantum framework

From version 2.7.0 Quantum comes with new migration feature which allows developers to fill that gap they have previously. It currently supports MySQL 8 and above as well as Mariadb 10.5.x and above. In near future it’s expected to have SQLIte and PostgreSQL included. The way of creating and applying migrations, are done via console commands and it mostly similar to other PHP frameworks if you tried one already.

The first thing to do is to generate a migration file. which can be done via migration:generate command. The command accepts arguments, "type" and "table name". There are 4 types available for generating migrations, create, alter, rename and drop.

Create table migration

To generate create type of migration we specify the type as first parameter and the table name as second.

> php qt migration:generate create users
Enter fullscreen mode Exit fullscreen mode

The new migration file will be created in the project migrations directory and will named something like create_table_users_1655307916.php with some prefilled content.

<?php 
use Quantum\Migration\QtMigration; 
use Quantum\Factory\TableFactory; 
use Quantum\Libraries\Database\Schema\Type; 
use Quantum\Libraries\Database\Schema\Key; 
class Create_table_users_1655307916 extends QtMigration 
{ 
    public function up(?TableFactory $tableFactory) 
    {
        $table = $tableFactory->create('users'); 
    } 
    public function down(?TableFactory $tableFactory) 
    { 
        $tableFactory->drop('users'); 
    } 
}
Enter fullscreen mode Exit fullscreen mode

Alter table migration

To generate alter type of migration we specify the type as first parameter and the table name as second.

> php qt migration:generate alter users
Enter fullscreen mode Exit fullscreen mode

The new migration file will be created in the project migrations directory and will named something like alter_table_users_1655308339.php with some prefilled content

<?php 
use Quantum\Migration\QtMigration; 
use Quantum\Factory\TableFactory; 
use Quantum\Libraries\Database\Schema\Type; 
use Quantum\Libraries\Database\Schema\Key; 
class Alter_table_users_1655308339 extends QtMigration 
{ 
    public function up(?TableFactory $tableFactory) 
    { 
        $table = $tableFactory->get('users'); 
    } 
    public function down(?TableFactory $tableFactory) 
    { 
        $table = $tableFactory->get('users'); 
    } 
}
Enter fullscreen mode Exit fullscreen mode

Rename table migration

To generate rename type of migration we specify the type as first parameter and the table name as second.

> php qt migration:generate rename users
Enter fullscreen mode Exit fullscreen mode

The new migration file will be created in the project migrations directory and will named something like rename_table_users_1655308655.php with some prefilled content.

Notice, that the new table name need to be added there as a 2nd argument of rename() method.

<?php 
use Quantum\Migration\QtMigration;  
use Quantum\Factory\TableFactory; 
class Rename_table_users_1655308655 extends QtMigration 
{ 
    public function up(?TableFactory $tableFactory) 
    { 
        $tableFactory->rename('users', $newName); 
    } 
    public function down(?TableFactory $tableFactory) 
    { 
        $tableFactory->rename($newName, 'users'); 
    } 
}
Enter fullscreen mode Exit fullscreen mode

Drop table migration

To generate drop type of migration we specify the type as first parameter and the table name as second.

> php qt migration:generate drop users
Enter fullscreen mode Exit fullscreen mode

The new migration file will be created in the project migrations directory and will named something like drop_table_users_1655308866.php with some prefilled content

<?php 
use Quantum\Migration\QtMigration; 
use Quantum\Factory\TableFactory; 
class Drop_table_users_1655308866 extends QtMigration 
{ 
    public function up(?TableFactory $tableFactory) 
    { 
        $tableFactory->drop('users'); 
    } 
    public function down(?TableFactory $tableFactory) 
    { 
        // 
    } 
}
Enter fullscreen mode Exit fullscreen mode

As we can see, every time it creates a class which extends the QTMigration base class and it always has 2 public methods: up() and down().

The up method is used to create new tables, columns, or indexes to your database, while the down method should reverse the operations performed by the up method.

Updating migration file content

Ok, now let’s open our migration file and add some new columns to the table with addColumn() method..

public function up(?TableFactory $tableFactory) 
{ 
    $table = $tableFactory->create('users'); 
    $table->addColumn('id', Type::INT, 11)->autoIncrement(); 
    $table->addColumn('username', Type::VARCHAR, 100)->unique(); 
    $table->addColumn('is_active', Type::BOOL); 
    $table->addColumn('options', Type::ENUM, ['op1', 'op2', 'op3'])->default('op2'); 
}
Enter fullscreen mode Exit fullscreen mode

We have added 3 columns to the table:

id as integer with autoincrement
username as varchar with unique index
options as enum with default value
We can also alter the existing column paramters with modifyColumn() method.

public function up(?TableFactory $tableFactory) 
{ 
    $table = $tableFactory->get('users'); 
    $table->modifyColumn('username', Type::VARCHAR, 50); 
    $table->modifyColumn('is_active', Type::TINYINT, 1); 
}
Enter fullscreen mode Exit fullscreen mode

Here we have modified 2 columns:

username column length was changed from 100 to 50
is_active column type was changed from boolean to tinyint
We can also rename columns with renameColumn() method.

public function up(?TableFactory $tableFactory) 
{ 
    $table = $tableFactory->get('users'); 
    $table->renameColumn('username', 'email'); 
}
Enter fullscreen mode Exit fullscreen mode

To drop columns we use dropColumn() method.

public function up(?TableFactory $tableFactory) 
{ 
    $table = $tableFactory->get('users'); 
    $table->dropColumn('options'); 
}
Enter fullscreen mode Exit fullscreen mode

Playing with indexes

When adding new column we can also specify the index with it as well, like:

$table->addColumn('profile_id', Type::INT, 11)->index();
Enter fullscreen mode Exit fullscreen mode

Also we can specify the index name:

$table->addColumn('profile_id', Type::INT, 11)
      ->index('idx_profile_id');
Enter fullscreen mode Exit fullscreen mode

There are 5 types of indexes are available: primary, index, unique, fulltext and spatial.

In order to add index key to an existing column we can use addIndex() method like:

$table->addIndex('profile_id', Key::INDEX);
Enter fullscreen mode Exit fullscreen mode

Or with the index name:

$table->addIndex('profile_id', Key::INDEX, 'idx_profile_id');
Enter fullscreen mode Exit fullscreen mode

Remember, if you are not specifying the index name, it will take the column name is its key name.

To drop the index from the column we can use dropIndex() method by passing the index name as paramter like this:

$table->dropIndex('idx_profile_id');
Enter fullscreen mode Exit fullscreen mode

Migrating migrations

Ok, now when we have our table schemas defined in the migration files, it’s time to migrate them.

So to apply the migrations we simply run this command (the argument up is optional and can be skipped):

> php qt migration:migrate up
Enter fullscreen mode Exit fullscreen mode

If you running this command first time, it will create new table in your database with name migrations, where it will store all applied migrations. Then it will iterate through the migrations which were created and run them all in chronological order.

Notice that the migration:migrate command runs only up migrations and only ones that weren't yet migrated.

If you want to roll back the migrated migrations you will need to run:

> php qt migration:migrate down
Enter fullscreen mode Exit fullscreen mode

Be aware that this will iterate through all the migrations in reverse chronological order and run down migrations. If you want to roll back not all the migrations but down to specific migration, you can specify the step:

> php qt migration:migrate down --step=2
Enter fullscreen mode Exit fullscreen mode

This is all for today, hope you find it useful, write comments if you have questions… Good luck.

Top comments (0)