The migrations in CodeIgniter or how to have a “Git” for your database (part 1 of 3)

Well… this title is a bit misleading, isn’t it? Let’s just start by talking about what is a Git.

Git is some sort of history tracker for your code. That is, whenever you modify something in your code, Git keeps a track of how the code looked before your update, and how it looks after the update. By using it, you and those that work with you can find out how the coding evolved and return to previous versions if something went wrong or want to create a new branch.

The same happens with the migrations, these being related to the database operations. Migrations help you and your team mates keep track of the changes you make to the database tables.

So let’s get to work and see how we can use them.

What we want to do

Before starting to use migrations we first need to know what we want to create with them. As an example, let’s suppose we want to create a “users” table.

First, the configuration

In order to work with migrations we first need to set up the configuration. I will assume that you already have the database enabled.

After that open application/config/migration.php.

In there you will see the $config[‘migration_enabled’]. Set it to TRUE. For security reasons, it is good to leave it back to FALSE when you no longer do migrations.

If you have CodeIgniter 3 (and I hope you do, because most of my tutorials use CI3), you will also see $config[‘migration_type’]. I usually set this to ‘timestamp’, because this way I can see at what time a change was made. Don’t worry the ‘timestamp’ in this particular context is not really timestamp but a datetime format (‘YYYYMMDDHHIISS’).

The migration history (actually, the last migration) is kept inside a database table. By default the table is named ‘migrations’. You can change the name by modifying the value of $config[‘migration_table’].

$config[‘migration_auto_latest’] tells the framework to update the migration to the latest version.

You also have the possibility to set an initial migration point. If you know for sure that you won’t return beyond a migration version anymore, you can set the version at $config[‘migration_version’]. In our case the timestamp.

You also have $config[‘migration_path’] which tells CodeIgniter where to look for the migration files. If you don’t have a directory called “migrations” inside your application directory, create it now.

Our first migration

OK, so we want to create a table called “users” that has the following fields: “id”,”username”, “email”,”password”.

To do this we create a file named “20150722101900_create_users_table.php” inside “application/migrations”. “Why would we give it such a long name?” you might ask. Well… the first part is the timestamp that we’ve talked about earlier, while the second part is a short explanation of what the migration does. Isn’t this neat and clean?

Now, we open this file and right the basic start for the migration:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<?php

defined('BASEPATH')ORexit('No direct script access allowed');

classMigration_Create_users_tableextendsCI_Migration{

publicfunctionup()

{

}

publicfunctiondown()

{

}

}

As you can see, the class name must start with “Migration_” and be followed by the string that we’ve attached to the timestamp, starting with uppercase “Create_users_table”.

Every migration class has two methods: an up() method and a down() method. The up() method tells our application what to create when the migration is run. The down() method tells the application what to do if we want to undo this particular migration. In our case, when we run the migration we want that the “users” table to be created. In case we want to undo the migration, the down() function should delete the “users” table.

The Forge class

The Database Forge Class is the class that CodeIgniter uses by default to manage database operations (http://www.codeigniter.com/user_guide/database/forge.html). By using this class’ methods we can manipulate our tables in the migrations. So let’s create a constructor inside our migration to load the class:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

<?php

defined('BASEPATH')ORexit('No direct script access allowed');

classMigration_Create_users_tableextendsCI_Migration{

publicfunction__construct()

{

parent::__construct();

$this->load->dbforge();

}

publicfunctionup()

{

}

publicfunctiondown()

{

}

}

Now let’s start with the up() method.

The up() method

As I said before, the up() method is used when we run the migration. So let’s tell the method what we want it to do.

First of all we will tell it what fields we want to be created, by passing their schema in an array

So, we want an “id” that will be an int autoincremented, an username that will be a VARCHAR 60, an email that will be a VARCHAR 255, and a password that will be VARCHAR 255.

In “forge language”, that will translate into an array like the one below:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

$fields=array(

'id'=>array(

'type'=>'INT',

'constraint'=>11,

'unsigned'=>TRUE,

'auto_increment'=>TRUE

),

'username'=>array(

'type'=>'VARCHAR',

'constraint'=>60

),

'email'=>array(

'type'=>'VARCHAR',

'constraint'=>255

),

'password'=>array(

'type'=>'VARCHAR',

'constraint'=>255

)

);

Self explaining, right?

Now we simply pass the dbforge the fields by using the add_field() method:

PHP

1

$this->dbforge->add_field($fields);

We also tell the dbforge that we want the id to be used as primary key field:

PHP

1

$this->dbforge->add_key('id',TRUE);

If we also want to create index keys we use the same add_key() method but without passing the second parameter as TRUE (this parameter tells the dbforge class that we want it to be PRIMARY, and by default is set to FALSE).

PHP

1

$this->dbforge->add_key('username');

Now we only have to tell the dbforge to create the table:

PHP

1

$this->dbforge->create_table('users',TRUE);

The second paramater, if set to TRUE will do a “IF NOT EXISTS” condition before creating the table. If we don’t want to verify the existence of the table we simply don’t pass a second parameter.

The down() method

The down() method simply undo what the up() method did. In our case it will delete the table:

PHP

1

$this->dbforge->drop_table('users',TRUE);

Now let’s see our Migration file again:

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

<?php

defined('BASEPATH')ORexit('No direct script access allowed');

classMigration_Create_users_tableextendsCI_Migration{

publicfunction__construct()

{

parent::__construct();

$this->load->dbforge();

}

publicfunctionup()

{

$fields=array(

'id'=>array(

'type'=>'INT',

'constraint'=>11,

'unsigned'=>TRUE,

'auto_increment'=>TRUE

),

'username'=>array(

'type'=>'VARCHAR',

'constraint'=>60

),

'email'=>array(

'type'=>'VARCHAR',

'constraint'=>255

),

'password'=>array(

'type'=>'VARCHAR',

'constraint'=>255

)

);

$this->dbforge->add_field($fields);

$this->dbforge->add_key('id',TRUE);

$this->dbforge->add_key('username');

$this->dbforge->create_table('users',TRUE);

}

publicfunctiondown()

{

$this->dbforge->drop_table('users',TRUE);

}

}

Cool… Now let’s move on to the migration…

The controller that calls the migration

Once we’ve created the migration file we must run it. To do this, we have to create a controller that will call the migration. So let’s make a new file named “Migrate.php” inside“application/controllers”:

As you can see, we’ve created a method named do_migration(). This method can accept a parameter that in our case will represent the “timestamp”. If you pass it a timestamp, CodeIgniter will call that version of the migration. If the version called is before the current version CodeIgniter will undo all migrations after that version.

Also, I’ve created an undo_migration() method, and a reset_migration() method that will reset the migrations to the $config[‘migration_version’] set in the configuration file. These methods are actually components of my Matches CLI script, of which I will talk in the third part of this tutorial.

Now we visit the do_migration() method with our browser (I sure hope you know how to access a controller in CodeIgniter…). If everything went OK, you should see in your database a table named “users”.

Important note on security

As you can see this is a security hole, as anyone can access that controller from the internet. To avoid any complication I would advise you to enable the migrations only on the development environment (you can see here how to do it – Step 2: Set up the environments), or allow that controller to be run only by authenticated users.

In the next tutorials I will show you how to alter the tables by using migrations and how to use the Matches to simplify your work.

One final note

Please, PLEASE do read the fine manual on the CodeIgniter site. Is one of the best resources on the web:

I’m a bit confused by the “undo_migration()” method in your Migrate controller. Suppose I’ve just migrated to version 20150728100000, and I want to do a roll-back. Running undo_migration/20150728100000 results in an error message, as if it wants to run the migration again (the up method that is). When I run undo_migration/ with the previous timestamp, then 20150728100000 is getting rolled back. Is this how it is intended to work?