Alexander Makarov
12 years ago
1 changed files with 316 additions and 0 deletions
@ -1,3 +1,319 @@
|
||||
Database Migration |
||||
================== |
||||
|
||||
Like source code, the structure of a database is evolving as we develop and maintain |
||||
a database-driven application. For example, during development, we may want to |
||||
add a new table; or after the application is put into production, we may realize |
||||
the need of adding an index on a column. It is important to keep track of these |
||||
structural database changes (called **migration**) like we do with our source |
||||
code. If the source code and the database are out of sync, it is very likely |
||||
the whole system may break. For this reason, Yii provides a database migration |
||||
tool that can keep track of database migration history, apply new migrations, |
||||
or revert existing ones. |
||||
|
||||
The following steps show how we can use database migration during development: |
||||
|
||||
1. Tim creates a new migration (e.g. create a new table) |
||||
2. Tim commits the new migration into source control system (e.g. GIT, Mercurial) |
||||
3. Doug updates from source control system and receives the new migration |
||||
4. Doug applies the migration to his local development database |
||||
|
||||
|
||||
Yii supports database migration via the `yiic migrate` command line tool. This |
||||
tool supports creating new migrations, applying/reverting/redoing migrations, and |
||||
showing migration history and new migrations. |
||||
|
||||
Creating Migrations |
||||
------------------- |
||||
|
||||
To create a new migration (e.g. create a news table), we run the following command: |
||||
|
||||
~~~ |
||||
yiic migrate/create <name> |
||||
~~~ |
||||
|
||||
The required `name` parameter specifies a very brief description of the migration |
||||
(e.g. `create_news_table`). As we will show in the following, the `name` parameter |
||||
is used as part of a PHP class name. Therefore, it should only contain letters, |
||||
digits and/or underscore characters. |
||||
|
||||
~~~ |
||||
yiic migrate/create create_news_table |
||||
~~~ |
||||
|
||||
The above command will create under the `protected/migrations` directory a new |
||||
file named `m101129_185401_create_news_table.php` which contains the following |
||||
initial code: |
||||
|
||||
~~~ |
||||
[php] |
||||
class m101129_185401_create_news_table extends \yii\db\Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
echo "m101129_185401_create_news_table cannot be reverted.\n"; |
||||
return false; |
||||
} |
||||
} |
||||
~~~ |
||||
|
||||
Notice that the class name is the same as the file name which is of the pattern |
||||
`m<timestamp>_<name>`, where `<timestamp>` refers to the UTC timestamp (in the |
||||
format of `yymmdd_hhmmss`) when the migration is created, and `<name>` is taken |
||||
from the command's `name` parameter. |
||||
|
||||
The `up()` method should contain the code implementing the actual database |
||||
migration, while the `down()` method may contain the code reverting what is |
||||
done in `up()`. |
||||
|
||||
Sometimes, it is impossible to implement `down()`. For example, if we delete |
||||
table rows in `up()`, we will not be able to recover them in `down()`. In this |
||||
case, the migration is called irreversible, meaning we cannot roll back to |
||||
a previous state of the database. In the above generated code, the `down()` |
||||
method returns `false` to indicate that the migration cannot be reverted. |
||||
|
||||
As an example, let's show the migration about creating a news table. |
||||
|
||||
~~~ |
||||
[php] |
||||
class m101129_185401_create_news_table extends \yii\db\Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
$this->db->createCommand()->createTable('tbl_news, array( |
||||
'id' => 'pk', |
||||
'title' => 'string NOT NULL', |
||||
'content' => 'text', |
||||
))->execute(); |
||||
} |
||||
|
||||
public function down() |
||||
{ |
||||
$this->db->createCommand()->dropTable('tbl_news')->execute(); |
||||
} |
||||
} |
||||
~~~ |
||||
|
||||
The base class [\yii\db\Migration] exposes a database connection via `db` |
||||
property. You can use it for manipulating data and schema of a database. |
||||
|
||||
Transactional Migrations |
||||
------------------------ |
||||
|
||||
While performing complex DB migrations, we usually want to make sure that each |
||||
migration succeed or fail as a whole so that the database maintains the |
||||
consistency and integrity. In order to achieve this goal, we can exploit |
||||
DB transactions. |
||||
|
||||
We could explicitly start a DB transaction and enclose the rest of the DB-related |
||||
code within the transaction, like the following: |
||||
|
||||
~~~ |
||||
[php] |
||||
class m101129_185401_create_news_table extends \yii\db\Migration |
||||
{ |
||||
public function up() |
||||
{ |
||||
$transaction=$this->getDbConnection()->beginTransaction(); |
||||
try |
||||
{ |
||||
$this->db->createCommand()->createTable('tbl_news, array( |
||||
'id' => 'pk', |
||||
'title' => 'string NOT NULL', |
||||
'content' => 'text', |
||||
))->execute(); |
||||
$transaction->commit(); |
||||
} |
||||
catch(Exception $e) |
||||
{ |
||||
echo "Exception: ".$e->getMessage()."\n"; |
||||
$transaction->rollback(); |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
// ...similar code for down() |
||||
} |
||||
~~~ |
||||
|
||||
> Note: Not all DBMS support transactions. And some DB queries cannot be put |
||||
> into a transaction. In this case, you will have to implement `up()` and |
||||
> `down()`, instead. And for MySQL, some SQL statements may cause |
||||
> [implicit commit](http://dev.mysql.com/doc/refman/5.1/en/implicit-commit.html). |
||||
|
||||
|
||||
Applying Migrations |
||||
------------------- |
||||
|
||||
To apply all available new migrations (i.e., make the local database up-to-date), |
||||
run the following command: |
||||
|
||||
~~~ |
||||
yiic migrate |
||||
~~~ |
||||
|
||||
The command will show the list of all new migrations. If you confirm to apply |
||||
the migrations, it will run the `up()` method in every new migration class, one |
||||
after another, in the order of the timestamp value in the class name. |
||||
|
||||
After applying a migration, the migration tool will keep a record in a database |
||||
table named `tbl_migration`. This allows the tool to identify which migrations |
||||
have been applied and which are not. If the `tbl_migration` table does not exist, |
||||
the tool will automatically create it in the database specified by the `db` |
||||
application component. |
||||
|
||||
Sometimes, we may only want to apply one or a few new migrations. We can use the |
||||
following command: |
||||
|
||||
~~~ |
||||
yiic migrate/up 3 |
||||
~~~ |
||||
|
||||
This command will apply the 3 new migrations. Changing the value 3 will allow |
||||
us to change the number of migrations to be applied. |
||||
|
||||
We can also migrate the database to a specific version with the following command: |
||||
|
||||
~~~ |
||||
yiic migrate/to 101129_185401 |
||||
~~~ |
||||
|
||||
That is, we use the timestamp part of a migration name to specify the version |
||||
that we want to migrate the database to. If there are multiple migrations between |
||||
the last applied migration and the specified migration, all these migrations |
||||
will be applied. If the specified migration has been applied before, then all |
||||
migrations applied after it will be reverted (to be described in the next section). |
||||
|
||||
|
||||
Reverting Migrations |
||||
-------------------- |
||||
|
||||
To revert the last one or several applied migrations, we can use the following |
||||
command: |
||||
|
||||
~~~ |
||||
yiic migrate/down [step] |
||||
~~~ |
||||
|
||||
where the optional `step` parameter specifies how many migrations to be reverted |
||||
back. It defaults to 1, meaning reverting back the last applied migration. |
||||
|
||||
As we described before, not all migrations can be reverted. Trying to revert |
||||
such migrations will throw an exception and stop the whole reverting process. |
||||
|
||||
|
||||
Redoing Migrations |
||||
------------------ |
||||
|
||||
Redoing migrations means first reverting and then applying the specified migrations. |
||||
This can be done with the following command: |
||||
|
||||
~~~ |
||||
yiic migrate/redo [step] |
||||
~~~ |
||||
|
||||
where the optional `step` parameter specifies how many migrations to be redone. |
||||
It defaults to 1, meaning redoing the last migration. |
||||
|
||||
|
||||
Showing Migration Information |
||||
----------------------------- |
||||
|
||||
Besides applying and reverting migrations, the migration tool can also display |
||||
the migration history and the new migrations to be applied. |
||||
|
||||
~~~ |
||||
yiic migrate/history [limit] |
||||
yiic migrate/new [limit] |
||||
~~~ |
||||
|
||||
where the optional parameter `limit` specifies the number of migrations to be |
||||
displayed. If `limit` is not specified, all available migrations will be displayed. |
||||
|
||||
The first command shows the migrations that have been applied, while the second |
||||
command shows the migrations that have not been applied. |
||||
|
||||
|
||||
Modifying Migration History |
||||
--------------------------- |
||||
|
||||
Sometimes, we may want to modify the migration history to a specific migration |
||||
version without actually applying or reverting the relevant migrations. This |
||||
often happens when developing a new migration. We can use the following command |
||||
to achieve this goal. |
||||
|
||||
~~~ |
||||
yiic migrate/mark 101129_185401 |
||||
~~~ |
||||
|
||||
This command is very similar to `yiic migrate/to` command, except that it only |
||||
modifies the migration history table to the specified version without applying |
||||
or reverting the migrations. |
||||
|
||||
|
||||
Customizing Migration Command |
||||
----------------------------- |
||||
|
||||
There are several ways to customize the migration command. |
||||
|
||||
### Use Command Line Options |
||||
|
||||
The migration command comes with four options that can be specified in command |
||||
line: |
||||
|
||||
* `interactive`: boolean, specifies whether to perform migrations in an |
||||
interactive mode. Defaults to true, meaning the user will be prompted when |
||||
performing a specific migration. You may set this to false should the |
||||
migrations be done in a background process. |
||||
|
||||
* `migrationPath`: string, specifies the directory storing all migration class |
||||
files. This must be specified in terms of a path alias, and the corresponding |
||||
directory must exist. If not specified, it will use the `migrations` |
||||
sub-directory under the application base path. |
||||
|
||||
* `migrationTable`: string, specifies the name of the database table for storing |
||||
migration history information. It defaults to `tbl_migration`. The table |
||||
structure is `version varchar(255) primary key, apply_time integer`. |
||||
|
||||
* `connectionID`: string, specifies the ID of the database application component. |
||||
Defaults to 'db'. |
||||
|
||||
* `templateFile`: string, specifies the path of the file to be served as the code |
||||
template for generating the migration classes. This must be specified in terms |
||||
of a path alias (e.g. `application.migrations.template`). If not set, an |
||||
internal template will be used. Inside the template, the token `{ClassName}` |
||||
will be replaced with the actual migration class name. |
||||
|
||||
To specify these options, execute the migrate command using the following format |
||||
|
||||
~~~ |
||||
yiic migrate/up --option1=value1 --option2=value2 ... |
||||
~~~ |
||||
|
||||
For example, if we want to migrate for a `forum` module whose migration files |
||||
are located within the module's `migrations` directory, we can use the following |
||||
command: |
||||
|
||||
~~~ |
||||
yiic migrate/up --migrationPath=ext.forum.migrations |
||||
~~~ |
||||
|
||||
|
||||
### Configure Command Globally |
||||
|
||||
While command line options allow us to configure the migration command |
||||
on-the-fly, sometimes we may want to configure the command once for all. |
||||
For example, we may want to use a different table to store the migration history, |
||||
or we may want to use a customized migration template. We can do so by modifying |
||||
the console application's configuration file like the following, |
||||
|
||||
```php |
||||
TBD |
||||
``` |
||||
|
||||
Now if we run the `migrate` command, the above configurations will take effect |
||||
without requiring us to enter the command line options every time. |
||||
|
Loading…
Reference in new issue