こんにちは。エンジニアの砂町です。
今回はLaravelでソフトデリートした際に、子や孫のリレーション先のデータも一緒に削除する方法についてお話ししようと思います。
課題
例えば、userテーブル(親)とpostテーブル(子)が1:nの関係であった場合、userのデータをソフトデリートしたらuserに紐づくpostのデータも一緒にソフトデリートしたいとします。
物理削除の場合は、外部キーにCASCADEを設定していれば、userのデータをソフトデリートした際に自動的にリレーション先のpostのデータもソフトデリートしてくれます。
しかし、論理削除の時は外部キーにCASCADEを設定していても、自動的にソフトデリートしてくれません。
リレーション先を自動的にソフトデリートする方法
今回はModel側にロジックをいれることで、対象のデータがソフトデリートされた時に関連するデータも自動的にソフトデリートするという動きを実装します。
ちなみに今回は触れませんが、laravel-soft-cascadeというライブラリを使えば簡単にできるという記事もあったのでよければ試してみてください。
では実際にModelにロジックを追加していきます。
まずuserモデルにbootを定義し、そこで下記のようなdeletingメソッドを記述します。
protected static function boot() { parent::boot(); static::deleting(function ($user) { $user->post()->delete(); }); }
これでuserソフトデリート時に「$user->post()->delete()」が呼ばれ、関連するpostのデータも自動的に削除する動きとなります。
あとは、リレーション先を定義して、userをソフトデリートする関数を記述します。
//ソフトデリート実行 use SoftDeletes; public static function deleteData($user_id){ $result = DB::transaction(function () use ($user_id) { $user = User::where('user_id', $user_id->first(); $user->delete(); return true; }); return $result; } //リレーション public function post(){ return $this->hasMany(Post::class, 'post_id'); }
userソフトデリート時は、いきなりdeleteメソッドを呼ぶのではなく、一度取得したデータに対してdeleteメソッドを呼び出します。
このようにしないとモデルイベントを発行しないため、bootで記述した関連するpostデータのソフトデリートが行えません。
また、複数データの削除になるため、トランザクジョン処理を挟んでいます。
まとめ
今回お話ししたやりかたではテーブルが多いとそれだけ記述する量が増えるので、もっと簡単に実装できる方法があればいいのになと思いました。
見つけたら、またブログで記事にしたいなと思います。