0 follower

Migracje bazy danych

W czasie rozwoju i utrzymywania aplikacji zasilanej danymi z bazy danych, struktura tej ostatniej ewoluuje podobnie jak sam kod źródłowy. Przykładowo, rozbudowując aplikację konieczne jest dodanie nowej tabeli, lub też już po wydaniu aplikacji na serwerze produkcyjnym przydałby się indeks, aby poprawić wydajność zapytania itd. Zmiana struktury bazy danych często pociąga za sobą zmiany w kodzie źródłowym, dlatego też Yii udostępnia funkcjonalność tak zwanych migracji bazodanowych, która pozwala na kontrolowanie zmian w bazie danych (migracji).

Poniższe kroki pokazują, jak migracje mogą być wykorzystane przez zespół deweloperski w czasie pracy:

  1. Tomek tworzy nową migrację (np. dodaje nową tabelę, zmienia definicję kolumny, itp.).
  2. Tomek rejestruje ("commit") nową migrację w systemie kontroli wersji (np. Git, Mercurial).
  3. Mariusz uaktualnia swoje repozytorium z systemu kontroli wersji i otrzymuje nową migrację.
  4. Mariusz dodaje migrację do swojej lokalnej bazy danych, dzięki czemu synchronizuje ją ze zmianami, które wprowadził Tomek.

A poniższe kroki opisują w skrócie jak stworzyć nowe wydanie z migracją bazy danych na produkcji:

  1. Rafał tworzy tag wydania dla repozytorium projektu, który zawiera nowe migracje bazy danych.
  2. Rafał uaktualnia kod źródłowy na serwerze produkcyjnym do otagowanej wersji.
  3. Rafał dodaje zebrane nowe migracje do produkcyjnej bazy danych.

Yii udostępnia zestaw narzędzi konsolowych, które pozwalają na:

  • utworzenie nowych migracji;
  • dodanie migracji;
  • cofnięcie migracji;
  • ponowne zaaplikowanie migracji;
  • wyświetlenie historii migracji i jej statusu.

Powyższe narzędzia są dostępne poprzez komendę yii migrate. W tej sekcji opiszemy szczegółowo w jaki sposób z nich korzystać. Możesz również zapoznać się ze sposobem użycia narzędzi w konsoli za pomocą komendy pomocy yii help migrate.

Wskazówka: Migracje mogą modyfikować nie tylko schemat bazy danych, ale również same dane, a także mogą służyć do innych zadań jak tworzenie hierarchi kontroli dostępu dla ról (RBAC) lub czyszczenie pamięci podręcznej.

Uwaga: Modyfikowanie danych w migracji zwykle jest znacznie prostsze, jeśli użyje się do tego klas Active Record, dzięki logice już tam zaimplementowanej. Należy jednak pamiętać, że logika aplikacji jest podatna na częste zmiany, a naturalnym stanem kodu migracji jest jego stałość - w przypadku zmian w warstwie Active Record aplikacji ryzykujemy zepsucie migracji, które z niej korzystają. Z tego powodu kod migracji powinien być utrzymywany niezależnie od pozostałej logiki aplikacji.

Tworzenie migracji

Aby utworzyć nową migrację, uruchom poniższą komendę:

yii migrate/create <nazwa>

Wymagany argument nazwa przekazuje zwięzły opis migracji. Przykładowo, jeśli migracja ma dotyczyć utworzenia nowej tabeli o nazwie news, możesz użyć jako argumentu create_news_table i uruchomić komendę:

yii migrate/create create_news_table

Uwaga: Argument nazwa zostanie użyty jako część nazwy klasy nowej migracji i z tego powodu powinien składać się tylko z łacińskich liter, cyfr i/lub znaków podkreślenia.

Powyższa komenda utworzy nowy plik klasy PHP o nazwie podobnej do m150101_185401_create_news_table.php w folderze @app/migrations. Plik będzie zawierał poniższy kod, gdzie zadeklarowany jest szkielet klasy m150101_185401_create_news_table:

<?php

use yii\db\Migration;

class m150101_185401_create_news_table extends Migration
{
    public function up()
    {

    }

    public function down()
    {
        echo "m101129_185401_create_news_table cannot be reverted.\n";

        return false;
    }

    /*
    // Use safeUp/safeDown to run migration code within a transaction
    public function safeUp()
    {
    }

    public function safeDown()
    {
    }
    */
}

Każda migracja zdefiniowana jest jako klasa PHP rozszerzająca yii\db\Migration. Nazwa klasy migracji jest generowana automatycznie w formacie m<YYMMDD_HHMMSS>_<Nazwa>, gdzie

  • <YYMMDD_HHMMSS> to data i czas UTC wskazujące na moment utworzenia migracji,
  • <Nazwa> jest identyczna z wartością argumentu nazwa podanego dla komendy.

Wewnątrz klasy migracji należy napisać kod w metodzie up(), która wprowadzi zmiany w strukturze bazy danych. Można również napisać kod w metodzie down(), który spowoduje cofnięcie zmian wprowadzonych w up(). Metoda up() jest uruchamiana w momencie aktualizacji bazy, a down() w momencie przywracania jej do poprzedniego stanu. Poniższy kod pokazuje, jak można zaimplementować klasę migracji, aby utworzyć tabelę news:

<?php

use yii\db\Schema;
use yii\db\Migration;

class m150101_185401_create_news_table extends Migration
{
    public function up()
    {
        $this->createTable('news', [
            'id' => Schema::TYPE_PK,
            'title' => Schema::TYPE_STRING . ' NOT NULL',
            'content' => Schema::TYPE_TEXT,
        ]);
    }

    public function down()
    {
        $this->dropTable('news');
    }
}

Informacja: Nie wszystkie migracje są odwracalne. Dla przykładu, jeśli w up() usuwane są wiersze z tabeli, możesz nie być w stanie przywrócić ich w metodzie down(). Może też zdarzyć się, że celowo nie podasz nic w down() - cofanie zmian migracji bazy danych nie jest czymś powszechnym - w takim wypadku należy zwrócić false w metodzie down(), aby wyraźnie wskazać, że migracja nie jest odwracalna.

Podstawowa klasa migracji yii\db\Migration umożliwia połączenie z bazą danych poprzez właściwość db. Możesz użyć jej do modyfikowania schematu bazy za pomocą metod opisanych w sekcji Praca ze schematem bazy danych.

Przy tworzeniu tabeli albo kolumny zamiast używać rzeczywistych typów, powinno się stosować typy abstrakcyjne, dzięki czemu migracje będą niezależne od pojedynczych silników bazodanowych. Klasa yii\db\Schema definiuje zestaw stałych, które reprezentują wspierane typy abstrakcyjne. Stałe te nazwane są według schematu TYPE_<Nazwa>. Dla przykładu, TYPE_PK odnosi się do typu klucza głównego z autoinkrementacją; TYPE_STRING do typu łańcucha znaków. Kiedy migracja jest dodawana do konkretnej bazy danych, typy abstrakcyjne są tłumaczone na odpowiadające im typy rzeczywiste. W przypadku MySQL, TYPE_PK jest zamieniony w int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, a TYPE_STRING staje się varchar(255).

Możesz łączyć abstrakcyjne typy z dodatkowymi definicjami - w powyższym przykładzie NOT NULL jest dodane do Schema::TYPE_STRING, aby oznaczyć, że kolumna nie może być ustawiona jako null.

Informacja: Mapowanie typów abstrakcyjnych na rzeczywiste jest określone we właściwości $typeMap dla każdej klasy QueryBuilder poszczególnych wspieranych silników baz danych.

Począwszy od wersji 2.0.6, możesz skorzystać z nowej klasy budowania schematów, która pozwala na znacznie wygodniejszy sposób definiowana kolumn. Dzięki temu migracja z przykładu powyżej może być napisana następująco:

<?php

use yii\db\Migration;

class m150101_185401_create_news_table extends Migration
{
    public function up()
    {
        $this->createTable('news', [
            'id' => $this->primaryKey(),
            'title' => $this->string()->notNull(),
            'content' => $this->text(),
        ]);
    }

    public function down()
    {
        $this->dropTable('news');
    }
}

Lista wszystkich metod do definiowania typów kolumn dostępna jest w dokumentacji API dla yii\db\SchemaBuilderTrait.

Generowanie migracji

Począwszy od wersji 2.0.7 konsola migracji pozwala na wygodne utworzenie nowej migracji.

Jeśli nazwa migracji podana jest w jednej z rozpoznawalnych form, np. create_xxx_table lub drop_xxx_table, wtedy wygenerowany plik migracji będzie zawierał dodatkowy kod, w tym przypadku odpowiednio kod tworzenia i usuwania tabeli. Poniżej opisane są wszystkie warianty tej funkcjonalności.

Tworzenie tabeli

yii migrate/create create_post_table

generuje

/**
 * Handles the creation for table `post`.
 */
class m150811_220037_create_post_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function up()
    {
        $this->createTable('post', [
            'id' => $this->primaryKey()
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
        $this->dropTable('post');
    }
}

Aby jednocześnie od razu dodać kolumny tabeli, zdefiniuj je za pomocą opcji --fields.

yii migrate/create create_post_table --fields="title:string,body:text"

generuje

/**
 * Handles the creation for table `post`.
 */
class m150811_220037_create_post_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function up()
    {
        $this->createTable('post', [
            'id' => $this->primaryKey(),
            'title' => $this->string(),
            'body' => $this->text(),
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
        $this->dropTable('post');
    }
}

Możesz określić też więcej parametrów kolumny.

yii migrate/create create_post_table --fields="title:string(12):notNull:unique,body:text"

generuje

/**
 * Handles the creation for table `post`.
 */
class m150811_220037_create_post_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function up()
    {
        $this->createTable('post', [
            'id' => $this->primaryKey(),
            'title' => $this->string(12)->notNull()->unique(),
            'body' => $this->text()
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
        $this->dropTable('post');
    }
}

Uwaga: Klucz główny jest dodawany automatycznie i nazwany domyślnie id. Jeśli chcesz użyć innej nazwy, możesz zdefiniować go bezpośrednio np. --fields="name:primaryKey".

Klucze obce

Począwszy od wersji 2.0.8 generator pozwala na zdefiniowanie kluczy obcych za pomocą opcji foreignKey.

yii migrate/create create_post_table --fields="author_id:integer:notNull:foreignKey(user),category_id:integer:defaultValue(1):foreignKey,title:string,body:text"

generuje

/**
 * Handles the creation for table `post`.
 * Has foreign keys to the tables:
 *
 * - `user`
 * - `category`
 */
class m160328_040430_create_post_table extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function up()
    {
        $this->createTable('post', [
            'id' => $this->primaryKey(),
            'author_id' => $this->integer()->notNull(),
            'category_id' => $this->integer()->defaultValue(1),
            'title' => $this->string(),
            'body' => $this->text(),
        ]);

        // creates index for column `author_id`
        $this->createIndex(
            'idx-post-author_id',
            'post',
            'author_id'
        );

        // add foreign key for table `user`
        $this->addForeignKey(
            'fk-post-author_id',
            'post',
            'author_id',
            'user',
            'id',
            'CASCADE'
        );

        // creates index for column `category_id`
        $this->createIndex(
            'idx-post-category_id',
            'post',
            'category_id'
        );

        // add foreign key for table `category`
        $this->addForeignKey(
            'fk-post-category_id',
            'post',
            'category_id',
            'category',
            'id',
            'CASCADE'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
        // drops foreign key for table `user`
        $this->dropForeignKey(
            'fk-post-author_id',
            'post'
        );

        // drops index for column `author_id`
        $this->dropIndex(
            'idx-post-author_id',
            'post'
        );

        // drops foreign key for table `category`
        $this->dropForeignKey(
            'fk-post-category_id',
            'post'
        );

        // drops index for column `category_id`
        $this->dropIndex(
            'idx-post-category_id',
            'post'
        );

        $this->dropTable('post');
    }
}

Umiejscowienie słowa foreignKey w definicji kolumny nie ma znaczenia dla generatora, zatem:

  • author_id:integer:notNull:foreignKey(user)
  • author_id:integer:foreignKey(user):notNull
  • author_id:foreignKey(user):integer:notNull

wygenerują ten sam kod.

Opcja foreignKey może być wzbogacona o parametr w nawiasach, który oznacza nazwę tabeli relacji dla generowanego klucza obcego. Bez tego parametru użyta zostanie nazwa tabeli relacji zgodna z nazwą kolumny.

W przykładzie powyżej author_id:integer:notNull:foreignKey(user) wygeneruje kolumnę o nazwie author_id z kluczem obcym wskazującym na tabelę user, natomiast category_id:integer:defaultValue(1):foreignKey wygeneruje kolumnę category_id z kluczem obcym wskazującym na tabelę category.

Począwszy od wersji 2.0.11, dla foreignKey można podać drugi parametr, oddzielony białym znakiem, z nazwą kolumny relacji dla generowanego klucza obcego. Jeśli drugi parametr nie jest podany, nazwa kolumny jest pobierana ze schematu tabeli. Jeśli schemat nie istnieje , klucz główny nie jest ustawiony lub jest kluczem kompozytowym, używana jest domyślna nazwa id.

Usuwanie tabeli

yii migrate/create drop_post_table --fields="title:string(12):notNull:unique,body:text"

generuje

class m150811_220037_drop_post_table extends Migration
{
    public function up()
    {
        $this->dropTable('post');
    }

    public function down()
    {
        $this->createTable('post', [
            'id' => $this->primaryKey(),
            'title' => $this->string(12)->notNull()->unique(),
            'body' => $this->text()
        ]);
    }
}

Dodawanie kolumny

Jeśli nazwa migracji jest w postaci add_xxx_column_to_yyy_table, wtedy plik będzie zawierał wywołania metod addColumn i dropColumn.

Aby dodać kolumnę:

yii migrate/create add_position_column_to_post_table --fields="position:integer"

co generuje

class m150811_220037_add_position_column_to_post_table extends Migration
{
    public function up()
    {
        $this->addColumn('post', 'position', $this->integer());
    }

    public function down()
    {
        $this->dropColumn('post', 'position');
    }
}

Możesz dodać wiele kolumn jednocześnie:

yii migrate/create add_xxx_column_yyy_column_to_zzz_table --fields="xxx:integer,yyy:text"

Usuwanie kolumny

Jeśli nazwa migracji jest w postaci drop_xxx_column_from_yyy_table, wtedy plik będzie zawierał wywołania metod dropColumn i addColumn.

yii migrate/create drop_position_column_from_post_table --fields="position:integer"

generuje

class m150811_220037_drop_position_column_from_post_table extends Migration
{
    public function up()
    {
        $this->dropColumn('post', 'position');
    }

    public function down()
    {
        $this->addColumn('post', 'position', $this->integer());
    }
}

Dodawanie tabeli węzła

Jeśli nazwa migracji jest w postaci create_junction_table_for_xxx_and_yyy_tables lub create_junction_xxx_and_yyy_tables, wtedy plik będzie zawierał kod potrzebny do wygenerowania tabeli węzła pomiędzy tabelami xxx i yyy.

yii migrate/create create_junction_table_for_post_and_tag_tables --fields="created_at:dateTime"

generuje

/**
 * Handles the creation for table `post_tag`.
 * Has foreign keys to the tables:
 *
 * - `post`
 * - `tag`
 */
class m160328_041642_create_junction_table_for_post_and_tag_tables extends Migration
{
    /**
     * {@inheritdoc}
     */
    public function up()
    {
        $this->createTable('post_tag', [
            'post_id' => $this->integer(),
            'tag_id' => $this->integer(),
            'created_at' => $this->dateTime(),
            'PRIMARY KEY(post_id, tag_id)',
        ]);

        // creates index for column `post_id`
        $this->createIndex(
            'idx-post_tag-post_id',
            'post_tag',
            'post_id'
        );

        // add foreign key for table `post`
        $this->addForeignKey(
            'fk-post_tag-post_id',
            'post_tag',
            'post_id',
            'post',
            'id',
            'CASCADE'
        );

        // creates index for column `tag_id`
        $this->createIndex(
            'idx-post_tag-tag_id',
            'post_tag',
            'tag_id'
        );

        // add foreign key for table `tag`
        $this->addForeignKey(
            'fk-post_tag-tag_id',
            'post_tag',
            'tag_id',
            'tag',
            'id',
            'CASCADE'
        );
    }

    /**
     * {@inheritdoc}
     */
    public function down()
    {
        // drops foreign key for table `post`
        $this->dropForeignKey(
            'fk-post_tag-post_id',
            'post_tag'
        );

        // drops index for column `post_id`
        $this->dropIndex(
            'idx-post_tag-post_id',
            'post_tag'
        );

        // drops foreign key for table `tag`
        $this->dropForeignKey(
            'fk-post_tag-tag_id',
            'post_tag'
        );

        // drops index for column `tag_id`
        $this->dropIndex(
            'idx-post_tag-tag_id',
            'post_tag'
        );

        $this->dropTable('post_tag');
    }
}

Począwszy od wersji 2.0.11, nazwy kolumn kluczy obcych dla tabeli węzła są pobierane ze schematu tabel. Jeśli tabela nie jest zdefiniowana w schemacie, lub jej klucz główny nie jest ustawiony lub jest kluczem kompozytowym, używana jest domyślna nazwa id.

Migracje transakcyjne

Przy wykonywaniu skomplikowanych migracji bazodanowych, bardzo ważnym jest zapewnienie, aby wszystkie ich operacje zakończyły się sukcesem, a w przypadku niepowodzenia nie zostały wprowadzone tylko częściowo, dzięki czemu baza danych może zachować spójność. Zalecane jest, aby w tym celu wykonywać operacje migracji wewnątrz transakcji.

Najprostszym sposobem implementacji migracji transakcyjnych jest umieszczenie ich kodu w metodach safeUp() i safeDown(). Metody te różnią się od up() i down() tym, że są wywoływane automatycznie wewnątrz transakcji. W rezultacie niepowodzenie wykonania dowolnej z operacji skutkuje automatycznym cofnięciem wszystkich poprzenich udanych operacji.

W poniższym przykładzie oprócz stworzenia tabeli news dodatkowo dodajemy pierwszy wiersz jej danych.

<?php

use yii\db\Migration;

class m150101_185401_create_news_table extends Migration
{
    public function safeUp()
    {
        $this->createTable('news', [
            'id' => $this->primaryKey(),
            'title' => $this->string()->notNull(),
            'content' => $this->text(),
        ]);

        $this->insert('news', [
            'title' => 'test 1',
            'content' => 'content 1',
        ]);
    }

    public function safeDown()
    {
        $this->delete('news', ['id' => 1]);
        $this->dropTable('news');
    }
}

Zwróć uwagę na to, że dodając wiele operacji bazodanowych w safeUp(), zwykle powinieneś odwrócić kolejność ich wykonywania w safeDown(). W naszym przykładzie najpierw tworzymy tabelę, a potem dodajemy wiersz w safeUp(), natomiast w safeDown() najpierw kasujemy wiersz, a potem usuwamy tabelę.

Uwaga: Nie wszystkie silniki baz danych wspierają transakcje i nie wszystkie rodzaje komend bazodanowych można umieszczać w transakcjach. Dla przykładu, zapoznaj się z rozdziałem dokumentacji MySQL Statements That Cause an Implicit Commit. W przypadku braku możliwości skorzystania z transakcji, powinieneś użyć up() i down().

Metody pozwalające na dostęp do bazy danych

Bazowa klasa migracji yii\db\Migration udostępnia zestaw metod, dzięki którym można połączyć się z i manipulować bazą danych. Metody te są nazwane podobnie jak metody DAO klasy yii\db\Command. Przykładowo metoda yii\db\Migration::createTable() pozwala na stworzenie nowej tabeli, tak jak yii\db\Command::createTable().

Zaletą korzystania z metod yii\db\Migration jest brak konieczności bezpośredniego tworzenia instancji yii\db\Command, a wywołanie każdej z tych metod dodatkowo wyświetli użyteczne informacje na temat operacji bazodanowych i czasu ich wykonywania.

Poniżej znajdziesz listę wspomnianych wcześniej metod:

Informacja: yii\db\Migration nie udostępnia metod dla kwerendy danych. Wynika to z tego, że zwykle nie jest potrzebne wyświetlanie dodatkowych informacji na temat pobieranych danych z bazy. Dodatkowo możesz zawsze użyć potężnego Konstruktora kwerend do zbudowania i wywołania skomplikowanych kwerend. Użycie konstruktora kwerend w migracji może wyglądać następująco:

// uaktualnij kolumnę statusu dla wszystkich użytkowników
foreach((new Query)->from('users')->each() as $user) {
    $this->update('users', ['status' => 1], ['id' => $user['id']]);
}

Stosowanie migracji

Aby uaktualnić bazę danych do najświeższej wersji jej struktury, należy zastosować wszystkie dostępne nowe migracje, korzystając z poniższej komendy:

yii migrate

Komenda ta wyświetli listę wszystkich migracji, które jeszcze nie zostały zastosowane. Jeśli potwierdzisz, że chcesz je zastosować, wywoła ona metodę up() lub safeUp() dla każdej z migracji na liście, w kolejności ich znaczników czasu. Jeśli którakolwiek z migracji nie powiedzie się, komenda zakończy działanie bez stosowania pozostałych migracji.

Wskazówka: Jeśli nie masz dostępu do linii komend na serwerze, wypróbuj rozszerzenie web shell.

Dla każdej udanej migracji komenda doda wiersz do bazy danych w tabeli migration, aby oznaczyć fakt zastosowania migracji. Pozwoli to na identyfikację, która z migracji została już zastosowana, a która jeszcze nie.

Informacja: Narzędzie do migracji automatycznie utworzy tabelę migration w bazie danych, wskazaną przez opcję db komendy. Domyślnie jest to baza danych określona w komponencie aplikacji db.

Czasem możesz mieć potrzebę zastosowania tylko jednej bądź kilku nowych migracji, zamiast wszystkich na raz. Możesz tego dokonać określając liczbę migracji, które chcesz zastosować uruchamiając komendę. Przykładowo, poniższa komenda spróbuje zastosować następne trzy dostępne migracje:

yii migrate 3

Możesz również dokładnie wskazać konkretną migrację, która powinna być zastosowana na bazie danych, używając komendy migrate/to na jeden z poniższych sposobów:

yii migrate/to 150101_185401                      # używając znacznika czasu z nazwy migracji
yii migrate/to "2015-01-01 18:54:01"              # używając łańcucha znaków, który może być sparsowany przez strtotime()
yii migrate/to m150101_185401_create_news_table   # używając pełnej nazwy
yii migrate/to 1392853618                         # używając UNIXowego znacznika czasu

Jeśli dostępne są niezaaplikowane migracje wcześniejsze niż ta wyraźnie wskazane w komendzie, zostaną one zastosowane automatycznie przed wskazaną migracją.

Jeśli wskazana migracja została już wcześniej zaaplikowana, wszystkie zaaplikowane aplikacje z późniejszą datą zostaną cofnięte.

Cofanie migracji

Aby odwrócić (wycofać) jedną lub więcej migracji, które zostały wcześniej zastosowane, możesz uruchomić następującą komendę:

yii migrate/down     # cofa ostatnio dodaną migrację
yii migrate/down 3   # cofa 3 ostatnio dodane migracje

Uwaga: Nie wszystkie migracje są odwracalne. Próba cofnięcia takiej migracji spowoduje błąd i zatrzyma cały proces.

Ponawianie migracji

Ponawianie migracji oznacza najpierw wycofanie jej, a potem ponowne zastosowanie. Można tego dokonać następująco:

yii migrate/redo        # ponawia ostatnio zastosowaną migrację
yii migrate/redo 3      # ponawia ostatnie 3 zastosowane migracje

Uwaga: Jeśli migracja nie jest odwracalna, nie będziesz mógł jej ponowić.

Odświeżanie migracji

Począwszy od Yii 2.0.13 możliwe jest usunięcie wszystkich tabel i kluczy obcych z bazy danych i zastosowanie wszystkich migracji od początku.

yii migrate/fresh       # czyści bazę danych i wykonuje wszystkie migracje od początku

Lista migracji

Aby wyświetlić listę wszystkich zastosowanych i oczekujących migracji, możesz użyć następujących komend:

yii migrate/history     # pokazuje ostatnie 10 zastosowanych migracji
yii migrate/history 5   # pokazuje ostatnie 5 zastosowanych migracji
yii migrate/history all # pokazuje wszystkie zastosowane migracje

yii migrate/new         # pokazuje pierwsze 10 nowych migracji
yii migrate/new 5       # pokazuje pierwsze 5 nowych migracji
yii migrate/new all     # pokazuje wszystkie nowe migracje

Modyfikowanie historii migracji

Czasem zamiast aplikowania lub odwracania migracji, możesz chcieć po prostu zaznaczyć, że baza danych zostałą już uaktualniona do konkretnej migracji. Może się tak zdarzyć, gdy ręcznie modyfikujesz bazę i nie chcesz, aby migracja(e) z tymi zmianami zostały potem ponownie zaaplikowane. Możesz to osiągnąć w następujący sposób:

yii migrate/mark 150101_185401                      # używając znacznika czasu z nazwy migracji
yii migrate/mark "2015-01-01 18:54:01"              # używając łańcucha znaków, który może być sparsowany przez strtotime()
yii migrate/mark m150101_185401_create_news_table   # używając pełnej nazwy
yii migrate/mark 1392853618                         # używając UNIXowego znacznika czasu

Komenda zmodyfikuje tabelę migration poprzez dodanie lub usunięcie wierszy, aby zaznaczyć, że baza danych ma już zastosowane migracje aż do tej określonej w komendzie. Migracje nie zostaną faktycznie zastosowane lub usunięte.

Dostosowywanie migracji

Dostępnych jest kilka opcji pozwalających na dostosowanie komendy migracji do własnych potrzeb.

Użycie opcji linii komend

Komenda migracji ma kilka opcji, które pozwalają na zmianę jej działąnia:

  • interactive: boolean (domyślnie true), określa czy przeprowadzić migrację w trybie interaktywnym. Jeśli ustawione jest true, użytkownik będzie poproszony o potwierdzenie przed wykonaniem określonych operacji. Możesz chcieć zmienić to ustawienie na false, jeśli komenda ma być używana w tle.

  • migrationPath: string|array (domyślnie @app/migrations), określa folder, gdzie znajdują się wszystkie pliki migracji. Parametr może być określony jako rzeczywista ścieżka lub alias. Zwróć uwagę na to, że folder musi istnieć, inaczej okmenda może wywołać błąd. Począwszy od wersji 2.0.12 można tutaj podać tablicę, aby załadować migracje z wielu źródeł.

  • migrationTable: string (domyślnie migration), określa nazwę tabeli w bazie danych, gdzie trzymana będzie historia migracji. Tabela będzie automatycznie stworzona, jeśli nie istnieje. Możesz również utworzyć ją ręcznie używając struktury version varchar(255) primary key, apply_time integer.

  • db: string (domyślnie db), określa identyfikator bazodanowego komponentu aplikacji. Reprezentuje on bazę danych, na której będą zastosowane migracje.

  • templateFile: string (domyślnie @yii/views/migration.php), określa ścieżkę pliku szablonu, używanego do generowania szkieletu plików migracji. Parametr może być określony jako rzeczywista ścieżka lub alias. Plik szablonu jest skryptem PHP, w którym możesz użyć predefiniowanej zmiennej $className, aby pobrać nazwę klasy migracji.

  • generatorTemplateFiles: array (domyślnie `[

      'create_table' => '@yii/views/createTableMigration.php',
      'drop_table' => '@yii/views/dropTableMigration.php',
      'add_column' => '@yii/views/addColumnMigration.php',
      'drop_column' => '@yii/views/dropColumnMigration.php',
      'create_junction' => '@yii/views/createTableMigration.php'
    

    ]`), określa pliki szablonów do generowania kodu migracji. Po więcej szczegółów przejdź do "Generowanie migracji".

  • fields: tablica definicji kolumn w postaci łańcuchów znaków do wygenerowania kodu migracji. Domyślnie []. Format każdej definicji to NAZWA_KOLUMNY:TYP_KOLUMNY:DEKORATOR_KOLUMNY. Dla przykładu, --fields=name:string(12):notNull generuje kolumnę typu "string" o rozmiarze 12, która nie może mieć wartości null.

Poniższy przykład pokazuje jak można użyć tych opcji.

Chcemy zmigrować moduł forum, którego pliki migracji znajdują się w folderze migrations modułu - używamy następującej komendy:

# stosuje migracje dla modułu forum w trybie nieinteraktywnym
yii migrate --migrationPath=@app/modules/forum/migrations --interactive=0

Konfigurowanie komendy globalnie

Zamiast podawać żmudnie te same opcje za każdym razem, gdy uruchamiamy komendę migracji, można ją skonfigurować w konfiguracji aplikacji:

return [
    'controllerMap' => [
        'migrate' => [
            'class' => 'yii\console\controllers\MigrateController',
            'migrationTable' => 'backend_migration',
        ],
    ],
];

Powyższa konfiguracja powoduje, że z każdym uruchomieniem komendy migracji, tabela backend_migration jest używana do zapisu historii migracji i nie musisz już określać jej za pomocą opcji linii komend migrationTable.

Migracje w przestrzeni nazw

Począwszy od 2.0.10 możliwe jest używanie przestrzeni nazw w klasach migracji. Możesz zdefiniować listę przestrzeni nazw za pomocą migrationNamespaces. Korzystanie z przestrzeni nazw pozwala na łatwe używanie wielu źródeł migracji. Przykładowo:

return [
    'controllerMap' => [
        'migrate' => [
            'class' => 'yii\console\controllers\MigrateController',
            'migrationPath' => null, // wyłącz ścieżkę do folderu migracji, jeśli dodajesz app\migrations na liście poniżej
            'migrationNamespaces' => [
                'app\migrations', // Wspólne migracje dla całej aplikacji
                'module\migrations', // Migracje konkretnego modułu
                'some\extension\migrations', // Migracje konkretnego rozszerzenia
            ],
        ],
    ],
];

Uwaga: Migracje zaaplikowane z różnych przestrzeni nazw będą dodane do pojedynczej historii migracji, przez co np. niemożliwym jest zastosowanie lub cofnięcie migracji z tylko wybranej przestrzeni nazw.

Wykonując operacje na migracjach z przestrzeni nazw: dodając nowe, odwracając je, itd., należy podać pełną przestrzeń nazw przed nazwą migracji. Zwróć uwagę na to, że odwrotny ukośnik (\) jest zwykle uważany za znak specjalny linii komend, zatem musisz odpowiednio zastosować symbol ucieczki, aby uniknąć błędów konsoli i niespodziewanych skutków komendy. Dla przykładu:

yii migrate/create app\\migrations\\CreateUserTable

Uwaga: Migracje, których lokalizacja określona jest poprzez migrationPath nie mogą zawierać przestrzeni nazw. Migracje w przestrzeni nazw mogą być zaaplikowane tylko jeśli są wymienione we właściwości yii\console\controllers\MigrateController::$migrationNamespaces.

Począwszy od wersji 2.0.12 właściwość migrationPath pozwala również na podanie tablicy wymieniającej wszystkie foldery zawierające migracje bez przestrzeni nazw. Zmiana ta została wprowadzona dla istniejących projektów, które używają migracji z wielu lokalizacji, głównie z zewnętrznych źródeł jak rozszerzenia Yii tworzone przez innych deweloperów, które z tego powodu nie mogą łatwo być zmodyfikowane, aby używać przestrzeni nazw.

Generowanie migracji w przestrzeni nazw

Migracje w przestrzeni nazw korzystają z formatu nazw "CamelCase" M<YYMMDDHHMMSS><Nazwa> (przykładowo M190720100234CreateUserTable). Generując taką migrację pamiętaj, że nazwa tabeli będzie przekonwertowana z formatu "CamelCase" na format "podkreślnikowy". Dla przykładu:

yii migrate/create app\\migrations\\DropGreenHotelTable

generuje migrację w przestrzeni nazw app\migrations usuwającą tabelę green_hotel, a

yii migrate/create app\\migrations\\CreateBANANATable

generuje migrację w przestrzeni nazw app\migrations tworzącą tabelę b_a_n_a_n_a.

Jeśli nazwa tabeli zawiera małe i wielkie litery (like studentsExam), poprzedź nazwę podkreślnikiem:

yii migrate/create app\\migrations\\Create_studentsExamTable

To wygeneruje migrację w przestrzeni nazw app\migrations tworzącą tabelę studentsExam.

Rozdzielenie migracji

Czasem korzystanie z pojedynczej historii migracji dla wszystkich migracji w projekcie jest uciążliwe. Dla przykładu, możesz zainstalować rozszerzenie 'blog', zawierające całkowicie oddzielne funkcjonalności i dostarczające własne migracje, które nie powinny wpływać na te dedykowane dla funkcjonalności głównego projektu.

Jeśli chcesz, aby część migracji mogła być zastosowana i śledzona całkowicie niezależnie od pozostałych, możesz skonfigurować kilka komend migracji, które będą używać różnych przestrzeni nazw i tabeli historii migracji:

return [
    'controllerMap' => [
        // Wspólne migracje dla całej aplikacji
        'migrate-app' => [
            'class' => 'yii\console\controllers\MigrateController',
            'migrationNamespaces' => ['app\migrations'],
            'migrationTable' => 'migration_app',
            'migrationPath' => null,
        ],
        // Migracje dla konkretnego modułu
        'migrate-module' => [
            'class' => 'yii\console\controllers\MigrateController',
            'migrationNamespaces' => ['module\migrations'],
            'migrationTable' => 'migration_module',
            'migrationPath' => null,
        ],
        // Migrations dla konkretnego rozszerzenia
        'migrate-rbac' => [
            'class' => 'yii\console\controllers\MigrateController',
            'migrationPath' => '@yii/rbac/migrations',
            'migrationTable' => 'migration_rbac',
        ],
    ],
];

Zwróć uwagę na to, że teraz, aby zsynchronizować bazę danych, musisz uruchomić kilka komend zamiast jednej:

yii migrate-app
yii migrate-module
yii migrate-rbac

Migrowanie wielu baz danych

Domyślnie migracje są stosowane do jednej bazy danych określonej przez komponent aplikacji db. Jeśli chcesz, aby były zastosowane do innej bazy, musisz zdefiniować opcję db w linii komend, jak poniżej,

yii migrate --db=db2

Ta komenda zastosuje migracje do bazy db2.

Czasem konieczne jest, aby zastosować niektóre migracje do jednej bazy, a inne do drugiej. Aby to uzyskać, podczas implementacji klasy migracji należy bezpośrednio wskazać identyfikator komponentu bazy danych, który migracja ma użyć, jak poniżej:

<?php

use yii\db\Migration;

class m150101_185401_create_news_table extends Migration
{
    public function init()
    {
        $this->db = 'db2';
        parent::init();
    }
}

Ta migracja będzie zastosowana do bazy db2, nawet jeśli w opcjach komendy określona będzie inna baza. Zwróć uwagę na to, że historia migracji będzie uaktualniona wciąż w bazie danych określonej przez opcję db linii komend.

Jeśli masz wiele migracji korzystających z tej samej bazy danych, zalecane jest utworzenie bazowej klasy migracji z powyższym kodem metody init(), a następnie dziedziczenie po niej w każdej kolejnej migracji.

Wskazówka: Oprócz ustawiania właściwości db, możesz również operować na różnych bazach poprzez tworzenie nowych połączeń bazodanowych w klasach migracji, a następnie korzystanie z metod DAO i tych połączeń.

Inną strategią migracji wielu baz danych jest utrzymywanie migracji dla różnych baz w różnych folderach migracji. Dzięki temu możesz te bazy migrować w osobnych komendach:

yii migrate --migrationPath=@app/migrations/db1 --db=db1
yii migrate --migrationPath=@app/migrations/db2 --db=db2
...

Pierwsza komenda zastosuje migracje z folderu @app/migrations/db1 na bazie db1, druga migracje z folderu @app/migrations/db2 na bazie db2, itd.

Found a typo or you think this page needs improvement?
Edit it on github !