概要
Laravelでは、マイグレーションファイルに外部キー制約の設定を記述しておくと、親テーブルのデータを削除したときに子テーブルのデータも同時に削除することができます。
但しこれは、DBから実際にデータを削除する「物理削除」の場合の話です。
DBからレコードを削除するわけではなく削除フラグを立てて削除したとみなす「論理削除」の場合は、マイグレーションファイルへの記述だけではリレーション先のデータを一緒に削除してくれません。
このような場合に、 モデルの boot
メソッド(もしくは booted
メソッド)を使って初期設定を行えば、あるテーブルのデータを論理削除したときにリレーション先のテーブルのデータも削除することができます。
業務でこの方法を使う機会があったので、 モデル boot
メソッドについて勉強したこととあわせて、備忘録を書いておきたいと思います。
論理削除(softDelete)の設定方法
まず事前準備としてソフトデリートの設定をします。
ソフトデリート機能を組み込む手順は以下の2つです。
1. マイグレーションファイルにソフトデリートの設定を記述する
2. EloquentモデルにSoftDeletesトレイトを組み込む
例として users
テーブルにソフトデリート機能を設定してみます。
1. マイグレーションファイルにソフトデリートの設定を記述する
Laravelの論理削除ではテーブルの deleted_at
カラムに値が入っている場合はデータは削除されたとみなすので、 deleted_at
カラムを追加する必要があります。
deleted_at
カラムを追加するため、マイグレーションファイルに以下の1行を追記します。
publicfunction up()
{
Schema::table('user',function (Blueprint $table) {
$table->softDeletes(); // 追記
});
}
これでマイグレーションを実行すると、DBのテーブルに deleted_at
カラムが作成されていることが確認できます。
2. EloquentモデルにSoftDeletesトレイトを組み込む
User
モデルファイルに以下のように追記します。
use Illuminate\Database\Eloquent\SoftDeletes; // 追記
class User extends Model
{
use SoftDeletes; // 追記
}
この記述により、deleteメソッドでモデルを削除した際はソフトデリートが実行されるようになります。
Laravelでソフトデリートを行うための設定は以上です。
モデルのbootedメソッドとは
本題の「ソフトデリートするときにリレーション先のデータも削除する」ための実装にあたり、先に今回使用する boooted
メソッドについて少し勉強しておきます。
Laravel
のモデルでは、初期起動時に boot
メソッドを走らせて初期設定をしています。
booted
メソッドはモデルの初期起動後に実行されるメソッドで、モデルに対して行いたい初期設定は booted
メソッドに書くよう readouble
にも説明があります。
【補足】bootedメソッドとbootメソッドについて
今回実装方法をググっているとboot
メソッドを使っている記事が見つかり、自分としても初期起動時のメソッドといえばboot
メソッドのイメージがあったので、booted
メソッドとboot
メソッドについて少し調べてみました。
readouble
の説明では、モデルの初期設定を行う方法としてLaravel6までは boot
メソッドが使われていますが、Laravel7以降では booted
メソッドが使われていました。
・Laravel8の該当箇所
・Laravel6の該当箇所
試しにモデルファイルに以下のように書き、 booted()
のところにマウスをホバーさせてVSCodeのヒントを表示してみます。
// モデルファイルに記述
/**
* @return void
*/
public static function booted(): void
{
//
}
// booted() にマウスをホバーさせると、以下の説明が表示される
Illuminate\Database\Eloquent\Model::booted
Perform any actions required after the model boots.
<?php
protected static function booted() { }
booted
メソッドについては、 Perform any actions required after the model boots.
と説明されています。
boot()
を書いた場合も確認します。
// モデルファイルに記述
/**
* @return void
*/
public static function boot(): void
{
//
}
// boot() にマウスをホバーさせると、以下の説明が出る
Illuminate\Database\Eloquent\Model::boot
Bootstrap the model and its traits.
<?php
protected static function boot() { }
@return void
boot
メソッドは Bootstrap the model and its traits.
と説明がありました。
※実際のLaravelのソースコードだと、以下のファイルに boot
メソッドも booted
メソッドもありました。
興味のある方はこちらもご覧ください。
メソッド名の通りですが、 booted
メソッドはモデルの初期起動後に実行されているということですね。
私は業務ではLaravel8を使っており、 boot
メソッドでも booted
メソッドでも同じように設定を行うことは出来ましたが、現在の readouble
の説明に従って booted
メソッドを使っていきたいと思います。
Modelのbootedメソッドにクロージャを書く
前置きが長くなりましたが、実際にモデルの booted
メソッドに「データを論理削除したらリレーション先のデータも削除する」設定を記述します。
前提
-
users
テーブル(親)と1対多で紐づくposts
テーブルがあるとします。 -
users
テーブルからレコードを論理削除したら、 リレーション先のposts
テーブルからもレコードを論理削除するように設定します。
実装方法
User.php
にリレーションと booted
メソッドを追記します。
class User extends Model
{
use HasFactory, SoftDeletes;
/**
* @return HasMany
*/
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
/**
* @return void
*/
public static function booted(): void
{
static::deleted(function ($user) {
$user->posts()->delete();
});
}
}
これにより、 users
テーブルのデータ削除時に、リレーション先の posts
テーブルからも同時に削除してくれます。
※ posts
テーブルに対してもソフトデリートの設定を行っている場合は posts
テーブルも論理削除、何も設定していなければ posts
テーブルからは物理削除になります。
最後に
論理削除はDBのレコードを削除するわけでは無く、SQLの命令文でいうと UPDATE
にあたるので、論理削除の場合はDB的には削除と見なされないということは、よく考えると当たり前でもありますが改めて勉強になりました。
またモデルの boot
メソッドや booted
メソッドの使い方や、これらが出来ることについても良い学びになりました。
Laravelのライフサイクルの理解を深めれば、もっともっと実装の引き出しが増えるんだろうなと思うので、コツコツ勉強していきたいと思います。
参考記事