CakePHP Migrations Plugin

CakePHP Migrations Plugin

Downloads:
160
Version:
1.0.0.0
Released on:
2010-01-11
Issue Tracker
 
Changelog
 

Subscribe

CakePHP Migrations Plugin

The Migrations plugin provides a comprehensive management system whereby the database schema for a CakePHP project can fluctuate during development involving any number of developers. This is achieved by incrementally managing database changes, providing customisable hooks and callbacks for data migration and changes to meet migration paths, and migration maps to provide flexible application of migration instances.

Migrations is great for any development team, or individual that wants to manage database schema and data changes throughout the development lifetime of a project.

Requirements

CakePHP 1.3 or higher.

If you need a version to use with CakePHP 1.2 please contact us directly.

Related Articles

Documentation

Installation

Extract the migrations plugin to the app/plugins directory in your application.

The standard directory name is migrations.

Directory Structure


/app
    /plugins
        /datasources
        /debug_kit
        /localized
        /migrations   (Same location as other plugins)

For more information on plugins and app structure, please see the Plugins section of the CakePHP Book.

Generating Your First Migration

The first step to adding migrations to an existing database is to import the database's structure into a format the migrations can work with. Namely a migration file. To create the _first_ migration file run the following command:

 cake migration generate 

This will give us the following prompt:


Cake Migration Shell
---------------------------------------------------------------
Please enter the descriptive name of the migration to generate:  
>

Enter in 001 initial migration as the name for the migration and hit return. Since this is the first time you ran this command you will then get the following prompt:


Do you wanna generate a dump from current database? (y/n) 
[y] >

Answer y to this question as we want to import the existing database schema into our migrations. This will scan the model paths and import schema for each model found. Once you have been returned to the prompt check app/config/migrations/ for the newly created files. You should see two files in the migrations directory. The first 001_initial_migration is the actual migration containing the table definitions for your database. The second is


Cake Migration Shell
---------------------------------------------------------------
Please enter the descriptive name of the migration to generate:  
>
0, this is a special Map file which keeps track of the order of migrations are to be run in. At this point you now have the current database imported into the migration system and can do further schema changes in a regular fashion.

Then the following output will be give as result:


Generating dump from current database...
Generating Migration...
Mapping Migrations...

Done.

Note: If you want import all tables regardless if it has a model or not you can use


Cake Migration Shell
---------------------------------------------------------------
Please enter the descriptive name of the migration to generate:  
>
1 (force) parameter while running the command:

 cake migration generate -f 

Generating Schema Diffs

Once you have Generated your first Migration you will probably do more changes to your database. To simplify the generation of new migration you can do Schema Diffs. To this, you need to follow the steps:

  1. Generate your first Migration (if haven't generated yet)
  2. Generate a schema file
  3. Do changes to your database
  4. Generate a new migration file

1. Generate your first Migration

As you know, this is the very first step when you install the migrations plugin, for this section read Generating your first Migration

2. Generate a schema file

There is impossible to generate schema diffs across migrations file. So you need to generate a CakeSchema file doing:


	cake schema generate

Doing it you will save the current database state.

3. Do changes to your database

Now you can do whatever changes you want to do.

4. Generate a new migration file

Since you Generate a schema file and did some changes to your database, you will be able to compare the schema file to your new database state. By doing it, you will generate a migration file that only contains the difference between the two states.

To generate a new migration file doing this comparison you do:


	cake migration generate

This will give us the following prompt:


	Cake Migration Shell
	---------------------------------------------------------------
	Please enter the descriptive name of the migration to generate:  
	>

Enter in 002 some changes to the database as the name for the migration and hit return. Since there is a schema.php file present in your environment you will then get the following prompt:


	Do you wanna compare the schema.php file to the database? (y/n) 
	[y] >

Answer y to this question as we want to compare the previous database state to the new database state. A new file on 002_some_changes_to_the_database.php, named 002_some_changes_to_the_database.php, will be created containing only the difference between the two states.

Then the following output will be give as result:


	Comparing schema.php to the database...
	Generating Migration...
	Mapping Migrations...

	Done.

Generating a New Migration

Once you Generated your first Migration and don't want or can't you can generate a regular migration.

Generating migrations is done with the Console.

 cake migration generate 

This will present an interactive console where you can specify the name of the migration, and confirm the schema generation.


Welcome to CakePHP v1.3.0-alpha Console
---------------------------------------------------------------
App : samplemigrations
Path: /samplemigrations
---------------------------------------------------------------
Cake Migration Shell
---------------------------------------------------------------
Please enter the descriptive name of the migration to generate:  
> migration title                                          
Do you wanna generate a dump from current database? (y/n) 
[y] > 
---------------------------------------------------------------
Generating Migration...
Mapping Migrations...

Done.

Map Files

Migrations generates two types of files.

The first is the map.php file, which keeps track of the migrations available, and the order in which they should be processed.

The second is the various migration instances themselves. Depending on the name you choose to give a migration, this filename will change.

map.php


<?php
$map = array(
    1 => array(
        'initial_schema' => 'M4b040dc7945c4db1b83f26347ff3ec9a'),
);
?>

The map.php file lists all the existing migrations for an application / plugin and is a map between their "public" file name and their "internal" class name.

Migrations are ordered by their creation date, so running cake migration generate will create a migration file placed at the end of the list.

Reordering migrations with the map file

Since the migration map file contains the order migrations are to be run in, reordering migrations in the map file will change the order in which they are executed. Take the following:


<?php
$map = array(
    1 => array(
        'initial_schema' => 'M4b040dc7945c4db1b83f26347ff3ec9a'),
	2 => array(
        'added_comment_count' => 'M4b040dc7945c4db1b83f26347fbb1a'),
	3 => array(
        'added_uploads_table' => 'M5b060dd7846c4cc1a53f26148fa11b'),
);
?>

Now if we wanted to reverse the order the 2nd and 3rd migration are to be run in, we can just re-order the items in the array.


<?php
$map = array(
    1 => array(
		'initial_schema' => 'M4b040dc7945c4db1b83f26347ff3ec9a'),
	2 	=> array(
		'added_uploads_table' => 'M5b060dd7846c4cc1a53f26148fa11b'),
	3 => array(
		'added_comment_count' => 'M4b040dc7945c4db1b83f26347fbb1a'),
);
?>

The order the migrations will be run in always reflects the order of elements in the $map.

Migration Files


<?php
class M4b040dc7945c4db1b83f26347ff3ec9a extends CakeMigration {
	
/**
 * Migration description
 *
 * @var string
 * @access public
 */
    var $description = '';
	
/**
 * Actions to be performed
 *
 * @var array $migration
 * @access public
 */
    var $migration = array(
        'up' => array(
            'create_table' => array(
                'ingredients' => array(
                    'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
                    'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100),
                    'indexes' => array(
                        'PRIMARY' => array('column' => 'id', 'unique' => 1),
                    ),
                    'tableParameters' => array('charset' => 'latin1', 'collate' => 'latin1_swedish_ci', 'engine' => 'MyISAM'),
                ),
                'ingredients_recipes' => array(
                    'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
                    'ingredient_id' => array('type' => 'integer', 'null' => false, 'default' => NULL),
                    'recipe_id' => array('type' => 'integer', 'null' => false, 'default' => NULL),
                    'indexes' => array(
                        'PRIMARY' => array('column' => 'id', 'unique' => 1),
                    ),
                    'tableParameters' => array('charset' => 'latin1', 'collate' => 'latin1_swedish_ci', 'engine' => 'MyISAM'),
                ),
                'recipes' => array(
                    'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
                    'user_id' => array('type' => 'integer', 'null' => false, 'default' => NULL),
                    'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100),
                    'content' => array('type' => 'text', 'null' => false, 'default' => NULL),
                    'created' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
                    'modified' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
                    'indexes' => array(
                        'PRIMARY' => array('column' => 'id', 'unique' => 1),
                    ),
                    'tableParameters' => array('charset' => 'latin1', 'collate' => 'latin1_swedish_ci', 'engine' => 'MyISAM'),
                ),
                'users' => array(
                    'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
                    'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 100),
                    'password' => array('type' => 'string', 'null' => false, 'default' => NULL),
                    'created' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
                    'modified' => array('type' => 'datetime', 'null' => false, 'default' => NULL),
                    'indexes' => array(
                        'PRIMARY' => array('column' => 'id', 'unique' => 1),
                    ),
                    'tableParameters' => array('charset' => 'latin1', 'collate' => 'latin1_swedish_ci', 'engine' => 'MyISAM'),
                ),
            ),
        ),
        'down' => array(
            'drop_table' => array(
                'ingredients', 'ingredients_recipes', 'recipes', 'users'
            ),
        ),
    );
	
/**
 * Before migration callback
 *
 * @param string $direction, up or down direction of migration process
 * @return boolean Should process continue
 * @access public
 */
    function before($direction) {
        return true;
    }
	
/**
 * After migration callback
 *
 * @param string $direction, up or down direction of migration process
 * @return boolean Should process continue
 * @access public
 */
    function after($direction) {
        return true;
    }
}
?>

This migration file contains all the information describing a migration.

$migration variable

The $migration variable contains two arrays describing actions to perform.

These actions are split into the directions the migration can operate:


<?php
$map = array(
    1 => array(
        'initial_schema' => 'M4b040dc7945c4db1b83f26347ff3ec9a'),
);
?>
0 or before().

Each set of actions describes schema changes that will be executed either when upgrading to this version or when downgrading from this version.

$description variable

The before() variable contains a readable description of the migration operations.

Callbacks

There are two callbacks that can be implemented: before() and before(). These are, as you would expect, called before and after each migration.

The passed parameter before() indicates whether the migration was before() or before(). You can use the value this variable to perform different actions on each callback, based on the direction the migration is going.

Callbacks are an ideal place to populate tables or fields with initial data, or to perform data transformations as fields and tables are adjusted through the lifetime of a migration set. Callbacks must return before() in order for the migration to run. If either callback returns a non before() value the migration will be considered as failed. Returning before() from the before() callback is useful when you want to abort the migration based on some conditions, like the ability of the migration to maintain consistent data.

Directives

Introduction

Directives are the key-values that are used in migrations array files to achieve various actions as the migrations run.

These directives are the key to getting the desired results from your migration files.

Directive - Create Table

Details

Name: create_table

Format: array of Tables

Description

Create table is used for the creation of new tables in your database.

Note that migrations will generate errors if the specified table already exists in the database.

Directives exist (Drop, Rename) to deal with existing tables before proceeding with table creation.

Example


'create_table' => array(
	'categories' => array(
		'id' => array(
			'type'    =>'string',
			'null'    => false,
			'default' => NULL,
			'length'  => 36,
			'key'     => 'primary'),
		'name' => array(
			'type'    =>'string',
			'null'    => false,
			'default' => NULL),
		'indexes' => array(
			'PRIMARY' => array(
				'column' => 'id',
				'unique' => 1)
		)
	),
	'emails' => array(
		'id' => array(
			'type'    => 'string',
			'length ' => 36,
			'null'    => false,
			'key'     => 'primary'),
		'data' => array(
			'type'    => 'text',
			'null'    => false,
			'default' => NULL),
		'sent' => array(
			'type'    => 'boolean',
			'null'    => false,
			'default' => '0'),
		'error' => array(
			'type'    => 'text',
			'default' => NULL),
		'created' => array(
			'type' => 'datetime'),
		'modified' => array(
			'type' => 'datetime'),
		'indexes' => array(
			'PRIMARY' => array(
				'column' => 'id',
				'unique' => 1)
		)
	)
)

Directive - Drop Table

Details

Name: drop_table

Format: array of table names

Description'

Drop table is used for removing tables from the schema.

Directives exist Create, Rename to handle other table based migration operations.

Example


'drop_table' => array(
	'categories',
	'emails'
)

Directive - Rename Table

Details

Name: rename_table

Format: array of table names as keys, and new names as values

Description

Changes the name of a table in the database.

Directives exist (Create, Drop) to handle creation and deletion of tables.

Example


'rename_table' => array(
	'categories' => 'groups',
	'emails' => 'email_addresses'
)

After the successful execution of the above rename_table command, the following will have occurred:

  1. categories table will be renamed to groups
  2. emails table will be renamed to email_addresses

Directive - Create Field

Details

Name: create_field

Format: array of [Migration Fields](/cakedc/migrations/wiki/migration-fields)

Description

Create Field is used to add fields to an existing table in the schema.

Note that migrations will generate errors if the specified field already exists in the table.

Directives exist (Drop, Rename, Alter) to deal with existing fields before proceeding with field addition.

Example


'create_field' => array(
	'categories' => array(
		'created' => array(
			'type' => 'datetime'),
		'modified' => array(
			'type' => 'datetime')
	)
)

This will result in the addition of a created and modified field to the categories table.

Directive - Drop Field

Details

Name: drop_field

Format: array of table names with field arrays

Description

Drop field is used for removing fields from existing tables in the schema.

Directives exist (Create, Rename, Alter) to handle other field based migration operations.

Example


'drop_field' => array(
	'categories' => array(
		'created',
		'modified'),
	'emails' => array(
		'error')
)

This example shows the removal of the created and modified fields from the categories table, and the removal of the error field from the emails table.

Directive - Alter Field

Details

Name: alter_field

Format: array of partial Migration Table specifications.

Description

Changes the field properties in an existing table.

Note that partial table specifications are passed, which is a subset of a full array of Table data.

These are the fields that are to be modified as part of the operation.

If you wish to leave some fields untouched, simply exclude them from the Table spec for the alter operation.

Directives exist (Create, Drop, Rename) to handle other field operations.

Example


'alter_field' => array(
	'categories' => array(
		'indexes' => array(
			'NAMES' => false,
			'NAME' => array(
				'column' => 'name',
				'unique' => 0),
		)
	)
),

This changes the indexes on the categories table.

Directive - Rename Field

Details

Name: rename_field

Format: array of table and field names as keys, and new names as values

Description

Changes the name of a field on a specified table in the database.

Directives exist (Create, Drop, Alter) to handle creation and deletion of fields.

Example


'rename_field' => array(
	'categories' => array(
		'name' => 'title'
	),
	'emails' => array(
		'error' => 'error_code',
		'modified' => 'updated'
	),
)

After the successful execution of the above rename_field directive, the following will have occurred:

  1. name field on the categories table will be renamed to title
  2. error field on the emails table will be renamed to error_code
  3. modified field on the emails table will be renamed to updated

Console

The Migrations console is the interface into the migrations structure in terms of running, generating and managing your migrations.

There are three commands available that provide all of the migrations functionality:

  1. Help
  2. Generate
  3. Run

Console - Help

The help command displays useful help information to provide assistance in using migrations.

At time of writing, the available help text was the following:


The Migration database management for CakePHP
---------------------------------------------------------------
Usage: cake migration <command> <param1> <param2>...
---------------------------------------------------------------
Params:
	-connection <config>
		Set db config <config>. Uses 'default' if none is specified.

	-plugin
		Plugin name to be used

	-f
		Force 'generate' to compare all tables.

Commands:
	migration help
		Shows this help message.

	migration run <up|down|all|reset|version>
		Run a migration to given direction or version.
		Provide a version number to get directly to the version.
		You can also use all to apply all migrations or reset to unapply all.

	migration <generate|add>
		Generates a migration file.
		To force generation of all tables when making a comparison/dump, use the -f param.

Console - Generate

Generate a new migration

 cake migration generate 

Force migration generation

This is used in the case that you want to generate data for tables without an existing CakePHP model available

 cake migration generate -f 

The -f switch means "force"

Specifying connections

If you wish to use a data source other than that defined on the default connection, specify it with the -connection <name> option:

 cake migration generate -connection test 

This will inspect the connection named test in your app/config/database.php configuration file, and generate migrations based on the information obtained from the datasource.

Console - Run

After generating or being supplied a set of migrations, you can process them to change the state of your database.

This is the crux of the migrations plugin, allowing migration of schemas up and down the migration chain, offering flexibility and easy management of your schema and data states.

Resetting migrations

The reset subcommand of run allows you to reset your schema back to a fresh installation.

 cake migration run reset 

You can also execute this through the interactive console.

This is initiated by running the migration shell with no commands:

 cake migration 

You are immediately shown a list of all migrations, and the date applied, if applied.

You can choose the

 cake migration 
0 options to "clean" the schema.

Downgrade

To revert your database schema to the version previous to the current state, migrate down:

 cake migration down 

Upgrade

Upgrade your database to the next available migration with the following command:

 cake migration up 

Both upgrade and downgrade operations provide output to indicate the process and result of the migration operation.

Any issues will be reported to you in the console.

Migrations for plugins

To apply a migration that is within a plugin, add the

 cake migration 
1 switch to your command:


cake migration reset -plugin users
cake migration up -plugin users
cake migration down -plugin users