Migracja bazy danych

Uwaga: Funkcjonalność migrowania bazy danych jest dostępna od wersji 1.1.6.

Podobnie jak kod tak i struktura bazy danych zmienia się wraz z rozwojem i zarządzaniem aplikacją bazodanową. Na przykład, podczas rozwoju, możemy chcieć dodać nową tabelę, czy też, po przeniesieniu aplikacji na serwer produkcyjny, może powstać potrzeba dodania indeksu do kolumny. Ważne jest, aby śledzić te zmiany w strukturze bazy danych (nazywane migracją) podobnie jak to robimy z kodem. Jeśli kod źródłowy i baza danych rozsynchronizują się, zachodzi duże prawdopodobieństwo, że cały system przestanie działać. Z tego powodu, Yii dostarcza narzędzie do migracji, które może śledzić historię migracji bazy danych, aplikować nowe migracje lub też odwracać istniejące.

Poniższe kroki pokazują sposób używania migracji bazy danych w trakcie rozwoju aplikacji:

  1. Jan utworzył nową migrację (np. dodał nową tabelę)
  2. Jan skomitował nową migrację do systemu kontroli wersji (np. SVN, GIT)
  3. Stefan ściągnął zmiany z systemy kontroli wersji i otrzymał nową migrację
  4. Stefan zaaplikował nową migrację na swoją lokalną bazę danych

Yii wspiera migrację bazy danych poprzez narzędzie linii poleceń yiic migrate. Pozwala ono na tworzenie nowych, aplikowanie/odwracanie/poprawianie migracji oraz wyświetlanie historii oraz nowych migracji.

W dalszej części opiszemy w jaki sposób używać tego narzędzia.

Uwaga: Zaleca się używanie narzędzia yiic właściwego dla danej aplikacji (np. cd ścieżka/do/protected) podczas pracy z komendą migrate zamiast tego, który znajduje się w katalogu framework. Upewnij się, że posiadasz folder protected\migrations z prawami zapisu. Sprawdź również, czy skonfigurowałeś połączenie z bazą danych w pliku protected/config/console.php.

1. Tworzenie migracji

Aby utworzyć nową migrację (np. dodającej nową tabelę), wykonujemy następujące polecenie:

yiic migrate create <name>

Obowiązkowy parametr name bardzo zwięźle opisuje migrację (np. create_news_table). Jak zobaczymy w dalszej części, parametr name używany jest jako część nazwy klasy PHP. Z tej przyczyny powinien zawierać jedynie litery, cyfry i/lub znaki podkreślenia.

yiic migrate create create_news_table

Powyższe polecenie utworzy w katalogu protected/migrations nowy plik o nazwie m20101129_185401_create_news_table.php, który zawierać będzie następujący, początkowy kod:

class m101129_185401_create_news_table extends CDbMigration
{
    public function up()
    {
    }
 
    public function down()
    {
        echo "m101129_185401_create_news_table does not support migration down.\n";
        return false;
    }
 
    /*
    // implement safeUp/safeDown instead if transaction is needed
    public function safeUp()
    {
    }
 
    public function safeDown()
    {
    }
    */
}

Zauważ, że nazwa klasy jest taka sama jak nazwa pliku, zgodnie z wzorcem m<timestamp>_<name>, gdzie <timestamp> wskazuje na stempel czasu UTC (w domyślnym formacie yymmdd_hhmmss) utworzenia migracji, zaś wartość reprezentująca <name> pobierana jest z parametru name polecenia.

Metoda up() powinna zawierać kod implementujący aklualną migrację bazy danych, podczas gdy metoda down() może zawierać kod odwracający to, co zostało zrobione w metodzie up().

Czasami niemożliwe jest zaimplementowanie metody down(). Na przykład, jeśli usunęliśmy wiersze tabeli w metodzie up(), nie będziemy w stanie ich odtworzyć w metodzie down(). W takim przypadku migracja nazywana jest nieodwracalną, co oznacza, że nie możemy powrócić do poprzedniego stanu bazy danych. W kodzie wygenerowanym powyżej, metoda down() zwraca false w celu zasygnalizowania nieodwracalności migracji.

Info: Poczynając od wersji 1.1.7, jeśli metody up() oraz down() zwracają false, wszystkie następne migrację zostaną anulowane. W poprzedniej wersji 1.1.6, każda musiała zwrócić wyjątek aby anulować kolejne migracje.

W ramach przykładu pokażemy migrację tworzącą tabelę wiadomości.

class m101129_185401_create_news_table 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');
    }
}

Klasa bazowa CDbMigration dostarcza metod manipulujących danymi oraz schematem bazy danych. Na przykład, metoda CDbMigration::createTable utworzy tabelę w bazie danych, zaś metoda CDbMigration::insert wstawi wiersz danych. Wszystkie te metody używają połączenia z bazą danych zwracanego przez metodę CDbMigration::getDbConnection(), która domyślnie zwraca Yii::app()->db.

Info: Jak zauważyłeś, metody bazodanowe dostarczane przez CDbMigration są bardzo podobne do tych z CDbCommand. Rzeczywiście są one prawie takie same z tym wyjątkien, że metody CDbMigration odmierzają czas zużyty na ich wykonanie oraz wyświetlają pewne dodatkowe informację o parametrach metody.

2. Migracje transakcyjne

Info: Wsparcie dla migracji transakcyjnych jest wpierane od wersji 1.1.7.

W trakcie wykonywania skomplikowanych migracji baz danych, zazwyczaj chcemy być pewni, że każda migracja powiedzie się lub nie w całości tak aby baza danych zachowała swoją spójność i integralność. Aby uzyskać ten efekt możemy wykorzystać transakcje baz danych.

Możemy wprost rozpocząć transakcję bazodanową i ująć resztę kodu związanego z bazą w ramach tej transakcji w następujący sposób:

class m101129_185401_create_news_table 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;
        }
    }
 
    // ...podobny kod dla metody down()
}

Jednakże łatwiejszym sposobem uzyskania wparcia dla transakcyjności jest zaimplementowanie metody safeUp() zamiast up() oraz safeDown() zamiastdown(). Na przykład:

class m101129_185401_create_news_table 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');
    }
}

Podczas przeprowadzania migracji przez framework Yii, rozpocznie on transakcję bazy danych a następnie wywoła metodę safeUp() lub safeDown(). Jeśli w metodzie safeUp() lub safeDown() wystąpi błąd na bazie transakcja zostanie wycofana zapewniając w ten sposób poprawny stan bazy danych.

Uwaga: Nie wszystkie DBMS wspierają transakcje. Również niektóre zapytania nie mogą być umieszczane w ramach transakcji. W takim przypadku będziesz musiał zaimplementować metodę up() oraz down(). Dla MySQL, część instrukcji SQL może powodować wywołanie niejawne polecenia COMMIT.

3. Aplikowanie migracji

Aby zaaplikować wszystkie nowe migracje (np. aktualizujące lokalną wersję bazy danych) uruchom następujące polecenie:

yiic migrate

Polecenie wyświetli listę wszystkich nowych migracji. Jeśli potwierdzisz zaaplikowanie tych migracji, polecenie to wywoła metodę up() każdej z klasy migracyjnych, jedna po drugiej, w kolejności wartości znacznika czasu (ang. timestamp) zapisanego w nazwie klasy.

Po zastosowaniu migracji, narzędzie migrujace zapisze rekord w tabeli tbl_migration. Pozwoli to narzędziu zidentyfikować, które migracje zostały zaaplikowane a które nie. Jeśli tabela tbl_migration nie istnieje, narzędzie migracji utworzy ją w bazie określonej przez komponent aplikacji db.

Czasami możemy chcieć zaaplikować tylko jedną lub kilka migracji. W tym celu używamy następującego polecenia:

yiic migrate up 3

Polecenie to zaaplikuje 3 nowe migracje. Zmieniając wartość 3 zmieniamy ilość migracji, które mają zostać zaaplikowane.

Możemy również zmigrować bazę danych do określonej wersji za pomocą następującego polecenia:

yiic migrate to 101129_185401

Oznacza to, że korzystamy z części nazwy migracji zawierającej informację o stemplu czasowym w celu określenia wersji, do której chcemy zmigrować bazę danych. Jeśli pomiędzy ostatnio zastosowaną migracją a określoną przez nas występują inne, wszystkie te migracje zostaną zaaplikowane. Jeśli dana migracja została zaaplikowana wcześniej, wtedy wszystkie migracje występujące po niej zostaną odwrócone (zostanie to opisane w dalszej części).

4. Odwracanie migracji

W celu odwrócenia ostaniej lub kilku ostatnio zaaplikowanych migracji, możemy użyć następującego polecenia:

yiic migrate down [step]

gdzie opcjonalny parametr step określa jak wiele migracji zostanie odwróconych. Domyślnie przyjmuje wartość 1 co oznacza odwracanie ostatnio zaaplikowanej migracji.

Tak jak wspominaliśmy wcześniej nie wszystkie migracje można odwracać. Próbując odwrócać nieodwracalną migrację otrzymamy wyjątek i proces odwracania zostanie przerwany.

5. Poprawianie migracji

Poprawianie migracji oznacza odwrócenie a następnie zaaplikowanie danej migracji jeszcze raz. Może to być zrobione za pomocą następującego polecenia:

yiic migrate redo [step]

gdzie opcjonalny parametr step określa jak wiele migracji zostanie poprawionych. Domyślnie przyjmuje wartość 1 co oznacza, że ostatnia migracja zostanie poprawiona.

6. Wyświetlanie informacji o migracji

Poza aplikowaniem i odwracaniem migracji, narzędzie do migracji potrafi również wyświetlać historię oraz listę nowych, gotowych do zaaplikowania migracji.

yiic migrate history [limit]
yiic migrate new [limit]

Opcjonalny parametr limit określa ilość migracji do wyświetlenia. Jeśli parametr limit nie został zdefiniowany, wyświetlone zostaną wszystkie migracje.

Pierwsze polecenie pokaże migracje, które zostały zaaplikowane, drugie zaś wyświetli migracje, które nie zostały jeszcze zaaplikowane.

7. Modyfikowanie historii migracji

Czasami możemy chcieć zmodyfikować historię migracji do konkretnej wersji bez aplikowania lub też odwracania migracji. Dzieje się to często, kiedy tworzymy nową migracje. Możemy użyć następującego polecenia aby to zrobić.

yiic migrate mark 101129_185401

Polecenie to jest bardzo podobne do yiic migrate to z tym wyjątkiem, że modyfikuje ono wyłącznie tabelę zawierającą wpisy o historii migracji tak aby wskazywała ona daną wersję migracji bez jej aplikowania czy też odwracania.

8. Dostosowywanie poleceń migracji

Istnieje kilka sposobów aby dostosować polecenia migracji.

Poprzez opcje linii poleceń

Polecenia migracji dostarczane są wraz z czterema opcjami, które mogą być określone w linii poleceń:

  • interactive: boolean, określa kiedy wykonywać migrację w trybie interaktywnym. Domyślnie true, co oznacza, iż użytkownik jest monitowany o wynikach migracji podczas jej wykonywania. Jeżeli ustawisz tę wartość na false migracja powinna przebiegać w tle.

  • migrationPath: łańcuch znaków, określający katalog przechowujący wszystkie pliki klas migracji. Należy go zdefiniować używając aliasu ścieżki do istniejącego katalogu. Jeśli opcja ta nie została określona, będzie ona używać podkatalogu migrations znajdującego się w katalogu bazowym aplikacji.

  • migrationTable: łańcuch znaków, określający nazwę tabeli bazy danych przechowywującej informacje o historii migracji. Domyślna wartość to tbl_migration. Struktura tej tabeli jest następująca version varchar(255) primary key, apply_time integer.

  • connectionID: łańcuch znaków, określający identyfikator komponentu bazy danych aplikacji. Domyślnie 'db'.

  • templateFile: łańcuch znaków, określający ścieżkę do pliku, który posłuży jako szablon kodu używany do generowania klas migracji. Należy go określić przy użyciu aliasu ścieżki (np. application.migrations.template). Jeśli nie został podany, wewnętrzny szablon zostanie użyty, W szablonie token {ClassName} zostanie zastąpiony aktualną nazwą klasy migrującej.

W celu zdefiniowania tych opcji wykonaj polecenia migracji używając formatu

yiic migrate up --opcja1=wartość1 --opcja2=wartość2 ...

Na przykład, jeśli chcemy przeprowadzić migrację dla modułu forum, którego pliki migracyjne znajdują się w jego wewnętrznym katalogu migrations, możemy użyć następującego polecenia:

yiic migrate up --migrationPath=ext.forum.migrations

Globalne konfigurowanie poleceń

Podczas gdy opcje linii poleceń pozwalają nam konfigurować polecenia w locie, czasami możemy chcieć skonfigurować polecenia raz na zawsze. Na przykład, możemy chcieć używać innej tabeli do przechowywania historii migracji, czy też możemy chcież używać zmienionego szablonu migracyjnego. Możemy to zrobić poprzez zmodyfikowanie pliku konfiguracyjnego aplikacji konsolowej następująco:

return array(
    ......
    'commandMap'=>array(
        'migrate'=>array(
            'class'=>'system.cli.commands.MigrateCommand',
            'migrationPath'=>'application.migrations',
            'migrationTable'=>'tbl_migration',
            'connectionID'=>'db',
            'templateFile'=>'application.migrations.template',
        ),
        ......
    ),
    ......
);

Jeśli teraz uruchomimy polecenie migrate powyższa konfiguracja będzie obowiązywać bez konieczności wprowadzania za każdym razem opcji poprzez linię poleceń.

$Id: database.migration.txt 3450 2011-11-20 22:52:07Z alexander.makarow $

Be the first person to leave a comment

Please to leave your comment.