57
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Laravelの外部キー制約でCASCADEを設定するときに使える新しいメソッド

Last updated at Posted at 2020-07-18

外部キー制約を試していたら、Laravel の新しいメソッドを見つけました。

要約

  • 外部キー制約の ON DELETECASCADE を設定するとき

    • これまでは onDelete('cascade') と書いてきた
    • だけど、最近は cascadeOnDelete() とも書けるよ
  • 外部キー制約の ON UPDATECASCADE を設定するとき

    • これまでは onUpdate('cascade') と書いてきた
    • だけど、Laravel 7.20.0 から cascadeOnUpdate() とも書けるようになったよ(CHANGELOG

もうこれ以上説明する必要は無さそうですが、それだとボリュームが寂しいので、だらだらと書いていきます。

環境

  • Laravel 7.20.0
  • PHP 7.4.7
  • MySQL 5.7.29

前提

以下の2テーブルが存在するとします。

  • memos
  • keywords
    • 外部キー制約により、 memo_idmemos.id と紐付いています
ER図.png

そもそもCASCADEとは

良い記事が多数あるので、ここでは MySQL の 公式マニュアル を引用しておきます。

CASCADE: 親テーブルの行を削除または更新し、子テーブル内の一致する行を自動的に削除または更新します。ON DELETE CASCADE と ON UPDATE CASCADE の両方がサポートされています。

ふむ、これだけじゃ、よくわからん

マイグレーションファイル

ON DELETEON 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 のレコードについて、 id1 から 3 へ更新します。

$model = App\Memo::find(1);
$model->id = 3;
$model->save();

更新後

memos.id1 から 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_id1 から 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 以降で使用できる
  • cascadeOnUpdate()

感想

今までどおり onDelete('cascade') と記述しても問題ないと思いますが、 cascadeOnDelete() のほうが若干タイプ数も少ないですし1、IDE がサジェストしてくれるので 'cascade' のスペルを心配しなくて済みます。

そもそも取り上げようと思ったきっかけは、外部キー制約を使ってみよう2 と思って書いていたら、IDE のサジェスト機能に偶然教えられたからでした。

そこから cascadeOnDelete() が追加された経緯を調べていたら、 4日前 に出た最新バージョンで cascadeOnUpdate() が追加されていたことに気づきました。

いわゆる 偶然の産物 ってやつですね。

  1. 前者は19文字なのに対して、後者は15文字。4文字 少ない。

  2. ちなみに、私の 案件ガチャ運 が悪いのかもしれませんが、外部キー制約を使っている案件を一度も担当したことがありません。『SQL アンチパターン』 とか読むと、「外部キー制約を使わないなんて頭おかしい」と言わんばかりに推奨されているのは知っていますけど。一度提案してみたら、「データを投入したり削除するときに考えないといけないことが増えるからイヤだ」と一蹴されましたし(それも分からなくもない)。たまに「テストコード書くのは常識となり...」みたいなことを言われる方を見かけますが、「どこの世界線の話なのかな」とか思っちゃいますね。そうやって、自分の置かれた環境が「常識」だと思うのはお止めになったほうが良いと思う...(というか、「脚注にポエムを書くな」という話ですが)

57
42
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
57
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?