【Laravel】 マイグレーションでテーブルの作成や編集をする
Laravelでは、マイグレーションを活用することでデータベースのテーブル構造をバージョン管理できます。
目次
マイグレーションする
早速マイグレーションしてみましょう。
マイグレーションの作成
マイグレーションは、artisan
を使用するのが楽です。
php artisan make:migration create_notes_table
実行すると、app/database/migrations/
ディレクトリ内にファイルが作成されています。
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('notes', function (Blueprint $table) { $table->id(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('notes'); } };
up()
はマイグレーションを実行する際に実行される部分、down
はマイグレーションをロールバックする際に実行される部分です。
create_xxyys_table
とすると、自動的にxxyys
という名前のテーブル名で定義されます。そのため、ファイル名は必ずこの構造にしておきましょう。
マイグレーションの実行
先に下の内容でテーブル内容を変えてからの方が楽です。
先に走らせてしまっても、1つロールバックすればよいだけではあります。
マイグレーションを作成したら、それを実行してデータベースを更新します。
# 未処理の全てのマイグレーションを処理 php artisan migrate # 破壊的変更がある際のyes noの選択を省略する場合 php artisan migrate --force
あとはデータベースを確認してテーブルが作成されていれば完了です。
その他のマイグレーションのコマンド
マイグレーションをロールバックするなどの操作もあります。開発環境ではmigrate:fresh
を、本番では--force
を付けるといったことはよくあると思います。
マイグレーションは、順番に実行するものです。そのため、特定のマイグレーションだけをロールバックしたり、実行したりといったことは行うべきではありません。
特定の1つのファイルをロールバックするのではなく、それに相当するマイグレーションを作成するべきです。
# どのマイグレーションを実行済みか表示 php artisan migrate:status # 最後のマイグレーションをロールバック php artisan migrate:rollback --step=1 # 最後から3つならstepを3に php artisan migrate:rollback --step=3 # テーブル構造をダンプ (dumpのため、開発環境用) php artisan schema:dump # 全てのマイグレーションをロールバック (全部down) php artisan migrate:reset # 全てのマイグレーションをロールバックして再実行 (全部downして全部up) php artisan migrate:refresh # 指定個のマイグレーションをロールバックして再実行 (全部downして全部up) php artisan migrate:refresh --step=1 # テーブルを全部削除してマイグレーションし直し (単純にテーブルを削除して全部up) php artisan migrate:fresh # 指定したマイグレーションだけ実行 (うまく行かない場合も) php artisan migrate:refresh --step=1 --path=/database/migrations/ファイル名
テーブルの定義
マイグレーションファイルを作った直後ではid
とtimestamp
しかありません。ここにテーブル定義を追加していくことになります。
カラムの種類
例えば、最初からある$table->id()
のid
はカラムの種類の1つで、AUTO_INCREMENT
なBIGINT
で符号なしの主キーとなります。
また、ほとんどの種類で第1引数にはテーブルのカラム名が入ります。
// ... return new class extends Migration { public function up() { Schema::create('notes', function (Blueprint $table) { $table->id(); // 符号なしBigIntegerの主キー $table->timestamps(); // created_atとupdated_at $table->integer('hoge'); // 整数 $table->string('content', 255); // 指定した長さのVARCHAR 第2引数を省略すると255になる $table->decimal('d', 8, 2); // 10進数の小数。第2引数は合計桁数、第3引数は小数の桁数。第4引数はunsignedかどうかのboolean $table->float('f', 8, 2, false);// 2進数の小数。引数は上と同じ $table->boolean('b'); // 真偽値 $table->text('note_text'); // 文字列 $table->longText('note_text2'); // 長い文字列 $table->timestamp('last_access'); // タイムスタンプ $table->enum('content_type', ['text', 'html']); // 列挙型 }); } };
https://readouble.com/laravel/9.x/ja/migrations.htmlの”利用可能なカラムタイプ”に種類と引数がすべて載っています。
カラム修飾子
カラム修飾子では、そのカラムの位置やコメント、nullを入れられるかどうかなどを設定できます。
https://readouble.com/laravel/9.x/ja/migrations.htmlの”カラム修飾子”に種類と引数がすべて載っています。
カラム修飾子は、メソッドチェーン形式で並べます。
// ... return new class extends Migration { public function up() { Schema::create('notes', function (Blueprint $table) { $table->id(); $table->timestamps(); $table->string('content')->nullable(false)->comment('そのnoteの本文'); }); } };
メソッドチェーンは、a()->b("hoge")->c(10)->d()->e()
のように、メソッドの戻り値がクラスインスタンスであることを利用し、メソッド呼び出しの戻り値を使ってメソッドを呼び出す形式です。
カラムの初期値
レコード挿入時に値の指定がなかった場合に入る値は、default
メソッドを利用します。
このデフォルト値では、文字列や数値の場合は直接入れて問題ありませんが、null
やCURRENT_TIMESTAMP
など、数値や文字以外の場合はExpression
を使用する必要があります。
->default(new Expression('CURRENT_TIMESTAMP'))
のように、default
メソッドの引数に入れます。
// ... // useの追加を忘れないようにする use Illuminate\Database\Query\Expression; return new class extends Migration { public function up() { Schema::create('notes', function (Blueprint $table) { $table->id(); // 主キー $table->timestamps(); // created_atとupdated_at // 文字列や数値、nullの場合は、直接値を入れるだけで良い $table->string('hoge')->default('hogehoge default text あいうえお'); $table->integer('i')->default(10); $table->datetime('last_access')->nullable(true)->default(null); // 文字列以外の場合、new Expressionを使い、式として処理させる $table->dateTime('hoge_date')->default(new Expression('CURRENT_TIMESTAMP')); }); } };
データベースの制約上、BLOB、TEXT、GEOMETRY、JSONの4つは初期値を持つことができません。
テーブルの更新
テーブルの作成後にカラムを追加したり、削除したりといったことはよくあります。しかし、マイグレーションを書き換えるのはよくありません。マイグレーションのファイルはバージョンでもあり、そのマイグレーションを実行したかどうかも管理されています。
テーブルを変更したい場合、新しくマイグレーションファイルを作成して作業します。
どのマイグレーションの作成でもですが、ファイル名に_to_
を使うと、それ以降がテーブル名と判定されます。そのため、テーブル名の直前以外で_to_
は入れないようにするべきです。
カラムの追加と削除
カラムを追加する場合、作成時と同様に作りたいカラムを並べます。up
にはマイグレーション時に実行したいものを、down
にはロールバック時に実行したいものを書きます。
そのため、up
でカラムを追加する場合、down
にはカラムの削除を書くことが一般的です。
php artisan make:migration add_user_id_to_notes_table
public function up() { Schema::table('notes', function (Blueprint $table) { // 符号なし整数型のversionカラムを作成 $table->unsignedInteger('version'); }); } public function down() { Schema::table('notes', function (Blueprint $table) { // versionカラムを削除 $table->dropColumn('version'); }); }
当然ですが、カラムを削除するとそこに入っているデータも消えます。
作成したら、php artisan migrate
するとup
が実行されます。
カラム名変更
カラム名変更の場合、マイグレーションの作成時はrename_xxxx_yyyy_to_tablename_table
のようにするのがおすすめです。
# rename_content_to_text_on_notes_table のようにすると、テーブル名がtext_on_notesだと判定される php artisan make:migration rename_xxxx_yyyy_to_tablename_table
変更には、$table->renameColumn(from, to);
のように書きます。
public function up() { Schema::table('notes', function (Blueprint $table) { $table->renameColumn('content', 'text'); }); } public function down() { Schema::table('notes', function (Blueprint $table) { $table->renameColumn('text', 'content'); }); }
もしClass "Doctrine\DBAL\Driver\AbstractMySQLDriver" not found
のようなメッセージが出たら、composer require doctrine/dbal
してください。
カラムの型や属性変更
属性変更では、$table->新しい型('column name', ...)->...->change()
のように書きます。
php artisan make:migration change_type_text_to_notes_table
public function up() { Schema::table('notes', function (Blueprint $table) { $table->longText('text')->change(); $table->unsignedInteger('version')->default(100)->change(); }); } public function down() { Schema::table('notes', function (Blueprint $table) { $table->string('text')->change(); $table->unsignedInteger('version')->default(0)->change(); }); }
指定しなかった変更については変化しません。例えば、上の例でversion
にnull
周りの設定が入っても変化することはありません。
外部キー
外部キーを利用し、テーブル同士を紐付けると言ったことはよくあります。マイグレーションでは、forgin
を使用します。
事前に適当に別のテーブルを作成しておきましょう。php artisan make:migration create_groups_table
注意点として、マイグレーションはファイル名順に実行されていくため、必ず親のテーブル (紐付けられる側) を先に作る必要があるという点です。先に親のテーブルを用意しないとテーブルが存在しない状態で外部キーを紐付けようとしてしまうため、失敗します。
もし順番を変えられない、変えたくない場合などでは、先にテーブルだけ作成しておいて、後から外部キーを定義することも可能です。
ファイル名の変更は、文字通りファイル名を変えるだけです。ファイル名の頭には日付がついているはずですので、その日付を適当に変えるだけです。
しかし、マイグレーション済みのファイルは、名前を変えるべきではありません。
外部キーを定義する際は、符号なし整数型 (つまり$table->id()
で作成したもの) のカラム作成であればforeignId
を、それ以外の型や後から外部キーを付ける場合などはforeign
を用います。
Schema::create('notes', function (Blueprint $table) { $table->id(); $table->timestamps(); // group_id というカラム(big integer unsigned)を作り、groupsテーブルのidと紐付ける $table->foreignId('gruop_id') ->references('id') ->on('groups'); });
Schema::create('notes', function (Blueprint $table) { $table->id(); $table->timestamps(); // まずカラムをつくる $table->unsignedBigInteger('group_id'); // カラムに対して外部キーを定義する。 foreign(カラム名)->references(対象テーブルのカラム)->on(対象テーブル名); $table->foreign('group_id') ->references('id') ->on('groups'); });
外部キー制約
CASCADE
やRESTRICT
などの外部キー制約を付与したい場合、メソッドチェーンでonUpdate
とonDelete
つなげます。
$table->foreign('group_id') ->references('id') ->on('groups') ->onUpdate('restrict')->onDelete('restrict');
外部キー制約は、以下の4種類があります
制約名 | UPDATE | DELETE |
---|---|---|
CASCADE | 参照先を変更すると、参照側も変わる | 参照先が無くなると同時に参照側も削除される |
RESTRICT | 参照先を変更しようとするとエラー | 参照先を削除しようとするとエラー |
SET NULL | 参照先を変更するとNULLになる | 参照先を削除するとNULLになる |
NO ACTION | RESTRICTと同じ | RESTRICTと同じ |
SET NULL
を使いたい場合、該当カラムがnullable
である必要があります。
onUpdate('制約')
の代わりに、cascadeOnUpdate()
、restrictOnUpdate()
、cascadeOnDelete()
、restrictOnDelete()
、nullOnDelete()
でも同じことが可能です。
ここで、foreignId
におけるカラム修飾子は、constrained()
より前に呼ぶ必要があります。
$table->foreignId('group_id') ->nullable() ->constrained(); // constrained()->nullable() はNG
外部キーの削除
外部キーを削除する際は、$table->dropForeign(インデックス名);
、または$table->dropForeign(['カラム名', ...])
と書きます。
$table->dropForeign('notes_group_id_foreign'); $table->dropForeign(['group_id']);
ソフトデリート
データを削除する際は、本当に削除するハードデリートと、削除したという目印だけを付けてデータは残すソフトデリートの2つがあります。
もしソフトデリートしたい場合、$table->softDeletes();
します。第1引数にはカラム名が入り、デフォルトはdeleted_at
です。
public function up() { Schema::table('notes', function (Blueprint $table) { $table->softDeletes(); }); } public function down() { Schema::table('notes', function (Blueprint $table) { $table->dropSoftDeletes(); }); }
まとめ
マイグレーション作成時はartisan
を使用すると便利です。create_xxyy_table
やadd_hoge_column_to_xxyy_table
など、artisan
実行時のファイル名はLaravelがテーブル名を検出できるようにするのが大事です。
どの作業も$table
からメソッドをつなぐと使えるので、入力補完なども駆使してテーブルを作ると良いです。
テーブルを作ったら、早速モデルを利用してデータのCRUDを見ていきましょう。
