🗂

レコードが削除されたときに子要素も一緒に削除する(laravel)

2021/05/12に公開

レコードが削除されたときに、リレーション先の子要素は削除されずに存在していて、データに不整合性が生まれることがよくあります、

今回はレコードが削除されたときにリレーション先の子要素も一緒に削除する方法について2種類解説します。

パターン1: モデル内に削除されたときのロジックを書く

laravelのmodelでは、bootメソッドの中にCRUD発動時のロジックを書くことができます。

例えば、記事(article)が削除されたときにそれに紐づくコメントを削除したいとした場合は、このように書きます。

class Article extends Model {
.
.
.
    public static function boot()
    {
        parent::boot();

        static::deleting(function ($article) {
            $article->comments()->delete();
        });
    }
.
.
.
}

bootメソッドの中でdeletingを記述すると、削除されたときのロジックを書くことができます。
同様にupdatingやcreatingとかも設定できます。

ちなみにObserverを使用しても同様のことができるので、Model内の記述を出来る限り減らしたい方はObserver使うのがいいかもしれません。

Observerを使用してモデルのイベントをキャッチしよう(Laravel)

パターン2: マイグレーションファイルに記述してDBの外部キー制約設定をする

DBで外部キー制約の設定をすると、リレーション先の要素も同時に削除することができます。
どちらかというとLaravelの機能というよりはDBの話です。

先程の、記事(article)を削除する際にコメント(comment)も同時に削除したい場合、articleのマイグレーションファイルはこうなります。

特に普通のマイグレーションファイルです。

<?php

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

class CreateArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->id();
            $table->string("title");
            $table->text('url');
            $table->text('thumbnail_url');
            $table->text('posted_at');
        });
    }

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

そして、コメントの方のマイグレーションファイルはこうなります。

<?php

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

class CreateCommentsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->bigInteger('article_id')->unsigned()->nullable(); // bigIntegerとunsignedをつけないとエラーになる
            $table->text('body');
            $table->timestamps();

            $table->foreign('article_id') // 外部キー設定をして、「cascadeOnDelete」をつける
                ->references('id')
                ->on('articles')
                ->cascadeOnDelete();
        });
    }

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

コメントテーブルのマイグレーションの方で、外部キー設定をして「cascadeOnDelete」をつける必要があります。
※cascadeOnDeleteの部分はLaravelのバージョンによって書き方が異なることがあるので注意してください。

終わりに

「レコード削除時にリレーション先の子要素も削除する」に関して2通りの方法を説明しました。
整合性がとれずに、DBが良くわからない状態になってしまうと結構困っちゃうので、必要であれば何か対策をしてみてください。

Discussion