diff --git a/docs/guide-pl/db-migrations.md b/docs/guide-pl/db-migrations.md
new file mode 100644
index 0000000000..dfefdfcb73
--- /dev/null
+++ b/docs/guide-pl/db-migrations.md
@@ -0,0 +1,1070 @@
+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`.
+
+> Tip: 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.
+
+> Note: Modyfikowanie danych w migracji zwykle jest znacznie prostsze, jeśli użyje się do tego klas
+ [Active Record](db-active-record.md), 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
+```
+
+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
+```
+
+> Note: 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
+_`, gdzie
+
+* `` to data i czas UTC wskazujące na moment utworzenia migracji,
+* `` 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
+createTable('news', [
+ 'id' => Schema::TYPE_PK,
+ 'title' => Schema::TYPE_STRING . ' NOT NULL',
+ 'content' => Schema::TYPE_TEXT,
+ ]);
+ }
+
+ public function down()
+ {
+ $this->dropTable('news');
+ }
+}
+```
+
+> Info: 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ść
+[[yii\db\Migration::db|db]]. Możesz użyć jej do modyfikowania schematu bazy za pomocą metod opisanych w sekcji
+[Praca ze schematem bazy danych](db-dao.md#database-schema).
+
+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_`. 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`.
+
+> Info: Mapowanie typów abstrakcyjnych na rzeczywiste jest określone we właściwości [[yii\db\QueryBuilder::$typeMap|$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
+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
+
+```php
+/**
+ * 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
+
+```php
+/**
+ * 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
+
+```php
+/**
+ * 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');
+ }
+}
+```
+
+> Note: 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
+
+```php
+/**
+ * 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
+
+```php
+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
+
+```php
+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`.
+
+```php
+yii migrate/create drop_position_column_from_post_table --fields="position:integer"
+```
+
+generuje
+
+```php
+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
+
+```php
+/**
+ * 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](db-dao.md#performing-transactions).
+
+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
+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ę.
+
+> Note: 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](http://dev.mysql.com/doc/refman/5.7/en/implicit-commit.html). 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](db-dao.md) 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:
+
+* [[yii\db\Migration::execute()|execute()]]: wykonywanie komendy SQL
+* [[yii\db\Migration::insert()|insert()]]: dodawanie pojedynczego wiersza
+* [[yii\db\Migration::batchInsert()|batchInsert()]]: dodawanie wielu wierszy
+* [[yii\db\Migration::upsert()|upsert()]]: dodawanie pojedynczego wiersza lub aktualizowanie go, jeśli już istnieje (od 2.0.14)
+* [[yii\db\Migration::update()|update()]]: aktualizowanie wierszy
+* [[yii\db\Migration::delete()|delete()]]: usuwanie wierszy
+* [[yii\db\Migration::createTable()|createTable()]]: tworzenie tabeli
+* [[yii\db\Migration::renameTable()|renameTable()]]: zmiana nazwy tabeli
+* [[yii\db\Migration::dropTable()|dropTable()]]: usuwanie tabeli
+* [[yii\db\Migration::truncateTable()|truncateTable()]]: usuwanie wszystkich wierszy w tabeli
+* [[yii\db\Migration::addColumn()|addColumn()]]: dodawanie kolumny
+* [[yii\db\Migration::renameColumn()|renameColumn()]]: zmiana nazwy kolumny
+* [[yii\db\Migration::dropColumn()|dropColumn()]]: usuwanie kolumny
+* [[yii\db\Migration::alterColumn()|alterColumn()]]: zmiana definicji kolumny
+* [[yii\db\Migration::addPrimaryKey()|addPrimaryKey()]]: dodawanie klucza głównego
+* [[yii\db\Migration::dropPrimaryKey()|dropPrimaryKey()]]: usuwanie klucza głównego
+* [[yii\db\Migration::addForeignKey()|addForeignKey()]]: dodawanie klucza obcego
+* [[yii\db\Migration::dropForeignKey()|dropForeignKey()]]: usuwanie klucza obcego
+* [[yii\db\Migration::createIndex()|createIndex()]]: tworzenie indeksu
+* [[yii\db\Migration::dropIndex()|dropIndex()]]: usuwanie indeksu
+* [[yii\db\Migration::addCommentOnColumn()|addCommentOnColumn()]]: dodawanie komentarza do kolumny
+* [[yii\db\Migration::dropCommentFromColumn()|dropCommentFromColumn()]]: usuwanie komentarza z kolumny
+* [[yii\db\Migration::addCommentOnTable()|addCommentOnTable()]]: dodawanie komentarza do tabeli
+* [[yii\db\Migration::dropCommentFromTable()|dropCommentFromTable()]]: usuwanie komentarza z tabeli
+
+> Info: [[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](db-query-builder.md) do zbudowania i wywołania skomplikowanych kwerend.
+ Użycie konstruktora kwerend w migracji może wyglądać następująco:
+>
+> ```php
+> // 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.
+
+> Tip: Jeśli nie masz dostępu do linii komend na serwerze, wypróbuj rozszerzenie [web shell](https://github.com/samdark/yii2-webshell).
+
+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.
+
+> Info: Narzędzie do migracji automatycznie utworzy tabelę `migration` w bazie danych, wskazaną przez opcję
+ [[yii\console\controllers\MigrateController::db|db]] komendy. Domyślnie jest to baza danych określona w
+ [komponencie aplikacji](structure-application-components.md) `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
+```
+
+> Note: 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
+```
+
+> Note: 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](concept-aliases.md).
+ 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](structure-application-components.md).
+ 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](concept-aliases.md).
+ 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](#generating-migrations)".
+
+* `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:
+
+```php
+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ą [[yii\console\controllers\MigrateController::migrationNamespaces|migrationNamespaces]]. Korzystanie z przestrzeni
+nazw pozwala na łatwe używanie wielu źródeł migracji. Przykładowo:
+
+```php
+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
+ ],
+ ],
+ ],
+];
+```
+
+> Note: 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
+```
+
+> Note: Migracje, których lokalizacja określona jest poprzez
+ [[yii\console\controllers\MigrateController::migrationPath|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ść [[yii\console\controllers\MigrateController::migrationPath|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` (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:
+
+```php
+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](structure-application-components.md) `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
+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.
+
+> Tip: Oprócz ustawiania właściwości [[yii\db\Migration::db|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](db-dao.md) 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.
diff --git a/docs/guide/db-migrations.md b/docs/guide/db-migrations.md
index 0c536c99c1..cbc9ef5d7e 100644
--- a/docs/guide/db-migrations.md
+++ b/docs/guide/db-migrations.md
@@ -35,7 +35,7 @@ All these tools are accessible through the command `yii migrate`. In this sectio
how to accomplish various tasks using these tools. You may also get the usage of each tool via the help
command `yii help migrate`.
-> Tip: migrations could affect not only database schema but adjust existing data to fit new schema, create RBAC
+> Tip: Migrations could affect not only database schema but adjust existing data to fit new schema, create RBAC
hierarchy or clean up cache.
> Note: When manipulating data using a migration you may find that using your [Active Record](db-active-record.md) classes
@@ -303,7 +303,7 @@ class m150811_220037_create_post_table extends Migration
}
```
-> Note: primary key is added automatically and is named `id` by default. If you want to use another name you may
+> Note: Primary key is added automatically and is named `id` by default. If you want to use another name you may
> specify it explicitly like `--fields="name:primaryKey"`.
#### Foreign keys
@@ -690,6 +690,7 @@ Below is the list of all these database accessing methods:
* [[yii\db\Migration::insert()|insert()]]: inserting a single row
* [[yii\db\Migration::batchInsert()|batchInsert()]]: inserting multiple rows
* [[yii\db\Migration::update()|update()]]: updating rows
+* [[yii\db\Migration::upsert()|upsert()]]: inserting a single row or updating it if it exists (since 2.0.14)
* [[yii\db\Migration::delete()|delete()]]: deleting rows
* [[yii\db\Migration::createTable()|createTable()]]: creating a table
* [[yii\db\Migration::renameTable()|renameTable()]]: renaming a table
@@ -722,7 +723,6 @@ Below is the list of all these database accessing methods:
> }
> ```
-
## Applying Migrations
To upgrade a database to its latest structure, you should apply all available new migrations using the following command:
@@ -801,7 +801,7 @@ yii migrate/redo 3 # redo the last 3 applied migrations
Since Yii 2.0.13 you can delete all tables and foreign keys from the database and apply all migrations from the beginning.
```
-yii migrate/fresh # Truncate the database and apply all migrations from the beginning.
+yii migrate/fresh # truncate the database and apply all migrations from the beginning
```
## Listing Migrations
@@ -935,7 +935,7 @@ return [
];
```
-> Note: migrations applied from different namespaces will create a **single** migration history, e.g. you might be
+> Note: Migrations applied from different namespaces will create a **single** migration history, e.g. you might be
unable to apply or revert migrations from particular namespace only.
While operating namespaced migrations: creating new, reverting and so on, you should specify full namespace before
@@ -943,10 +943,10 @@ migration name. Note that backslash (`\`) symbol is usually considered a special
to escape it properly to avoid shell errors or incorrect behavior. For example:
```
-yii migrate/create 'app\\migrations\\createUserTable'
+yii migrate/create app\\migrations\\CreateUserTable
```
-> Note: migrations specified via [[yii\console\controllers\MigrateController::migrationPath|migrationPath]] can not
+> Note: Migrations specified via [[yii\console\controllers\MigrateController::migrationPath|migrationPath]] can not
contain a namespace, namespaced migration can be applied only via [[yii\console\controllers\MigrateController::migrationNamespaces]]
property.
@@ -956,6 +956,32 @@ This is mainly added to be used in existing projects which use migrations from d
from external sources, like Yii extensions developed by other developers,
which can not be changed to use namespaces easily when starting to use the new approach.
+#### Generating namespaced migrations
+
+Namespaced migrations follow "CamelCase" naming pattern `M` (for example `M190720100234CreateUserTable`).
+When generating such migration remember that table name will be converted from "CamelCase" format to "underscored". For
+example:
+
+```
+yii migrate/create app\\migrations\\DropGreenHotelTable
+```
+
+generates migration within namespace `app\migrations` dropping table `green_hotel` and
+
+```
+yii migrate/create app\\migrations\\CreateBANANATable
+```
+
+generates migration within namespace `app\migrations` creating table `b_a_n_a_n_a`.
+
+If table's name is mixed-cased (like `studentsExam`) you need to precede the name with underscore:
+
+```
+yii migrate/create app\\migrations\\Create_studentsExamTable
+```
+
+This generates migration within namespace `app\migrations` creating table `studentsExam`.
+
### Separated Migrations
Sometimes using single migration history for all project migrations is not desirable. For example: you may install some
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 9f8e7330ac..8f4d9161c5 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -7,6 +7,7 @@ Yii Framework 2 Change Log
- Bug #17219: Fixed quoting of table names with spaces in MSSQL (alexkart)
- Bug #10020: Fixed quoting of column names with dots in MSSQL (alexkart)
- Bug #17424: Subdomain support for `User::loginRequired` (alex-code)
+- Bug #17437: Fixed generating namespaced migrations (bizley)
2.0.23 July 16, 2019
diff --git a/framework/console/controllers/BaseMigrateController.php b/framework/console/controllers/BaseMigrateController.php
index 2a85a82e5c..704bba7450 100644
--- a/framework/console/controllers/BaseMigrateController.php
+++ b/framework/console/controllers/BaseMigrateController.php
@@ -17,6 +17,7 @@ use yii\console\ExitCode;
use yii\db\MigrationInterface;
use yii\helpers\Console;
use yii\helpers\FileHelper;
+use yii\helpers\Inflector;
/**
* BaseMigrateController is the base class for migrate controllers.
@@ -661,17 +662,15 @@ abstract class BaseMigrateController extends Controller
if (strpos($name, '\\') !== false) {
$namespace = substr($name, 0, strrpos($name, '\\'));
$name = substr($name, strrpos($name, '\\') + 1);
- } else {
- if ($this->migrationPath === null) {
- $migrationNamespaces = $this->migrationNamespaces;
- $namespace = array_shift($migrationNamespaces);
- }
+ } elseif ($this->migrationPath === null) {
+ $migrationNamespaces = $this->migrationNamespaces;
+ $namespace = array_shift($migrationNamespaces);
}
if ($namespace === null) {
$class = 'm' . gmdate('ymd_His') . '_' . $name;
} else {
- $class = 'M' . gmdate('ymdHis') . ucfirst($name);
+ $class = 'M' . gmdate('ymdHis') . Inflector::camelize($name);
}
return [$namespace, $class];
diff --git a/framework/console/controllers/MigrateController.php b/framework/console/controllers/MigrateController.php
index 836781301a..4d89ebb85e 100644
--- a/framework/console/controllers/MigrateController.php
+++ b/framework/console/controllers/MigrateController.php
@@ -13,6 +13,7 @@ use yii\db\Query;
use yii\di\Instance;
use yii\helpers\ArrayHelper;
use yii\helpers\Console;
+use yii\helpers\Inflector;
/**
* Manages application migrations.
@@ -375,6 +376,26 @@ class MigrateController extends BaseMigrateController
return static::MAX_NAME_LENGTH;
}
+ /**
+ * Normalizes table name for generator.
+ * When name is preceded with underscore name case is kept - otherwise it's converted from camelcase to underscored.
+ * Last underscore is always trimmed so if there should be underscore at the end of name use two of them.
+ * @param string $name
+ * @return string
+ */
+ private function normalizeTableName($name)
+ {
+ if (substr($name, -1) === '_') {
+ $name = substr($name, 0, -1);
+ }
+
+ if (strpos($name, '_') === 0) {
+ return substr($name, 1);
+ }
+
+ return Inflector::underscore($name);
+ }
+
/**
* {@inheritdoc}
* @since 2.0.8
@@ -386,13 +407,20 @@ class MigrateController extends BaseMigrateController
$foreignKeys = $parsedFields['foreignKeys'];
$name = $params['name'];
+ if ($params['namespace']) {
+ $name = substr($name, strrpos($name, '\\') + 1);
+ }
$templateFile = $this->templateFile;
$table = null;
- if (preg_match('/^create_junction(?:_table_for_|_for_|_)(.+)_and_(.+)_tables?$/', $name, $matches)) {
+ if (preg_match(
+ '/^create_?junction_?(?:table)?_?(?:for)?(.+)_?and(.+)_?tables?$/i',
+ $name,
+ $matches
+ )) {
$templateFile = $this->generatorTemplateFiles['create_junction'];
- $firstTable = $matches[1];
- $secondTable = $matches[2];
+ $firstTable = $this->normalizeTableName($matches[1]);
+ $secondTable = $this->normalizeTableName($matches[2]);
$fields = array_merge(
[
@@ -420,20 +448,20 @@ class MigrateController extends BaseMigrateController
$foreignKeys[$firstTable . '_id']['column'] = null;
$foreignKeys[$secondTable . '_id']['column'] = null;
$table = $firstTable . '_' . $secondTable;
- } elseif (preg_match('/^add_(.+)_columns?_to_(.+)_table$/', $name, $matches)) {
+ } elseif (preg_match('/^add(.+)columns?_?to(.+)table$/i', $name, $matches)) {
$templateFile = $this->generatorTemplateFiles['add_column'];
- $table = $matches[2];
- } elseif (preg_match('/^drop_(.+)_columns?_from_(.+)_table$/', $name, $matches)) {
+ $table = $this->normalizeTableName($matches[2]);
+ } elseif (preg_match('/^drop(.+)columns?_?from(.+)table$/i', $name, $matches)) {
$templateFile = $this->generatorTemplateFiles['drop_column'];
- $table = $matches[2];
- } elseif (preg_match('/^create_(.+)_table$/', $name, $matches)) {
+ $table = $this->normalizeTableName($matches[2]);
+ } elseif (preg_match('/^create(.+)table$/i', $name, $matches)) {
$this->addDefaultPrimaryKey($fields);
$templateFile = $this->generatorTemplateFiles['create_table'];
- $table = $matches[1];
- } elseif (preg_match('/^drop_(.+)_table$/', $name, $matches)) {
+ $table = $this->normalizeTableName($matches[1]);
+ } elseif (preg_match('/^drop(.+)table$/i', $name, $matches)) {
$this->addDefaultPrimaryKey($fields);
$templateFile = $this->generatorTemplateFiles['drop_table'];
- $table = $matches[1];
+ $table = $this->normalizeTableName($matches[1]);
}
foreach ($foreignKeys as $column => $foreignKey) {
diff --git a/framework/views/addColumnMigration.php b/framework/views/addColumnMigration.php
index cadeb63345..e3a781a22a 100644
--- a/framework/views/addColumnMigration.php
+++ b/framework/views/addColumnMigration.php
@@ -9,9 +9,6 @@
/* @var $table string the name table */
/* @var $fields array the fields */
-preg_match('/^add_(.+)_columns?_to_(.+)_table$/', $name, $matches);
-$columns = str_replace('_column_', ', ', $matches[1]);
-
echo " to table `= $table ?>`.
+ * Handles adding columns to table `= $table ?>`.
= $this->render('_foreignTables', [
'foreignKeys' => $foreignKeys,
]) ?>
diff --git a/framework/views/dropColumnMigration.php b/framework/views/dropColumnMigration.php
index be1a4d2d96..b91f5938a6 100644
--- a/framework/views/dropColumnMigration.php
+++ b/framework/views/dropColumnMigration.php
@@ -8,8 +8,6 @@
/* @var $namespace string the new migration class namespace */
/* @var $table string the name table */
/* @var $fields array the fields */
-preg_match('/^drop_(.+)_columns?_from_(.+)_table$/', $name, $matches);
-$columns = $matches[1];
echo " from table `= $table ?>`.
+ * Handles dropping columns from table `= $table ?>`.
= $this->render('_foreignTables', [
'foreignKeys' => $foreignKeys,
]) ?>
diff --git a/tests/data/console/migrate_create/add_columns_fk.php b/tests/data/console/migrate_create/add_columns_fk.php
index 07004834bb..c80f4c9009 100644
--- a/tests/data/console/migrate_create/add_columns_fk.php
+++ b/tests/data/console/migrate_create/add_columns_fk.php
@@ -8,7 +8,7 @@
return <<createTable('{{%post_tag}}', [
- 'post_id' => \$this->integer(),
- 'tag_id' => \$this->integer(),
- 'PRIMARY KEY(post_id, tag_id)',
+ \$this->createTable('{{%{junctionTable}}}', [
+ '{firstTable}_id' => \$this->integer(),
+ '{secondTable}_id' => \$this->integer(),
+ 'PRIMARY KEY({firstTable}_id, {secondTable}_id)',
]);
- // creates index for column `post_id`
+ // creates index for column `{firstTable}_id`
\$this->createIndex(
- '{{%idx-post_tag-post_id}}',
- '{{%post_tag}}',
- 'post_id'
+ '{{%idx-{junctionTable}-{firstTable}_id}}',
+ '{{%{junctionTable}}}',
+ '{firstTable}_id'
);
- // add foreign key for table `{{%post}}`
+ // add foreign key for table `{{%{firstTable}}}`
\$this->addForeignKey(
- '{{%fk-post_tag-post_id}}',
- '{{%post_tag}}',
- 'post_id',
- '{{%post}}',
+ '{{%fk-{junctionTable}-{firstTable}_id}}',
+ '{{%{junctionTable}}}',
+ '{firstTable}_id',
+ '{{%{firstTable}}}',
'id',
'CASCADE'
);
- // creates index for column `tag_id`
+ // creates index for column `{secondTable}_id`
\$this->createIndex(
- '{{%idx-post_tag-tag_id}}',
- '{{%post_tag}}',
- 'tag_id'
+ '{{%idx-{junctionTable}-{secondTable}_id}}',
+ '{{%{junctionTable}}}',
+ '{secondTable}_id'
);
- // add foreign key for table `{{%tag}}`
+ // add foreign key for table `{{%{secondTable}}}`
\$this->addForeignKey(
- '{{%fk-post_tag-tag_id}}',
- '{{%post_tag}}',
- 'tag_id',
- '{{%tag}}',
+ '{{%fk-{junctionTable}-{secondTable}_id}}',
+ '{{%{junctionTable}}}',
+ '{secondTable}_id',
+ '{{%{secondTable}}}',
'id',
'CASCADE'
);
@@ -70,31 +70,31 @@ class {$class} extends Migration
*/
public function safeDown()
{
- // drops foreign key for table `{{%post}}`
+ // drops foreign key for table `{{%{firstTable}}}`
\$this->dropForeignKey(
- '{{%fk-post_tag-post_id}}',
- '{{%post_tag}}'
+ '{{%fk-{junctionTable}-{firstTable}_id}}',
+ '{{%{junctionTable}}}'
);
- // drops index for column `post_id`
+ // drops index for column `{firstTable}_id`
\$this->dropIndex(
- '{{%idx-post_tag-post_id}}',
- '{{%post_tag}}'
+ '{{%idx-{junctionTable}-{firstTable}_id}}',
+ '{{%{junctionTable}}}'
);
- // drops foreign key for table `{{%tag}}`
+ // drops foreign key for table `{{%{secondTable}}}`
\$this->dropForeignKey(
- '{{%fk-post_tag-tag_id}}',
- '{{%post_tag}}'
+ '{{%fk-{junctionTable}-{secondTable}_id}}',
+ '{{%{junctionTable}}}'
);
- // drops index for column `tag_id`
+ // drops index for column `{secondTable}_id`
\$this->dropIndex(
- '{{%idx-post_tag-tag_id}}',
- '{{%post_tag}}'
+ '{{%idx-{junctionTable}-{secondTable}_id}}',
+ '{{%{junctionTable}}}'
);
- \$this->dropTable('{{%post_tag}}');
+ \$this->dropTable('{{%{junctionTable}}}');
}
}
diff --git a/tests/framework/console/controllers/MigrateControllerTest.php b/tests/framework/console/controllers/MigrateControllerTest.php
index 5651dc0b5f..4f3e1ea530 100644
--- a/tests/framework/console/controllers/MigrateControllerTest.php
+++ b/tests/framework/console/controllers/MigrateControllerTest.php
@@ -11,6 +11,7 @@ use Yii;
use yii\console\controllers\MigrateController;
use yii\db\Migration;
use yii\db\Query;
+use yii\helpers\Inflector;
use yiiunit\TestCase;
/**
@@ -57,96 +58,300 @@ class MigrateControllerTest extends TestCase
return $query->from('migration')->all();
}
- public function assertFileContent($expectedFile, $class, $table)
+ public function assertFileContent($expectedFile, $class, $table, $namespace = null)
{
+ if ($namespace) {
+ $namespace = "namespace {$namespace};\n\n";
+ }
$expected = include Yii::getAlias("@yiiunit/data/console/migrate_create/$expectedFile.php");
$expected = str_replace('{table}', $table, $expected);
- $this->assertEqualsWithoutLE($expected, $this->parseNameClassMigration($class, $table));
+ $this->assertEqualsWithoutLE($expected, $this->parseNameClassMigration($class));
}
protected function assertCommandCreatedFile($expectedFile, $migrationName, $table, $params = [])
{
- $class = 'm' . gmdate('ymd_His') . '_' . $migrationName;
$params[0] = $migrationName;
- $this->runMigrateControllerAction('create', $params);
- $this->assertFileContent($expectedFile, $class, $table);
+ list($config, $namespace, $class) = $this->prepareMigrationNameData($migrationName);
+
+ $this->runMigrateControllerAction('create', $params, $config);
+ $this->assertFileContent($expectedFile, $class, $table, $namespace);
}
- // Tests :
-
- public function testGenerateDefaultMigration()
+ public function assertFileContentJunction($expectedFile, $class, $junctionTable, $firstTable, $secondTable, $namespace = null)
{
- $this->assertCommandCreatedFile('default', 'DefaultTest', 'default');
+ if ($namespace) {
+ $namespace = "namespace {$namespace};\n\n";
+ }
+ $expected = include Yii::getAlias("@yiiunit/data/console/migrate_create/$expectedFile.php");
+ $expected = str_replace(
+ ['{junctionTable}', '{firstTable}', '{secondTable}'],
+ [$junctionTable, $firstTable, $secondTable],
+ $expected
+ );
+ $this->assertEqualsWithoutLE($expected, $this->parseNameClassMigration($class));
}
- public function testGenerateCreateMigration()
+ protected function assertCommandCreatedJunctionFile($expectedFile, $migrationName, $junctionTable, $firstTable, $secondTable)
{
- $tables = [
- 'test',
- 'TEST',
- ];
- foreach ($tables as $table) {
- $migrationName = 'create_' . $table . '_table';
+ list($config, $namespace, $class) = $this->prepareMigrationNameData($migrationName);
- $this->assertCommandCreatedFile('create_test', $migrationName, $table);
+ $this->runMigrateControllerAction('create', [$migrationName], $config);
+ $this->assertFileContentJunction($expectedFile, $class, $junctionTable, $firstTable, $secondTable, $namespace);
+ }
- $this->assertCommandCreatedFile('create_fields', $migrationName, $table, [
+ protected function prepareMigrationNameData($migrationName)
+ {
+ $config = [];
+ $namespace = null;
+
+ $lastSlashPosition = strrpos($migrationName, '\\');
+ if ($lastSlashPosition !== false) {
+ $config = [
+ 'migrationPath' => null,
+ 'migrationNamespaces' => [$this->migrationNamespace],
+ ];
+ $class = 'M' . gmdate('ymdHis') . Inflector::camelize(substr($migrationName, $lastSlashPosition + 1));
+ $namespace = substr($migrationName, 0, $lastSlashPosition);
+ } else {
+ $class = 'm' . gmdate('ymd_His') . '_' . $migrationName;
+ }
+
+ return [$config, $namespace, $class];
+ }
+
+ /**
+ * @return array
+ */
+ public function generateMigrationDataProvider()
+ {
+ $params = [
+ 'create_fields' => [
'fields' => 'title:string(10):notNull:unique:defaultValue("test"),
body:text:notNull,
price:money(11,2):notNull,
parenthesis_in_comment:string(255):notNull:comment(\'Name of set (RU)\')',
- ]);
-
- $this->assertCommandCreatedFile('create_title_pk', $migrationName, $table, [
+ ],
+ 'create_title_pk' => [
'fields' => 'title:primaryKey,body:text:notNull,price:money(11,2)',
- ]);
-
- $this->assertCommandCreatedFile('create_unsigned_pk', $migrationName, $table, [
+ ],
+ 'create_unsigned_pk' => [
'fields' => 'brand_id:primaryKey:unsigned',
- ]);
-
- $this->assertCommandCreatedFile('create_unsigned_big_pk', $migrationName, $table, [
+ ],
+ 'create_unsigned_big_pk' => [
'fields' => 'brand_id:bigPrimaryKey:unsigned',
- ]);
-
- $this->assertCommandCreatedFile('create_id_pk', $migrationName, $table, [
+ ],
+ 'create_id_pk' => [
'fields' => 'id:primaryKey,
address:string,
address2:string,
email:string',
- ]);
-
- $this->assertCommandCreatedFile('create_foreign_key', $migrationName, $table, [
+ ],
+ 'create_foreign_key' => [
'fields' => 'user_id:integer:foreignKey,
product_id:foreignKey:integer:unsigned:notNull,
order_id:integer:foreignKey(user_order):notNull,
created_at:dateTime:notNull',
- ]);
-
- $this->assertCommandCreatedFile('create_prefix', $migrationName, $table, [
+ ],
+ 'create_prefix' => [
'useTablePrefix' => true,
'fields' => 'user_id:integer:foreignKey,
product_id:foreignKey:integer:unsigned:notNull,
order_id:integer:foreignKey(user_order):notNull,
created_at:dateTime:notNull',
- ]);
- }
+ ],
+ 'create_title_with_comma_default_values' => [
+ 'fields' => 'title:string(10):notNull:unique:defaultValue(",te,st"),
+ body:text:notNull:defaultValue(",test"),
+ test:custom(11,2,"s"):notNull',
+ ],
+ 'create_field_with_colon_default_values' => [
+ 'fields' => 'field_1:dateTime:notNull:defaultValue(\'0000-00-00 00:00:00\'),
+ field_2:string:defaultValue(\'default:value\')',
+ ],
+ 'drop_fields' => [
+ 'fields' => 'body:text:notNull,price:money(11,2)',
+ ],
+ 'add_columns_test' => [
+ 'fields' => 'title:string(10):notNull,
+ body:text:notNull,
+ price:money(11,2):notNull,
+ created_at:dateTime',
+ ],
+ 'add_columns_fk' => [
+ 'fields' => 'user_id:integer:foreignKey,
+ product_id:foreignKey:integer:unsigned:notNull,
+ order_id:integer:foreignKey(user_order):notNull,
+ created_at:dateTime:notNull',
+ ],
+ 'add_columns_prefix' => [
+ 'useTablePrefix' => true,
+ 'fields' => 'user_id:integer:foreignKey,
+ product_id:foreignKey:integer:unsigned:notNull,
+ order_id:integer:foreignKey(user_order):notNull,
+ created_at:dateTime:notNull',
+ ],
+ 'add_two_columns_test' => [
+ 'fields' => 'field_1:string(10):notNull,
+ field_2:text:notNull',
+ ],
+ 'drop_columns_test' => [
+ 'fields' => 'title:string(10):notNull,body:text:notNull,
+ price:money(11,2):notNull,
+ created_at:dateTime',
+ ],
+ ];
- // @see https://github.com/yiisoft/yii2/issues/10876
- foreach (['products_from_store', 'products_FROM_store'] as $table) {
- $this->assertCommandCreatedFile('drop_products_from_store_table', 'drop_' . $table . '_table', $table);
- }
- // @see https://github.com/yiisoft/yii2/issues/11461
- $this->assertCommandCreatedFile('create_title_with_comma_default_values', 'create_test_table', 'test', [
- 'fields' => 'title:string(10):notNull:unique:defaultValue(",te,st"),
- body:text:notNull:defaultValue(",test"),
- test:custom(11,2,"s"):notNull',
- ]);
+ return [
+ ['default', 'DefaultTest', 'default', []],
- $this->assertCommandCreatedFile('create_field_with_colon_default_values', 'create_test_table', 'test', [
- 'fields' => 'field_1:dateTime:notNull:defaultValue(\'0000-00-00 00:00:00\'),
- field_2:string:defaultValue(\'default:value\')',
- ]);
+ // underscore + table name = case keeped
+ ['create_test', 'create_test_table', 'test', []],
+ ['create_test', 'create_test__table', 'test_', []],
+ ['create_test', 'create_TEST_table', 'TEST', []],
+ ['create_test', 'Create_tEsTTable', 'tEsT', []],
+
+ // no underscore + table name = camelcase converted to underscore
+ ['create_test', 'CreateTestTable', 'test', []],
+ ['create_test', 'createTest_table', 'test', []],
+ ['create_test', 'createTe_st_table', 'te_st', []],
+ ['create_test', 'createTest__table', 'test_', []],
+ ['create_test', 'createTESTtable', 't_e_s_t', []],
+
+ ['create_fields', 'create_test_table', 'test', $params['create_fields']],
+ ['create_fields', 'create_TEST_table', 'TEST', $params['create_fields']],
+ ['create_title_pk', 'create_test_table', 'test', $params['create_title_pk']],
+ ['create_title_pk', 'create_TEST_table', 'TEST', $params['create_title_pk']],
+ ['create_unsigned_pk', 'create_test_table', 'test', $params['create_unsigned_pk']],
+ ['create_unsigned_pk', 'create_TEST_table', 'TEST', $params['create_unsigned_pk']],
+ ['create_unsigned_big_pk', 'create_test_table', 'test', $params['create_unsigned_big_pk']],
+ ['create_unsigned_big_pk', 'create_TEST_table', 'TEST', $params['create_unsigned_big_pk']],
+ ['create_id_pk', 'create_test_table', 'test', $params['create_id_pk']],
+ ['create_id_pk', 'create_TEST_table', 'TEST', $params['create_id_pk']],
+ ['create_foreign_key', 'create_test_table', 'test', $params['create_foreign_key']],
+ ['create_foreign_key', 'create_TEST_table', 'TEST', $params['create_foreign_key']],
+ ['create_prefix', 'create_test_table', 'test', $params['create_prefix']],
+ ['create_prefix', 'create_TEST_table', 'TEST', $params['create_prefix']],
+
+ // @see https://github.com/yiisoft/yii2/issues/11461
+ ['create_title_with_comma_default_values', 'create_test_table', 'test', $params['create_title_with_comma_default_values']],
+ ['create_field_with_colon_default_values', 'create_test_table', 'test', $params['create_field_with_colon_default_values']],
+
+ ['drop_test', 'drop_test_table', 'test', []],
+ ['drop_test', 'drop_test__table', 'test_', []],
+ ['drop_test', 'drop_TEST_table', 'TEST', []],
+ ['drop_test', 'Drop_tEStTable', 'tESt', []],
+ ['drop_test', 'DropTestTable', 'test', []],
+ ['drop_test', 'DropTest_Table', 'test', []],
+ ['drop_test', 'DropTest__Table', 'test_', []],
+ ['drop_test', 'DropTESTtable', 't_e_s_t', []],
+
+ ['drop_fields', 'drop_test_table', 'test', $params['drop_fields']],
+ ['drop_fields', 'drop_TEST_table', 'TEST', $params['drop_fields']],
+
+ // @see https://github.com/yiisoft/yii2/issues/10876
+ ['drop_products_from_store_table', 'drop_products_from_store_table', 'products_from_store', []],
+ ['drop_products_from_store_table', 'drop_products_FROM_store_table', 'products_FROM_store', []],
+
+ ['add_columns_test', 'add_columns_column_to_test_table', 'test', $params['add_columns_test']],
+ ['add_columns_test', 'add_columns_column_to_test__table', 'test_', $params['add_columns_test']],
+ ['add_columns_test', 'add_columns_column_to_TEST_table', 'TEST', $params['add_columns_test']],
+ ['add_columns_test', 'AddColumns_column_to_teSTtable', 'teST', $params['add_columns_test']],
+ ['add_columns_test', 'AddColumnsColumnTo_tEstTable', 'tEst', $params['add_columns_test']],
+ ['add_columns_test', 'addColumnsColumnToTestTable', 'test', $params['add_columns_test']],
+ ['add_columns_test', 'AddColumnsColumnToTest_Table', 'test', $params['add_columns_test']],
+ ['add_columns_test', 'AddCol__umnsColumnToTest__Table', 'test_', $params['add_columns_test']],
+ ['add_columns_test', 'AddColumnsColumnToTESTTable', 't_e_s_t', $params['add_columns_test']],
+
+ ['add_columns_fk', 'add_columns_column_to_test_table', 'test', $params['add_columns_fk']],
+ ['add_columns_fk', 'add_columns_column_to_TEST_table', 'TEST', $params['add_columns_fk']],
+ ['add_columns_prefix', 'add_columns_column_to_test_table', 'test', $params['add_columns_prefix']],
+ ['add_columns_prefix', 'add_columns_column_to_TEST_table', 'TEST', $params['add_columns_prefix']],
+ ['add_two_columns_test', 'add_field_1_column_field_2_column_to_test_table', 'test', $params['add_two_columns_test']],
+ ['add_two_columns_test', 'add_field_1_column_field_2_column_to_TEST_table', 'TEST', $params['add_two_columns_test']],
+
+ ['drop_columns_test', 'drop_columns_column_from_test_table', 'test', $params['add_columns_test']],
+ ['drop_columns_test', 'drop_columns_columns_from_test_table', 'test', $params['add_columns_test']],
+ ['drop_columns_test', 'drop_columns_column_from_test__table', 'test_', $params['add_columns_test']],
+ ['drop_columns_test', 'drop_columns_column_from_TEST_table', 'TEST', $params['add_columns_test']],
+ ['drop_columns_test', 'drop_columns_columns_from_TEST_table', 'TEST', $params['add_columns_test']],
+ ['drop_columns_test', 'dropColumnsColumNSFrom_TEstTable', 'TEst', $params['add_columns_test']],
+ ['drop_columns_test', 'DropFewColumnsFrom_Test_Table', 'Test', $params['add_columns_test']],
+ ['drop_columns_test', 'DropFewColumnsFromTestTable', 'test', $params['add_columns_test']],
+ ['drop_columns_test', 'DropFewColumnsFromTest_Table', 'test', $params['add_columns_test']],
+ ['drop_columns_test', 'DropFewColumnsFromTest__Table', 'test_', $params['add_columns_test']],
+ ['drop_columns_test', 'DropFewColumnsFromTeStTable', 'te_st', $params['add_columns_test']],
+ ];
+ }
+
+ /**
+ * @param string $expectedFile
+ * @param string $migrationName
+ * @param string $table
+ * @param array $params
+ * @dataProvider generateMigrationDataProvider
+ */
+ public function testGenerateMigration($expectedFile, $migrationName, $table, $params)
+ {
+ $this->migrationNamespace = 'yiiunit\runtime\test_migrations';
+
+ $this->assertCommandCreatedFile($expectedFile, $migrationName, $table, $params);
+ $this->assertCommandCreatedFile(
+ $expectedFile,
+ $this->migrationNamespace . '\\' . $migrationName,
+ $table,
+ $params
+ );
+ }
+
+ /**
+ * @return array
+ */
+ public function generateJunctionMigrationDataProvider()
+ {
+ return [
+ ['create_junction_post_and_tag_tables', 'post_tag', 'post', 'tag'],
+ ['create_junction_for_post_and_tag_tables', 'post_tag', 'post', 'tag'],
+ ['create_junction_table_for_post_and_tag_tables', 'post_tag', 'post', 'tag'],
+ ['create_junction_table_for_post_and_tag_table', 'post_tag', 'post', 'tag'],
+ ['CreateJunction_postAnd_tagTables', 'post_tag', 'post', 'tag'],
+ ['CreateJunctionFor_postAnd_tagTables', 'post_tag', 'post', 'tag'],
+ ['CreateJunctionTableFor_postAnd_tagTables', 'post_tag', 'post', 'tag'],
+ ['CreateJunctionTableFor_postAnd_tagTable', 'post_tag', 'post', 'tag'],
+ ['CreateJunctionPostAndTagTables', 'post_tag', 'post', 'tag'],
+ ['CreateJunctionPost_AndTag_Tables', 'post_tag', 'post', 'tag'],
+ ['CreateJunctionPost__AndTag__Tables', 'post__tag_', 'post_', 'tag_'],
+ ['CreateJunctionPost__AndTagTables', 'post__tag', 'post_', 'tag'],
+ ['CreateJunctionPostAndTag__Tables', 'post_tag_', 'post', 'tag_'],
+ ['CreateJunctionPostAndTaGTables', 'post_ta_g', 'post', 'ta_g'],
+ ['CreateJunctionPoStAndTagTables', 'po_st_tag', 'po_st', 'tag'],
+ ];
+ }
+
+ /**
+ * @param string $migrationName
+ * @param string $junctionTable
+ * @param string $firstTable
+ * @param string $secondTable
+ * @dataProvider generateJunctionMigrationDataProvider
+ */
+ public function testGenerateJunctionMigration($migrationName, $junctionTable, $firstTable, $secondTable)
+ {
+ $this->migrationNamespace = 'yiiunit\runtime\test_migrations';
+
+ $this->assertCommandCreatedJunctionFile(
+ 'junction_test',
+ $migrationName,
+ $junctionTable,
+ $firstTable,
+ $secondTable
+ );
+ $this->assertCommandCreatedJunctionFile(
+ 'junction_test',
+ $this->migrationNamespace . '\\' . $migrationName,
+ $junctionTable,
+ $firstTable,
+ $secondTable
+ );
}
public function testUpdatingLongNamedMigration()
@@ -190,100 +395,6 @@ class MigrateControllerTest extends TestCase
$controller->run('create', $params);
}
- public function testGenerateDropMigration()
- {
- $tables = [
- 'test',
- 'TEST',
- ];
- foreach ($tables as $table) {
- $migrationName = 'drop_' . $table . '_table';
-
- $this->assertCommandCreatedFile('drop_test', $migrationName, $table);
-
- $this->assertCommandCreatedFile('drop_fields', $migrationName, $table, [
- 'fields' => 'body:text:notNull,price:money(11,2)',
- ]);
- }
-
- // @see https://github.com/yiisoft/yii2/issues/10876
- foreach (['products_from_store', 'products_FROM_store'] as $table) {
- $this->assertCommandCreatedFile('drop_products_from_store_table', 'drop_' . $table . '_table', $table);
- }
- }
-
- public function testGenerateAddColumnMigration()
- {
- $tables = [
- 'test',
- 'TEST',
- ];
- foreach ($tables as $table) {
- $migrationName = 'add_columns_column_to_' . $table . '_table';
-
- $this->assertCommandCreatedFile('add_columns_test', $migrationName, $table, [
- 'fields' => 'title:string(10):notNull,
- body:text:notNull,
- price:money(11,2):notNull,
- created_at:dateTime',
- ]);
-
- $this->assertCommandCreatedFile('add_columns_fk', $migrationName, $table, [
- 'fields' => 'user_id:integer:foreignKey,
- product_id:foreignKey:integer:unsigned:notNull,
- order_id:integer:foreignKey(user_order):notNull,
- created_at:dateTime:notNull',
- ]);
-
- $this->assertCommandCreatedFile('add_columns_prefix', $migrationName, $table, [
- 'useTablePrefix' => true,
- 'fields' => 'user_id:integer:foreignKey,
- product_id:foreignKey:integer:unsigned:notNull,
- order_id:integer:foreignKey(user_order):notNull,
- created_at:dateTime:notNull',
- ]);
-
- $this->assertCommandCreatedFile('add_two_columns_test', 'add_field_1_column_field_2_column_to_' . $table . '_table', $table, [
- 'fields' => 'field_1:string(10):notNull,
- field_2:text:notNull',
- ]);
- }
- }
-
- public function testGenerateDropColumnMigration()
- {
- $tables = [
- 'test',
- 'TEST',
- ];
- foreach ($tables as $table) {
- $migrationNames = [
- 'drop_columns_column_from_' . $table . '_table',
- 'drop_columns_columns_from_' . $table . '_table',
- ];
- foreach ($migrationNames as $migrationName) {
- $this->assertCommandCreatedFile('drop_columns_test', $migrationName, $table, [
- 'fields' => 'title:string(10):notNull,body:text:notNull,
- price:money(11,2):notNull,
- created_at:dateTime',
- ]);
- }
- }
- }
-
- public function testGenerateCreateJunctionMigration()
- {
- $migrationNames = [
- 'create_junction_post_and_tag_tables',
- 'create_junction_for_post_and_tag_tables',
- 'create_junction_table_for_post_and_tag_tables',
- 'create_junction_table_for_post_and_tag_table',
- ];
- foreach ($migrationNames as $migrationName) {
- $this->assertCommandCreatedFile('junction_test', $migrationName, 'post_tag');
- }
- }
-
/**
* Test the migrate:fresh command.
* @dataProvider refreshMigrationDataProvider
diff --git a/tests/framework/console/controllers/MigrateControllerTestTrait.php b/tests/framework/console/controllers/MigrateControllerTestTrait.php
index e09a1880bd..c9e32bd31e 100644
--- a/tests/framework/console/controllers/MigrateControllerTestTrait.php
+++ b/tests/framework/console/controllers/MigrateControllerTestTrait.php
@@ -172,7 +172,7 @@ CODE;
{
$files = FileHelper::findFiles($this->migrationPath);
$file = file_get_contents($files[0]);
- if (preg_match('/class (m\d+_\d+_.*) extends Migration/', $file, $match)) {
+ if (preg_match('/class (m\d+_?\d+_?.*) extends Migration/i', $file, $match)) {
$file = str_replace($match[1], $class, $file);
}
$this->tearDownMigrationPath();