Laravel ではデータベース管理にマイグレーション機能が提供されています。

Laravel 5.5 (5.4.17 ? ) より追加された新オプションの migration:fresh コマンド説明と、複数 DB 利用時にエラーが発生する場合の対処法を記載いたします。

マイグレーションの説明、導入については次をご覧ください。

[Laravel]はじめてのマイグレーション
atuweb.net

fresh は何をするコマンド ?

「マイグレーションをやりなおす」ためのコマンドには、すでに次のものがあります。

  • migrate:rollback
  • migrate:reset

ではなぜ、Laravel 5.5 (5.4.17 ? ) より、新オプション migrete:fresh が追加されたのでしょうか。

migrate:rollback

migrate:rollback は 「Migrate クラスを 1つずつ参照してローロバック 」を行います。

これは、実行済み Migrate クラスを参照して down() を実行しています。
そのため、Migrate クラスがdatabase/migrations以下に存在する必要があります。


これらは、例えば Git でブランチを切り替えた場合などに上手くいかなくなってしまう事がありました。
具体的には、「ブランチの変更でファイル構成が変わり、実行済み Migrate クラスがなくなってしまう」ような場合、クラスが見つからずにロールバックが失敗する、というようなケースです。

migrete:fresh

migrete:fresh は「データベースの全テーブルの Drop 後に、migrate」を行います。
Drop に関して Migrate クラスを参照しない、というのが従来のオプションと異なる点です。

参考

Laravel News : Laravel Migrate Fresh Command
https://laravel-news.com/migrate-fresh

クラス参照がなくなるため、前述のような場合であってもエラーとはならずに DB 再構築が楽になりました !

再構築系 migrate オプションまとめ

関連するコマンドと処理をまとめます。

コマンド 意味
migrete:rollback Migrate クラスの down() を実行する
migrete:reset 全 Migrate クラスの down() を実行する
migrete:refresh migrete:reset 実行後、migrate を実行する
migrete:fresh migrete:reset 実行後、migrate を実行する
migrete:fresh データベースの全テーブルを Drop 後、migrate を実行する

意図しないエラーが発生

Laravel 5.5 がリリースされて、プロジェクトをバージョンアップし、ワクワクしながらコマンドを実行したところ、DB エラーが発生し、期待通りに再構築ができませんでした。

エラーの内容は「テーブル Hoge はすでに存在しています」というものです。
私は「migratin:fresh を使えば DB の再構築が楽になるな」と期待してしまっていただけに、戸惑いが隠せません。


試しに新規プロジェクトを構築して実験したところ、期待通りに migrete:fresh が完了しました。

その違いは「 複数のデータベースを管理しているかどうか 」のようです。

migrate:fresh の処理は複数 DB を考慮していない

migrate:fresh コマンドの実行ファイルをみてみたところ、テーブルの Drop を行うのは 1つ のデータベースのみである事が読み取れました。

Illuminate\Database\Console\Migrations\FreshCommand.php の該当処理を抜粋します。

public function handle()
{
    if (! $this->confirmToProceed()) {
        return;
    }
    $this->dropAllTables(
        $database = $this->input->getOption('database')
    );
    $this->info('Dropped all tables successfully.');
    $this->call('migrate', [
        '--database' => $database,
        '--path' => $this->input->getOption('path'),
        '--force' => true,
    ]);
    if ($this->needsSeeding()) {
        $this->runSeeder($database);
    }
}

はい、どう見ても複数データベースに対応しているように見えませんね。


テーブルのドロップだけではなく、再構築までセットで行うという便利コマンドを目指した事が、逆にトラブルになっていますね。

今回の私の例でいうと次の感じですね。

  • データベース A はテーブルを Drop が完了
  • データベース B はテーブルの Drop は行わずにそのまま
  • migrate が実行される
    – データベース B にテーブルが残っているため Create Table が失敗

オプションを駆使して対処する

複数 DB を定義してしまっている私のプロジェクトで、マイグレーション関連は次のように構成しております。

  • database/migrations 以下に全てのデータベースのマイグレーション
  • database/seeds/DatabaseSeeder にすべてのシーダーを定義

リリース済みプロジェクトのため、このあたりのファイル構成を変更するのはかなりやばいです、多分。


migrate:fresh のオプションを眺めてみると、接続先データベースやマイグレーションファイル群のパスを指定できる事がわかりました。

試しに次のようなコマンドを実行すると、「 テーブルの Drop は実行するけれども、再構築は行わない 」というように制御する事ができました。

$ php artisan migrate:fresh --database=mysql_fuga --path=dummy

Dropped all tables successfully.
Migration table created successfully.
Nothing to migrate.

dummy というディレクトリがないため、migrate が実行されず、テーブルのドロップのみを実行する事ができるんですねー。HACK (汚い)ですねー。


ちょっと手間はかかりますが、これを組み合わせて次のようにする事でエラーなく migrate:fresh を実行できるようになりました。

$ php artisan migrate:fresh --database=mysql_fuga --path=dummy
$ php artisan migrate:fresh --seed

「デフォルトDB以外をオプション付きで実行し、最後にデフォルトDBを実行する」のがポイントです。

正解の運用

今回 migrate:fresh の実装から「マイグレーションのファイル構成はこうした方が良い」というのが見えてきました。

こんな感じでいかがでしょうか。

ファイル構成

  • database/migrations 以下に、データベース単位でサブディレクトリを設ける
  • database/seeds/DatabaseSeeder クラスをデータベース単位で分ける

migrate と シーダーの実行コマンド

$ php artisan migrate --database=mysql_hoge --path=database/migrations/hoge
$ php artisan db:seed --class=HogeDatabaseSeeder

migrate に –seed オプションが存在しますが、シーダーで実行するクラスの指定ができず、デフォルトが呼ばれてしまいます。
そのため、面倒でもdb:seedは別途実行する必要があります。

migrate:freshの実行コマンド

$ php artisan migrate:fresh \
  --database=mysql_hoge \
  --path=database/migrations/hoge \
  --seed --seeder=HogeDatabaseSeeder

おわりに

複数 DB を扱う際、Migrate クラスに DB 接続先名を定義すればよく、こんなところでトラブルになるとは想像もしていませんでしたが、なんとか対処できてよかったです。

Linux 設計指針のように、「 1つ の処理で多くのことをしない 」ことは、やはり大事なんだなと再確認しました。

この記事は tomita@atuweb が執筆しました。


PHPフレームワーク Laravel入門

掌田津耶乃
出版社:秀和システム  発売日:2017-09-16

Amazonで詳細を見る

2017年10月21日:タイトルを『[Laravel] 複数 DB で migratin:fresh がコケる件』より変更