Laravelのモデルイベントについて理解が少なく、イベントが発火しない条件があることを知ったのでまとめておきます。
事象
LaravelのmodelのDeletingイベントを使用してリレーション先を削除しようとした際に、イベントが発火されていなかった。
原因
結論としては、以下のことが原因だった
(laravel readableより引用) url: https://readouble.com/laravel/8.x/ja/eloquent.html
Note: Eloquentを介して一括更新を発行する場合、更新されたモデルに対して、saving、saved、updating、updatedモデルイベントは発生しません。これは一括更新を実行する場合に、モデルが実際には取得されないからです。
実際の例
Userとそれに紐づくPost、またPostに紐づくコメントがあるとする。
特定のUserが消えた際に全てのPost,それに紐づくコメントを消すのをmodelイベントを駆使して実行します。
controller
// ユーザーを削除
public function delete(int $id): void
{
User::find($id)->delete();
}
UserModel
削除が実行された際に、紐づいているPostも削除する。
*これはモデルイベントが発火するのでうまく行きます。
// イベント発火時の挙動を定義
protected static function boot()
{
parent::boot();
// ユーザー削除時に紐づいている投稿も削除する
static::deleting(function ($user) {
$user->posts()->delete();
});
}
// postとのリレーション
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
PostModel
Postが削除された際に、紐づいているCommentも削除する。
*Userモデルで一括削除されているので、こちらのモデルイベントは発火しません。
// イベント発火時の挙動を定義
protected static function boot()
{
parent::boot();
// 投稿削除時に紐づいているコメントも削除する
static::deleting(function ($post) {
$post->comments()->delete();
});
}
// commentとのリレーション
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
解決法
- UserモデルでのPost削除を一括ではなく、ループで実行する
紐づく投稿を個別で削除してあげることにより、全てのPostに関するモデルイベントが発火するようになる。
変更後
// イベント発火時の挙動を定義
protected static function boot()
{
parent::boot();
// ユーザー削除時に紐づいている投稿も削除する
static::deleting(function ($user) {
// Postのdeletingイベントを発火させるためにループで実行
$user->posts->each(function ($post){
$user->delete();
});
});
}
- cascadeOnDeleteを使用してMigrationファイルで操作する。
ここでは書きませんが、cascadeOnDelete等で指定したら紐づいているデータも親データ削除時に自動的に削除が実行されます。
ただ、softDeleteとは互換性がないので、論理削除を利用している場合には向きません。