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 ``. + * Handles adding columns to table ``. 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 ``. + * Handles dropping columns from table ``. 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();