🈳
[Laravel 11] nullOnUpdate()で親レコード更新時に外部キーをnullにできるようになった
概要
Laravel 11.24.0から、マイグレーション定義でnullOnUpdate()
メソッドが使えるようになったようです!
これを使えば、親レコードのIDが変更されたときに、外部キーを自動的にnull
にする設定を簡単に記述できます。
マイグレーションの例
以下は、actors
テーブルでnullOnUpdate()
を使った外部キーの定義例です。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up()
{
Schema::create('actors', function (Blueprint $table) {
$table->unsignedBigInteger('id')->primary();
$table->foreignId('user_id')
->nullable()
->constrained()
->nullOnDelete()
->nullOnUpdate(); // 親ID変更時にnullにする設定
$table->string('name');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('actors');
}
};
これで、親IDが変更された場合、外部キーが自動的にnullにリセットされるようになります。
挙動テスト
php artisan tinker
で実際に動作を確認してみました。
// Userレコード作成
$user = App\Models\User::factory()->create(['id' => 1001]);
// Actorレコード作成(外部キーとしてuser_id=1001を設定)
$actor = App\Models\Actor::factory()->create(['user_id' => 1001]);
// 親レコードのIDを変更
$user->update(['id' => 1002]);
// Actorレコードを再取得
$actor->refresh();
App\Models\Actor {
id: 5152,
user_id: null, // nullに更新されている
name: "Ms. Leslie Shields",
created_at: "2024-12-07 08:30:10",
updated_at: "2024-12-07 08:30:10",
}
IDを変更した際に、参照している外部キーが自動的にnull
になることがわかります
ユースケース
IDは基本的に不変な識別子として使われるべきですが、そうではないプロジェクトに遭遇したことがあります。
外部システムなどで発番された社員番号をインポートするなどで、IDの値が意味を持ってしまっているケースです。
例: ID=1001〜10000を管理ユーザー、ID=9000〜9999を一時ユーザーとして扱う
このような場合、次のようなID変更が想定されます。
- 一時ユーザー(IDが9000番台)の本登録(例: IDを9001 → 1001に変更)
- 管理ユーザーへの昇格(例: IDを20001 → 1001に変更)
こういった場合、関連する子レコードの外部キーをnullにリセットしたい場合があるかなと、、
nullOnUpdate()
を活用することで、このような処理を簡単に実現できますね。
まとめ
nullOnUpdate()
は、親レコードのID変更に対応した外部キーの管理を簡潔に実現できるので、IDに意味を持ってしまっているプロジェクトで使えそうですね。[1]
-
そういうプロジェクトはバージョンが古い傾向にあるので、
->nullOnUpdate()
ではなく->onUpdate('set null')
を使う必要がありそうですね ↩︎
Discussion
Laravel の外部キー周りは、バリエーションが色々あって少しややこしいですね😂
(ある意味、進化しているという事ですが…)
追加された
->nullOnUpdate()
は、->onUpdate('set null')
の簡略記法という感じですかね。丁度、
->nullOnDelete()
が、->onDelete('set null')
の簡略版という感じで。実際、
->onUpdate('set null')
でも問題無く動作しました😊nshiroさんコメントいただきありがとうございます☺️
なるほど簡略記法が追加されただけで以前から実現は可能だったのですねー!
記事にも注釈つけておきました!