【Laravel】migrationを理解しよう!

どーも!

たかぽんです!

今回はLaravelでmigrationをやっていきます!

migrationとは・・・?

migrationはDBの改編記録(どういうテーブルをつくったのか、どういうカラムを追加したのか?など)をしっかり残し、いつでもその改変を戻せるようにできます。

また、Laravelの場合、わざわざSQLを叩かずともlaravelで用意されている形式でその設定を書くことで、わかりやすくテーブルの作成等ができるようになっています。

大抵、Laravelを使っていてDBも存在している場合はこのmigrationが使われているかと思います。

DBのテーブル追加やカラム追加等を行う場合、例外を除いては必ず使うようにしましょう。

では、次節からその使い方をみていきます。

migrationファイルをつくる

さて、DBのマイグレーションはLaravelフレームワークにおいてDBのテーブルやカラムを追加していく際に使うものであるとおつたえしました。

そのテーブルの作成やカラムの追加を行う際に、こう言うテーブル・カラムを追加してね!ということ、そしてもしも戻す場合はこんな処理してね!ということ記述して用意してあげる必要があります。

それがmigrationファイルです。

migrationファイルは以下のパスに対して作成されるようになっています。

"{自分のプロジェクト名}/databases/migrations/{ファイル名}.php"

では、早速マイグレーションファイルを作ってみましょう!

マイグレーションファイルを作成する際は以下のコマンドで作成できます。

(base) Takapon:MyProject taka$ php artisan make:migration create_test_table
Created Migration: 2020_10_25_092620_create_test_table
(base) Takapon:MyProject taka$

上記のコマンドを打つことで、"test"という名前のテーブルを作成するためのマイグレーションファイルのテンプレートができます。

そこへ実際にどんな処理をするのかを書いていくわけです。

では、できたファイルを探してみると...?

"2020_10_25_092620_create_test_table.php"という名前で以下のようなファイルができていました。

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateTestTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('test', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('test');
    }
}

簡単にご説明します。

まずは前半のupメソッド。

    public function up()
    {
        Schema::create('test', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

これはmigration時に呼びだされる処理で、Schema::createで第一引数のテーブルが作成できます。

第二引数にはその際に作成するカラムを指定していくことができます。

特に重要なのがこの部分です。

$table->id();
$table->timestamps();

ここで、testというテーブルのカラムにid(主キー)の追加と、created_at(いつそのカラムが作成されたか)と、updated_at(いつそのカラムが更新されたか)を追加することができます。

(timestampsで作成時刻と更新時刻のカラムが複数作成されるんです!)

これらのカラムはどんなテーブルに対しても必要になるので、Laravelの先ほどのコマンドでデフォルトで作成されるようになっています。

(BluePrint型等は一旦スルーして大丈夫です。)

そして、次はdownメソッドです。

    public function down()
    {
        Schema::dropIfExists('test');
    }

downメソッドは、もしもmigrationを実行して、後からやっぱり元に戻したい...

と言う場合にロールバックをするんですが、その際に実行される内容を設定していきます。

基本的にはupで追加・削除したものをそれぞれ削除・追加するように逆の操作を書けばOKです。

上記例では先ほど説明したようにupでは以下のことを行いました。

  • testテーブルの作成
  • testテーブルにidのカラムの追加
  • testテーブルにtimestampsカラム(created_at, updated_at)の追加

では、逆の操作としては...

  • testテーブルの削除
  • testテーブルからidカラムの削除
  • testテーブルからtimestampsカラムの削除

になりそうですね。

ただ、ちょっと待ってください。

"テーブルを削除する" = "そのテーブルのカラム全ても削除される"んです。

そのため、今回のupに対するdownとしては

Schema::dropIfExists('test');

もしtestテーブルが存在するならdrop(SQLで言うところの削除です)するだけなんですね。

このdownメソッドはmigration実行時には特に実行されません。

あくまで、ロールバックをする際に呼び出されます。

さて、ファイルは理解できましたね!

現時点では、あくまで"叩きたいSQLの設定が完了した"だけなので、まだDBへは反映されていません。

では、上記の設定のテーブルを実際に作ってみましょう!

以下のように"php artisan migrate"というコマンドを入力すると...?

(base) Takapon:MyProject taka$ php artisan migrate
Migrating: 2020_10_25_092620_create_test_table
Migrated:  2020_10_25_092620_create_test_table (0.02 seconds)

Migratedとなりましたね!

これでマイグレーションの実行は終わりです!

接続しているDBをみにいくと...

takapon-test=> show test;
ERROR:  unrecognized configuration parameter "test"
takapon-test=> \d test;
                                          Table "public.test"
   Column   |              Type              | Collation | Nullable |             Default
------------+--------------------------------+-----------+----------+----------------------------------
 id         | bigint                         |           | not null | nextval('test_id_seq'::regclass)
 created_at | timestamp(0) without time zone |           |          |
 updated_at | timestamp(0) without time zone |           |          |
Indexes:
    "test_pkey" PRIMARY KEY, btree (id)

takapon-test=> select * from test;
 id | created_at | updated_at
----+------------+------------
(0 rows)

takapon-test=>

筆者はPostgresSQLを使用しているので、その結果になりますが、show testでテーブル情報をみると...、ちゃんとできていますね。

idは主キーなのでnullにはできなくなっており、型はbigint(たくさんの数字が入るint型的な...!)、そしてcreated_at, updated_atも追加されていますね!

ただ、あくまで先ほどの設定に基づいてテーブルを作っただけなので、selectで中身をみても何も入っていません。

これがmigrationの一連の動作になります。

もちろん、カラムとして文字列型のカラムを指定することだってできます。

詳しくは公式ドキュメントの日本語訳の以下ページより、カラムタイプをご確認してみてください。

Migrationで気をつけること

さて、マイグレーションがどんなものかわかりましたか...?

Migrationファイルをいじいじしてマイグレーションを実行すればそのマイグレーションファイルのupが実行されることがわかったかと思います。

ただ、編集する際には十分に気をつけて欲しいことがあります。

テーブル名の修正をする際は、ファイル名、クラス名も修正するようにしてください。

例えば上記例の場合、"test"テーブルとしましたが、一般的にテーブル名は複数形というお決まりがあります。

(単数形だと1カラムしかデータはいらない想定になってしまうので...)

そこでtestsテーブルに変えよう!

とした場合...以下のように書き換えたらうまくいくと思いますが...

// 2020_10_25_092620_create_test_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

// 以下のクラス名を複数形に修正
class CreateTestsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        // 以下のcreateの第一引数を修正
        Schema::create('tests', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        // 削除次も修正
        Schema::dropIfExists('tests');
    }
}

これではうまくいきません。

出力は以下のような感じです。

Migrating: 2020_10_24_100132_create_test_users_table
Migrated:  2020_10_24_100132_create_test_users_table (0.01 seconds)

   Error

  Class 'CreateTestTable' not found

  at vendor/laravel/framework/src/Illuminate/Database/Migrations/Migrator.php:453
    449|     public function resolve($file)
    450|     {
    451|         $class = Str::studly(implode('_', array_slice(explode('_', $file), 4)));
    452|
  > 453|         return new $class;
    454|     }
    455|
    456|     /**
    457|      * Get all of the migration files in a given path.

      +32 vendor frames
  33  artisan:37
      Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))

合わせてファイル名も変えてあげないといけないんですね。

"2020_10_25_092620_create_tests_table.php"

そうすればうまくいくはずです。

なぜこれでエラーになるのか...と言うと、Laravelは裏で色々とやってくれています。

そして、このマイグレーションを実行する時にはファイル名からそのファイル名に対応したクラスを探し、その処理を実行をするようになっているようです。

そのため、ファイル名とクラス名に差分が生じていると、あれ?このクラスあるはずなんだけどないよ?って怒られてしまいます。

つまるところ、初心者のうちはテーブル名間違いなどをしてしまった場合、再度コマンドでテンプレートを作成するところから始めた方が無難です。

もちろん、上記を理解した上で書き換えるのは全然いいと思います!

すでに間違ったテーブルを作成した場合はその削除を行ってからファイルの編集を行うのがベターでしょう。

例えば以下のコマンドです。

(base) Takapon:MyProject taka$ php artisan migrate:rollback --step=1
Rolling back: 2020_10_25_092620_create_tests_table
Rolled back:  2020_10_25_092620_create_tests_table (0.01 seconds)


// 以下、DB操作
// DBはちゃんと消えていた
takapon-test=> select * from test;
ERROR:  relation "test" does not exist
LINE 1: select * from test;
                      ^
takapon-test=> \d test;
Did not find any relation named "test".
takapon-test=>

step数直前に何個ぶんのmigrationを削除するのかを指定することができます。

上記のrollbackを実行すると、downメソッドが呼ばれ、そのテーブルがなくなるわけです。

ロールバックをせずに、migrationでテーブルを作成して、マイグレーションファイルを削除してしまうと、その名前のDBを消すのが困難になります...(直接DBから削除とかになってしまう)

そのため、もし間違ってマイグレーションファイルを作成、マイグレーションしてしまった場合...

ロールバックしてDBの辻褄を合わせて(マイグレーションでupにtable作成を指定しているならDBにはtableが存在しない状態にして)、マイグレーションファイルを消すのが良さそうです。

つまり、upの操作だけ行った状態でファイル消さないように!って感じですね!

兎にも角にも、以下のようなエラーの原因になってしまうので、migrationの指定とDBの状態に解離が発生しないように気をつけてください。

  • migrationファイルでは作成しているはずのテーブルがDBには存在しない... -> ロールバック時に存在しないDBを削除しようとしてエラー
  • migrationファイルでは削除しているはずのテーブルがDBに存在する... -> DB追加のmigration実行時にすでにテーブルが存在していてエラー

また、さらにもう一点、マイグレーションは基本的に一度設定が決まって一度実行されたら編集しないようにした方がいいです。

というのも一度マイグレーションが実行されると、そのファイル名のマイグレーションは2度と行われません。

(すでにマイグレーションが行われているので、ファイルを編集されても、マイグレーション済と判定されてしまう。)

もしmigrationファイルを編集して、migrationを実行しても、"already up to date"反映されないのであれば、そこに気をつけるようにしてください。

(一応、ファイル名を少しでも変更すれば新規でマイグレーションが走るようになりますが、そういった解決方ではなく、ロールバック後に改めて同名でマイグレーションを行うのをお勧めします。)

まとめ

今回はmigrationについてみていきました!

基本的なところを理解していけばそんなに難しくはありませんが、触り始めだとどうしてもとっつきづらい内容になっています...

是非テスト環境等でたくさん触って、エラーおこして体で覚えていきましょう...!w

それでわ!

おすすめの記事