Databázové migrácie s Yii framework? Like a boss
Na poslednom WebElemente som spomínal databázové migrácie ako jednu z najlepších funkcií Yii frameworku.
Takisto si stále stojím za tým, že keď si na ne človek raz zvykne, nebude to chcieť robiť už nikdy inak. Poďme sa teda pozrieť ako to fičí.
Na tomto blogu mi chýbali kategórie. Nie je to nič zložité - jedna tabuľka s kategóriami a jeden nový stĺpec do tabuľky s článkami (jeden článok bude vždy v jednej kategórií).
Po starom by som to robil setom SQL príkazov, ktoré by som potom spustil alebo nejakým jednoduchým PHP skriptom, prípadne akciou v Controlleri. Možností je veľa.
Príkaz "migrate"
Yii framework má integrovaný príkazový riadok, ktorý pomáha pri generovaní kódu (shell
), prekladoch (message
), vytváraní úvodnej aplikácie (webapp
) a databázových migráciách (migrate
). Po jeho spustení, bez zadania konkrétneho príkazu dostaneme malú nápovedu.
vlado@pc:/cesta-k-projektu/protected$ ./yiic
Yii command runner (based on Yii v1.1.9)
Usage: ./yiic <command-name> [parameters...]
The following commands are available:
- message
- migrate
- shell
- webapp
To see individual command help, use the following:
./yiic help <command-name>
Nás budú dnes zaujímať len migrácie t.j. príkaz migrate
, ktorý spustíme.
vlado@pc:/cesta-k-projektu/protected$ ./yiic migrate
Yii Migration Tool v1.0 (based on Yii v1.1.9)
Creating migration history table "tbl_migration"...done.
No new migration found. Your system is up-to-date.
Systém nás informuje, že vytvoril tabuľku pre migrácie. Tabuľka má nasledovnú štruktúru. Pozrieť si ju môžte napríklad spustením SQL príkazu SHOW CREATE TABLE
tbl_migration``.
CREATE TABLE `tbl_migration` (
`version` varchar(255) NOT NULL,
`apply_time` int(11) DEFAULT NULL,
PRIMARY KEY (`version`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
Pozrime sa na zoznam všetkých dostupných príkazov.
vlado@pc:/cesta-k-projektu/protected$ ./yiic help migrate
USAGE
yiic migrate [action] [parameter]
DESCRIPTION
This command provides support for database migrations. The optional
'action' parameter specifies which specific migration task to perform.
It can take these values: up, down, to, create, history, new, mark.
If the 'action' parameter is not given, it defaults to 'up'.
Each action takes different parameters. Their usage can be found in
the following examples.
EXAMPLES
* yiic migrate
Applies ALL new migrations. This is equivalent to 'yiic migrate up'.
* yiic migrate create create_user_table
Creates a new migration named 'create_user_table'.
* yiic migrate up 3
Applies the next 3 new migrations.
* yiic migrate down
Reverts the last applied migration.
* yiic migrate down 3
Reverts the last 3 applied migrations.
* yiic migrate to 101129_185401
Migrates up or down to version 101129_185401.
* yiic migrate mark 101129_185401
Modifies the migration history up or down to version 101129_185401.
No actual migration will be performed.
* yiic migrate history
Shows all previously applied migration information.
* yiic migrate history 10
Shows the last 10 applied migrations.
* yiic migrate new
Shows all new migrations.
* yiic migrate new 10
Shows the next 10 migrations that have not been applied.
Vytvorím novú migráciu pre kategórie.
vlado@pc:/cesta-k-projektu/protected$ ./yiic migrate create blog_article_categories
Yii Migration Tool v1.0 (based on Yii v1.1.9)
Create new migration '/cesta-k-projektu/protected/migrations/m120501_115118_blog_article_categories.php'? [yes|no] yes
New migration created successfully.
Samozrejme, že som si želal vytvoriť i súbor, do ktorého teraz budem písať príkazy potrebné pre migráciu.
<?php
class m120501_115118_blog_article_categories extends CDbMigration
{
public function up()
{
}
public function down()
{
echo "m120501_115118_blog_article_categories does not support migration down.\n";
return false;
}
/*
// Use safeUp/safeDown to do migration with transaction
public function safeUp()
{
}
public function safeDown()
{
}
*/
}
Funkcie safeUp
a safeDown
slúžia na prevedenie potrebných príkazov transakčne. Tými sa pre jednoduchosť teraz zaoberať nebudem.
Migrácia pre novú tabuľku s kategóriami
Skúsim zhrnúť, čo všetko bude treba.
- Vytvorenie tabuľky
Category
a jej úpravy - Pridanie stĺpca do tabuľky
Article
, kde sú články - Nastavenie cudzieho kľúča
V časti up
vytvoríme tabuľku a nastavíme UNIQUE
kľuč na stĺpec url
. Pridáme stĺpec do tabuľky s článkami a vytvoríme na ňom INDEX
. Aby bolo možné nastaviť FOREIGN KEY
je nutné všetky články priradiť do nejakej kategórie. Vytvoríme teda kategóriu, prídáme do nej články, a nakoniec nastavíme spomínaný kľúč.
Majte na pamäti, že príkazy v operácii down
sa musia prevádzať presne v opačnom poradí (niečo ako Ctrl+Z
). Keby som najskôr zmazal tabuľku s kategóriami, tak vďaka ON DELETE CASCADE
by boli zmazané i všetky články.
Takže najskôr zmažeme FOREIGN KEY
, potom odstránime stĺpec categoryId
z tabuľky s článkami, a nakoniec môžeme odstrániť i tabuľku s kategóriami.
Výsledný kód potom vyzerá nasledovne.
<?php
class m120501_115118_blog_article_categories extends CDbMigration
{
public function up()
{
$this->execute("CREATE TABLE `Category` (
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
`name` varchar(100) COLLATE 'utf8_general_ci' NOT NULL,
`url` varchar(100) COLLATE 'utf8_general_ci' NOT NULL
) COMMENT='' ENGINE='InnoDB' COLLATE 'utf8_general_ci';");
$this->execute("ALTER TABLE `Category`ADD UNIQUE `url` (`url`);");
$this->execute("ALTER TABLE `Article` ADD `categoryId` int NOT NULL");
$this->execute("ALTER TABLE `Article` ADD INDEX `categoryId` (`categoryId`);");
$this->execute("INSERT INTO `Category` (`name`, `url`) VALUES ('Nezaradené', 'nezaradene');");
$this->execute("UPDATE `Article` SET `categoryId` = (SELECT `id` FROM `Category` WHERE `url` = 'nezaradene' LIMIT 1);");
$this->execute("ALTER TABLE `Article` ADD FOREIGN KEY (`categoryId`) REFERENCES `Category` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;");
return TRUE;
}
public function down()
{
$this->execute("ALTER TABLE `Article` DROP FOREIGN KEY `Article_ibfk_1`;");
$this->execute("ALTER TABLE `Article` DROP `categoryId`");
$this->execute("DROP TABLE `Category`;");
return TRUE;
}
}
Spustenie migrácie
Migráciu spustíme skriptom yiic
s príkazom migrate
a parametrom up
.
vlado@pc:/cesta-k-projektu/protected$ ./yiic migrate up
Yii Migration Tool v1.0 (based on Yii v1.1.9)
Total 1 new migration to be applied:
m120501_115118_blog_article_categories
Apply the above migration? [yes|no] yes
*** applying m120501_115118_blog_article_categories
*** applied m120501_115118_blog_article_categories (time: 0.034s)
Migrated up successfully.
Migrácia down
.
vlado@pc:/cesta-k-projektu/protected$ ./yiic migrate down
Yii Migration Tool v1.0 (based on Yii v1.1.9)
Total 1 migration to be reverted:
m120501_115118_blog_article_categories
Revert the above migration? [yes|no] yes
*** reverting m120501_115118_blog_article_categories
*** reverted m120501_115118_blog_article_categories (time: 0.019s)
Migrated down successfully.
Aby ste predišli problémom je fajn si migrácie up
aj down
otestovať niekoľko krát, aby ste si boli istí, že všeto funguje ako má.
Záver
Toto, samozrejme, nie je jediná možnosť ako pracovať s migráciami. Yii ponúka i veľa metód v triede CDbMigration
, ktoré vám umožnia robiť úpravy na databáze jednoduchšie. Spomeniem napr. createTable
pre vytvorenie tabuľky, createIndex
pre vytvorenie indexu, delete
pre príkaz zmazanie, insert
pre vloženie, addForeignKey
pre cudzie kľúče a podobne.
Čo sa ešte dá?
- vypnúť interaktívny mód
- nastaviť vlastnú šablónu pre súbor s migráciou
- zmeniť tabuľku, kam sa migrácie ukladajú
Pracujete s frameworkom, ktorý vie migrácie? Podeľte sa!