Hinweis: Das Feature zur Datenbankmigration ist seit Verison 1.1.6 verfügbar.
Genause wie der Quelltext einer Anwendung verändert sich auch die Struktur der zugehörigen Datenbank während der Entwicklung. So muss z.B. irgendwann eine Tabelle hinzugefügt werden. Oder nachdem die Anwendung produktiv geschaltet wurde, stellt man fest, dass eine Spalte noch dringend einen Index benötigt. Es ist daher genauso wichtig wie beim Quelltext, dass auch die strukturellen Änderungen in der Datenbank festgehalten werden (man spricht hier auch von Migration). Wenn Programmcode und Datenbank nicht mehr zueinander passen, ist ein Fehlverhalten praktisch vorprogrammiert. Yii enthält daher ein Tool zur Datenbankmigration, dass eine Historie von Migrationen verwalten, neue Migrationen durchführen oder existierende rückgangig machen kann.
Der folgende Ablauf soll verdeutlichen, wie Datenbanmigration bei der Entwicklung eingesetzt wird:
Migrationen werden in Yii über den Kommandozeilenbefehl yiic migrate
verwaltet. Mit diesem Befehl können neue Migrationen erstellt, angewendet,
zurückgenommen oder wiederholt werden. Man kann auch die Historie aller
Migrationen oder neue Migrationen anzeigen lassen.
Hinweis: Es wird empfohlen, den
migrate
-Befehl mit der yiic-Version im Applikationspfad (z.B.cd pfad/zu/protected
) statt derjenigen im Framework-Verzeichnis auszuführen. Stellen Sie sicher, dass dasprotected/migrations
-Verzeichnis existiert und beschreibbar ist. Prüfen Sie außerdem, ob Sie eine Datenbankverbindung inprotected/config/console.php
angegeben haben.
Um eine neu Migration (z.B. zum Anlegen einer News-Tabelle) zu erzeugen, verwendet man folgenden Befehl:
yiic migrate create <name>
Der Parameter name
dient dazu, die Migration kurz und knapp zu beschreiben
(z.B. erstelle_news_tabellle
). Wie wir weiter unten zeigen, wird dieser
Name Bestandteil eines PHP-Klassennamens werden. Er sollte daher nur
Buchstaben, Zahlen und/oder Unterstriche enthalten.
yiic migrate create erstelle_news_tabelle
Damit wird im Verzeichnis protected/migrations
eine Datei
m101129-185401_erstelle_news_tabelle.php
mit diesem Inhalt erzeugt:
class m101129_185401_erstelle_news_tabelle extends CDbMigration
{
public function up()
{
}
public function down()
{
echo "m101129_185401_erstelle_news_tabelle unterstützt migration down nicht.\n";
return false;
}
/*
// Falls Transaktionen benötigt werden, kann stattdessen safeUp/safeDown implementiert werden
public function safeUp()
{
}
public function safeDown()
{
}
*/
}
Beachten Sie, dass der Klassenname dem Dateinamen entspricht und dem Muster
m<zeitstempel>_<name>
folgt, wobei <zeitstempel>
für den UTC-Zeitstempel (im
Format JJMMTT_HHMMSS
) des Erstellzeitpunkts und <name>
für den obigen
Migrationsnamen steht.
Die up()
-Methode sollte den Code für die eigentliche Migration enthalten,
down()
den Code, um die Änderung rückgängig zu machen.
Manchmal kommt es vor, dass eine down()
-Methode nicht möglich ist, zum
Beispiel wenn in up()
einige Tabellenzeilen gelöscht wurden. In diesem Fall
wird die Migration irreversibel genannt. Man kann sie also nicht rückgängig
machen und die Datenbank in einen früheren Zustand zurückfahren. Im
generierten Code oben liefert die down()
-Methode false
zurück um
dies anzuzeigen.
Info: Seit Version 1.1.7 werden alle folgenden Migrationen abgebrochen, falls
up()
oderdown()
den Wertfalse
zurückgeben. In Version 1.1.6 musste man dazu noch eine Exception werfen.
Das folgende Beispiel zeigt die Migration zum Erstellen einer News-Tabelle.
class m101129_185401_erstelle_news_tabelle extends CDbMigration
{
public function up()
{
$this->createTable('tbl_news', array(
'id' => 'pk',
'title' => 'string NOT NULL',
'content' => 'text',
));
}
public function down()
{
$this->dropTable('tbl_news');
}
}
Die Basisklasse CDbMigration stellt einige Methoden zum Manipulieren von
Daten und DB-Schemas bereit. Mit CDbMigration::createTable kann man zum
Beispiel eine Tabelle erzeugen, während CDbMigration::insert Zeilen in eine
Tabelle einfügt. Alle Methoden verwenden die Datenbankverbindung, die von
CDbMigration::getDbConnection() zurückgeliefert wird. Standardmäßig ist das
Yii::app()->db
.
Info: Sie werden feststellen, dass die DB-Methoden von CDbMigration denen von CDbCommand sehr ähneln. Sie sind tatsächlich auch fast identisch, außer, dass bei den CDbMigration-Methoden zusätzlich die Zeit gemessen, sowie einige Informationen über die Parameter ausgegeben werden.
Info: Dieses Feature steht seit Version 1.1.7 zur Verfügung.
Werden komplexe Migrationen auf einer Datenbank ausgeführt, möchte man für gewöhnlich sicherstellen, dass jede einzelne Migration erfolgreich war. Falls nicht, sollen alle Migrationen abgebrochen werden, um die Datenbank in einem konsistenten Zustand zu hinterlassen. Zu diesem Zweck kann man DB-Transaktionen einsetzen.
Man könnte hierzu explizit eine Transaktion starten und sämtlichen DB-bezogenen Code innerhalb dieser Transaktion ausführen:
class m101129_185401_erstelle_news_tabelle extends CDbMigration
{
public function up()
{
$transaction=$this->getDbConnection()->beginTransaction();
try
{
$this->createTable('tbl_news', array(
'id' => 'pk',
'title' => 'string NOT NULL',
'content' => 'text',
));
$transaction->commit();
}
catch(Exception $e)
{
echo "Exception: ".$e->getMessage()."\n";
$transaction->rollback();
return false;
}
}
// ...ähnlicher Code für down()
}
Stattdessen kann man aber auch einfach die Funktionen safeUp()
statt up()
und safeDown()
statt down()
implementieren:
class m101129_185401_erstelle_news_tabelle extends CDbMigration
{
public function safeUp()
{
$this->createTable('tbl_news', array(
'id' => 'pk',
'title' => 'string NOT NULL',
'content' => 'text',
));
}
public function safeDown()
{
$this->dropTable('tbl_news');
}
}
Wenn Yii die Migrationen durchführt, startet es automatisch eine
DB-Transaktion und führt danach safeUp()
oder safeDown()
aus. Falls diese
Methoden einen Datenbankfehler verursachen, wird die Transaktion
zurückgefahren. Damit wird gewährleistet, dass die Datenbank in einem
definierten Status verbleibt.
Hinweis: Nicht alle DBMS unterstützen Transaktionen. Einige DB-Befehle können auch gar nicht in Transaktionen gesetzt werden. In diesen Fällen müssen sie stattdessen
up()
unddown()
implementieren. Beachten Sie für MySQL außerdem, dass einige Ausdrücke ein implizites Commit verursachen.
Um alle verfügbaren neuen Migrationen anzuwenden (um die lokale Datenbank also auf den aktuellsten Stand zu bringen) ruft man diesen Befehl auf:
yiic migrate
Der Befehl listet alle neuen Migrationen auf. Bestätigt man die Durchführung
der Migrationen, werden sämtliche up()
-Methoden aller neuen
Migrationsklassen ausgeführt. Die Reihenfolge entspricht dabei dem Zeitstempel im
Klassennamen.
Nachdem eine Migration angewendet wurde, speichert das Migrationstool dazu einen
Datensatz in einer Tabelle namens tbl_migration
. Das ermöglicht dem Tool,
festzustellen, welche Migrationen schon angewendet wurden und welche nicht.
Falls die Tabelle tbl_migration
nicht existiert, wird das Tool sie
automatisch anlegen und zwar in der Datenbank, die in der db
-Komponente
konfiguriert wurde.
Manchmal kommt es vor, dass man nur eine oder wenige Migrationen durchführen möchte. Das geht mit diesem Befehl:
yiic migrate up 3
Damit werden 3 neuen Migrationen angewendet. Die Zahl bestimmt also, wieviele Migrationen man anwenden möchte.
Man kann die Datenbank mit folgendem Befehl auch auf eine bestimmte Version migrieren:
yiic migrate to 101129_185401
Man gibt also den Zeitstempel-Teil des Migrationsnamen an, um die Version zu spezifizieren, zu der man migrieren möchte. Sämtliche Migrationen zwischen der aktuellen lokalen DB-Version und dem angegebenen Zeitstempel werden somit durchgeführt. Wurde die angegebene Migration bereits durchgeführt, so werden alle späteren Migrationen rückgängig gemacht (siehe unten).
Um die letzte(n) angewendete(n) Migration(en) rückgängig zu machen, kann man folgenden Befehl verwenden:
yiic migrate down [schritt]
wobei der optionale schritt
-Paramter angibt, wie viele Migrationen man
rückgängig machen möchte. Standardmäßig steht er auf 1, wodurch die letzte
Migration zurückgefahren wird.
Wie oben erwähnt, können nicht alle Migrationen rückgängig gemacht werden. Versucht man eine irreversible Migration rückgängig zu machen, wird eine Exception geworfen und der gesamte Prozess beendet.
Migrationen zu wiederholen bedeutet, sie erst rückgängig zu machen und dann erneut anzuwenden. Das geschieht mit diesem Befehl:
yiic migrate redo [schritt]
wobei der optionale schritt
-Parameter wieder angibt, wie viele Migrationen
wiederholt werden sollen. Standardmäßig wird auch hier die letzte Migration
wiederholt.
Das Tool kann nicht nur Migrationen anwenden und rückgängig machen, sondern auch die Historie der durchgeführten Migrationen bzw. die Liste der neuen Migrationen anzeigen.
yiic migrate history [limit] yiic migrate new [limit]
Der optionale Parameter limit
gibt an, wieviele Migrationen maximal
angezeigt werden sollen. Wird er weggelassen, werden alle Migrationen
angezeigt.
Der erste Befehl zeigt alle angewendenten Migrationen, der zweite die Liste der neuen, die noch nicht durchgeführt wurden.
Es kommt vor, dass man die Migrationshistorie auf eine bestimmte Version setzen möchte, ohne die Migration tatsächlich durchzführen oder zurückzufahren. Gerade wenn man eine neue Migration erstellt ist dies häufig der Fall. Dazu dient der folgende Befehl:
yiic migrate mark 101129_185401
Der Befehl ist ähnlich zu yiic migrate to
, außer, dass er nur die
Migrationshistorie auf die angegebene Version setzt, ohne Migrationen
durchzuführen oder zurückzufahren.
Der Migrationsbefehl kann auf verschiedene Arten angepasst werden.
Der Migrationsbefehl kennt vier Optionen, die an der Kommandozeile angegeben werden können:
interactive
: boolean, gibt an, ob die Migrationen im interaktiven Modus
ausgeführt werden soll, was standardmäßig der Fall ist. Der Anwender wird so
vor der Durchführung einer Migration gefragt. Setzt man die Option auf false
wird die Migration im Hintergrund durchgeführt.
migrationPath
: string, legt das Verzeichnis mit allen Migrationsdateien
fest. Es muss ein Pfadalias angegeben werden, der auf ein existierendes
Verzeichnis verweist. Wird nichts angegeben, wird das
migrations
-Unterverzeichnis im Basispfad verwendet.
migrationTable
: string, gibt den Namen der Tabelle für die
Migrationshistorie an. Vorgabewert ist tbl_migration
. Die Tabellenstruktur
ist version varchar(255) primary key, apply_time integer
.
connectionID
: string, die ID der DB-Komponenten. Standardmäßig 'db'.
templateFile
: string, gibt den Pfadalias zu einer Vorlagendatei für neue
Migrationsklassen an (z.B. application.migrations.template
). Wird diese
Option nicht angegeben, wird eine interne Vorlage verwendet. Innerhalb des
Templates wird der Token {ClassName}
später mit dem eigentlichen
Klassennamen ersetzt.
Diese Optionen können wie folgt angegeben werden:
yiic migrate up --option1=value1 --option2=value2 ...
Will man zum Beispiel ein Modul forum
migrieren, dessen Migrationsdateien im
migrations
-Verzeichnis des Moduls zu finden sind, eignet sich dieser Befehl:
yiic migrate up --migrationPath=ext.forum.migrations
Während man mit den Kommandozeilenoptionen die Migration direkt beeinflussen kann, ist es manchmal einfacher, die Konfiguration einmalig für alle Aufrufe zu verändern. Evtl. möchte man ja eine andere Tabelle für die Migrationshistorie oder eine angepasste Vorlage verwenden. Dazu kann man die Konfiguration für Konsolenanwendungen folgendermaßen anpassen:
return array(
......
'commandMap'=>array(
'migrate'=>array(
'class'=>'system.cli.commands.MigrateCommand',
'migrationPath'=>'application.migrations',
'migrationTable'=>'tbl_migration',
'connectionID'=>'db',
'templateFile'=>'application.migrations.template',
),
......
),
......
);
Ruft man nun den migrate
-Befehl auf, werden obige Optionen immer angewendet,
ohne sie explizit mit angeben zu müssen.
Found a typo or you think this page needs improvement?
Edit it on github !
Signup or Login in order to comment.