Skip to content
Bill Zhao edited this page Apr 7, 2015 · 9 revisions

Database Migrations in CodeIgniter

Migrations is a simple utility inspired by its Ruby on Rails counterpart that makes working with database changes a lot easier to development teams.

The concept is really simple: abstract each change to your database schema to a "migration" class in a way that other team members can apply (and undo) each change easily.

Note that Migrations are now available in the core, and this wiki is no longer relevant!

Common usage

  • Each individual in a team is suposedly working on their own installation of your system.
  • Whenever a team member makes a change to their database schema, he extracts that change into a class called a "migration".
  • When he is done working, he can move those "changes" to any other environment (the "live" environment, for example) and "install" each migration

Installation

Download and unzip the package: File:migrations.1.0.zip

Move its contents to your application folder.

A Database Utilities helper is included to make your migrations code a lot cleaner.

Edit config/migrations.php file in order to enable migrations and setting your migrations path.

Note: It is recommended that migrations are disabled on your live environment when you are not migrating your schema. It is intended to add further security measures in future realeses.

Note 2: Your migrations path should be write-enabled.

How it works

Each migration is defined inside a file named in the following way:

###_migration_unique_name.php

Where ### is the version number of the migration and migration_unique_name is a unique name that must be formatted according to CI naming conventions.

The version number is very important, since it will determine the order in which the migrations are installed and uninstalled. If two or more migrations share the same version number, a conflict will be announced and the migration will not work until it is resolved by the user.

The name of the migration should be unique. That is, no other migration can share its name (ignoring the version number and the first underscore). This is because a migration name is supposed to be descriptive as what it does when installed. For example 001_create_table_ratings.php would intuitively create a new table in our schema and it will be named "ratings".

If a development team defines a solid naming convention for their migrations, it would be very unlikely that two developers work on the same database migration without raising any alerts from the migrations utility.

Once the file has been created, a new class should be defined inside it. Classname should match the unique_name part of the file, and it must respect CI naming conventions (that is, first letter must be upper-case and the rest, lower-case).

The new class must implement the methods "up" and "down" which should work in oppossition. Whatever is done by "up" must be undone by "down"

Going up and down (applying/unapplying migrations)

When you have your migrations files ready, you are ready to start applying your changes.

The migrate controller is in charge of doing this with two methods: version and install

When calling version and specifying the desired version number, Migrate wil move up or down as many migrations as needed to reach the desired version, calling each migration method (corresponding to the direction of your migration) in the order specified by its number.

Example (migrations in this example are included in the zip):

Suppose you whish to add articles and comments to an existing schema. The first thing to do would by creating the required tables: "articles" and "comments".

So we create a new migration file located in our migrations path and name it 001_create_articles_schema.php

We numbered it 001 as it's the first migration we created and chose a meaningful name for it.

Then we open our editor and start coding for a change:

<?php
class Create_articles_schema {

    function up() {

        echo "Creating table 'articles'...";

        create_table (
            'articles',
            array (
                'id_article' => array ( INTEGER, NOT_NULL ),
                'title' => array ( STRING, LIMIT, 50),
                'body' => TEXT,
                'date_published' => DATE,
                'author' => array ( STRING, LIMIT, 30, DEFAULT_VALUE, 'Anonymous' ),
                'visible' => BOOLEAN
            ),
            'id_article'
        );

        echo "DONE<br />";
        echo "Creating table 'comments'...";

        create_table (
            'comments',
            array (
                'id_comment' => array ( INTEGER, NOT_NULL ),
                'body' => TEXT,
                'email' => STRING
            ),
            'id_comment'
        );
    
        echo "DONE<br />";
    
    }
    
    function down() {
    
        echo "Droping table 'articles'...";
        drop_table("articles");
        echo "DONE<br />";
    
        echo "Droping table 'comments'...";
        drop_table("comments");
        echo "DONE<br />";
    
    }
}

?>

Both functions: create_table and drop_table are defined in the dbutil helper. If you do not want to use this helper, you can always call get_instance() to retrieve the CI instance and call each query in the traditional way.

To make use of the functions in the dbutil helper, add the following line inside the Migrate constructor in migrate.php:

                 $this->load->helper(’dbutil’);

As you can notice, the "up" method creates tables articles and comments, while the "down" method drops them both.

You might have also noticed the "echo" calls in both methods. This is cool because we might need to know what the migration utility is doing in case something goes wrong.

We can now open our browser and type in the URL http://yourdomain/index.php/migrate/version/1 and your migration will be executed, that is, your tables will be created.

Your schema is now at version 1. If type in the same url again, no migration will take place, as your schema is already updated to version 1.

To undo your migration type in http://yourdomain/index.php/migrate/version/0

Back to the example.

Imagine that later on the table name "articles" is no longer considered appropiate and the team decides to rename it to "blogs".

A new migration is required for this change.

002_rename_articles.php

<?php
class Rename_articles {

    function up() {

        echo "Renaming table...";
        rename_table("articles", "blogs");
        echo "DONE";

    }

    function down() {

        echo "Renaming table...";
        rename_table("blogs", "articles");
        echo "DONE";

    }

}
?>

It becomes obvious that by abstracting schema changes in this way, there is a huge gain in code clarity and portability

Installing a schema to the last available version

The second available method in the Migrate Controller is install.

Intall will look for al migrations files and sequentially install all available migrations, moving up to the last available one.

Install comes in handy when moving to a new server or when a new member is added to the development team.

Future releases

This my first release intended to introduce migrations to those who never worked with RoR and are unfamiliar with the concept.

The idea is to get as much feedback as possible both here and in the forums to work in more advanced future releases I have already planned.

Some of the features I have planned to add:

  • Version history: store information on installed migrations and avoid conflicts when moving back to a previous migration
  • Conflict solving: I plan on a way to merge migrations when a version conflict arises or totally ignore specified migrations. My idea is to make this work much like the SVN conflicts.
  • Graphic interface: It would be nice to have a simple graphic interface to choose versions and the like.
  • Command line: Although CI devs never encouraged the use of command line, this feature would move CI Migrations one step closer to it's RoR counterpart.
Clone this wiki locally