外部キー制約を試していたら、Laravel の新しいメソッドを見つけました。
要約
-
外部キー制約の
ON DELETEにCASCADEを設定するとき- これまでは
onDelete('cascade')と書いてきた - だけど、最近は
cascadeOnDelete()とも書けるよ
- これまでは
-
外部キー制約の
ON UPDATEにCASCADEを設定するとき- これまでは
onUpdate('cascade')と書いてきた - だけど、Laravel 7.20.0 から
cascadeOnUpdate()とも書けるようになったよ(CHANGELOG)
- これまでは
もうこれ以上説明する必要は無さそうですが、それだとボリュームが寂しいので、だらだらと書いていきます。
環境
- Laravel 7.20.0
- PHP 7.4.7
- MySQL 5.7.29
前提
以下の2テーブルが存在するとします。
memos-
keywords- 外部キー制約により、
memo_idはmemos.idと紐付いています
- 外部キー制約により、
そもそもCASCADEとは
良い記事が多数あるので、ここでは MySQL の 公式マニュアル を引用しておきます。
CASCADE: 親テーブルの行を削除または更新し、子テーブル内の一致する行を自動的に削除または更新します。ON DELETE CASCADE と ON UPDATE CASCADE の両方がサポートされています。
ふむ、これだけじゃ、よくわからん
マイグレーションファイル
ON DELETE と ON UPDATE のそれぞれに CASCADE を設定します。
memos のマイグレーションファイルは、通常どおりです。
class CreateMemosTable extends Migration
{
public function up()
{
Schema::create('memos', function (Blueprint $table) {
$table->id();
$table->string('title')->nullable(false);
$table->text('body')->nullable(false);
$table->boolean('is_published')->default(false);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('memos');
}
}
keywords のマイグレーションファイルです。
onDelete('cascade') や onUpdate('cascade') と 記述 するかわりに、今回は cascadeOnDelete() と cascadeOnUpdate() を記述します。
class CreateKeywordsTable extends Migration
{
public function up()
{
Schema::create('keywords', function (Blueprint $table) {
$table->id();
$table->foreignId('memo_id')
->constrained()
->cascadeOnDelete() // ON DELETE で CASCADE
->cascadeOnUpdate(); // ON UPDATE で CASCADE
$table->string('name');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('keywords');
}
}
実際にやってみた
これだけだと味気ないので、実際にやってみました。
ON DELETE
削除前
外部キー制約により、 keywords.memo_id = 1 のレコードは、 memos.id = 1 のレコードに紐付いています。
memos
| id | title | body | is_published | created_at | updated_at |
|---|---|---|---|---|---|
| 1 | タイトル1 | 本文1 | 1 | 2020-07-18 17:42:21.0 | 2020-07-18 17:42:21.0 |
| 2 | タイトル2 | 本文2 | 1 | 2020-07-18 17:42:21.0 | 2020-07-18 17:42:21.0 |
keywords
| id | memo_id | name | created_at | updated_at |
|---|---|---|---|---|
| 1 | 1 | キーワード1 | 2020-07-18 17:42:22.0 | 2020-07-18 17:42:22.0 |
| 2 | 2 | キーワード2 | 2020-07-18 17:42:22.0 | 2020-07-18 17:42:22.0 |
削除実行
memos.id = 1 のレコードを削除します。
App\Memo::find(1)->delete();
削除後
memos.id = 1 のレコードが削除されました。
memos
| id | title | body | is_published | created_at | updated_at |
|---|---|---|---|---|---|
| 2 | タイトル2 | 本文2 | 1 | 2020-07-18 17:42:21.0 | 2020-07-18 17:42:21.0 |
また、外部キー制約により keywords.memos_id = 1 のレコードが削除されました。
keywords
| id | memo_id | name | created_at | updated_at |
|---|---|---|---|---|
| 2 | 2 | キーワード2 | 2020-07-18 17:42:22.0 | 2020-07-18 17:42:22.0 |
ON UPDATE
更新前
外部キー制約により、 keywords.memo_id = 1 のレコードは、 memos.id = 1 のレコードに紐付いています。
memos
| id | title | body | is_published | created_at | updated_at |
|---|---|---|---|---|---|
| 1 | タイトル1 | 本文1 | 1 | 2020-07-18 17:39:26.0 | 2020-07-18 17:39:26.0 |
| 2 | タイトル2 | 本文2 | 1 | 2020-07-18 17:39:26.0 | 2020-07-18 17:39:26.0 |
keywords
| id | memo_id | name | created_at | updated_at |
|---|---|---|---|---|
| 1 | 1 | キーワード1 | 2020-07-18 17:39:27.0 | 2020-07-18 17:39:27.0 |
| 2 | 2 | キーワード2 | 2020-07-18 17:39:27.0 | 2020-07-18 17:39:27.0 |
更新処理
memos.id = 1 のレコードについて、 id を 1 から 3 へ更新します。
$model = App\Memo::find(1);
$model->id = 3;
$model->save();
更新後
memos.id が 1 から 3 へ更新されました。
memos
| id | title | body | is_published | created_at | updated_at |
|---|---|---|---|---|---|
| 2 | タイトル2 | 本文2 | 1 | 2020-07-18 17:39:26.0 | 2020-07-18 17:39:26.0 |
| 3 | タイトル1 | 本文1 | 1 | 2020-07-18 17:39:26.0 | 2020-07-18 17:40:37.0 |
また、外部キー制約により keywords.memo_id も 1 から 3 へ更新されました。
keywords
| id | memo_id | name | created_at | updated_at |
|---|---|---|---|---|
| 1 | 3 | キーワード1 | 2020-07-18 17:39:27.0 | 2020-07-18 17:39:27.0 |
| 2 | 2 | キーワード2 | 2020-07-18 17:39:27.0 | 2020-07-18 17:39:27.0 |
補足
-
cascadeOnDelete()- Laravel 7.0.0 以降で使用できる
- 2019/11/09 の コミット で追加された
- Laravel 7.0.0 以降で使用できる
-
cascadeOnUpdate()- Laravel 7.20.0 以降で使用できる
- 2020/07/13 の プルリクエスト で追加された
- Laravel 7.20.0 以降で使用できる
感想
今までどおり onDelete('cascade') と記述しても問題ないと思いますが、 cascadeOnDelete() のほうが若干タイプ数も少ないですし1、IDE がサジェストしてくれるので 'cascade' のスペルを心配しなくて済みます。
そもそも取り上げようと思ったきっかけは、外部キー制約を使ってみよう2 と思って書いていたら、IDE のサジェスト機能に偶然教えられたからでした。
そこから cascadeOnDelete() が追加された経緯を調べていたら、 4日前 に出た最新バージョンで cascadeOnUpdate() が追加されていたことに気づきました。
いわゆる 偶然の産物 ってやつですね。
-
前者は19文字なのに対して、後者は15文字。4文字 少ない。 ↩
-
ちなみに、私の 案件ガチャ運 が悪いのかもしれませんが、外部キー制約を使っている案件を一度も担当したことがありません。『SQL アンチパターン』 とか読むと、「外部キー制約を使わないなんて頭おかしい」と言わんばかりに推奨されているのは知っていますけど。一度提案してみたら、「データを投入したり削除するときに考えないといけないことが増えるからイヤだ」と一蹴されましたし(それも分からなくもない)。たまに「テストコード書くのは常識となり...」みたいなことを言われる方を見かけますが、「どこの世界線の話なのかな」とか思っちゃいますね。そうやって、自分の置かれた環境が「常識」だと思うのはお止めになったほうが良いと思う...(というか、「脚注にポエムを書くな」という話ですが) ↩