39
35

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 5 years have passed since last update.

Laravelの論理削除と愉快なトラップたち

Last updated at Posted at 2017-07-09

Laravelは簡単に論理削除を実装できるのですが、意外と落とし穴が多くハマったところがあったので、列挙していきます。
よりスマートな回避方法があれば、突っ込んでください。

論理削除したのにuniqueのバリデーションが通らない

論理削除の場合、以下のようなバリデーションは削除済を含めて一意であることをバリデーションします。

    public function rules()
    {
        return [
            'name' => 'required|unique:countries'
        ];
    }

対策

1. uniqueのルールをカスタマイズする

    public function rules()
    {
        return [
            'name' => [
                'required',
                Rule::unique('countries')->whereNull('deleted_at')
            ]
        ];
    }

2. カスタムバリデーションでEloquent ORMを使う

Laravel5でカスタムバリデーションを参考にするとできる。今はめんどくさいけど、Laravel 5.5. Custom Validator Rulesによると、5.5で楽になりそう

また、existsについても同じように、「削除したのにバリデーションが通ってしまう」現象が起きるので、同様の処置が必要です。

->delete()メソッドを使うと孫が消えてくれない

論理削除のときにDelete on Cascade的なことをやりたいときの話
Country < User < Postのリレーションを例に使います。

まず、Userのdeletingイベントはこんな感じで書けると思います。

    protected static function boot()
    {
        parent::boot();
        self::deleting(function ($user) {
           $user->posts()->delete();
        });
    }

Countryのdeletingイベントをこう書くとCountry削除時にPostは消えません。あら悲しい。

    protected static function boot()
    {
        parent::boot();
        self::deleting(function ($country) {
           $country->users()->delete();
        });
    }

対策

1. さくっとぐるぐる削除

一番てっとり早いものの、できれば採用したくないやり方。
Userをひとつひとつ削除するので、Userモデルに書いたdeletingが発火してくれる。

    protected static function boot()
    {
        parent::boot();
        self::deleting(function ($country) {
            $users = $country->users;
            
            foreach($users as $user) {
                $user->delete();
            }
            
        });
    
    }

2. 削除するモデルを列挙

数が少ないならいいけど、リレーションしまっくてると書き出すのがめんどくさい

    protected static function boot()
    {
        parent::boot();
        self::deleting(function ($country) {
            $user_ids = $country->users->pluck('id')->toArray();
            
            Post::whereIn('user_id', $user_ids)->delete();
            User::destroy($user_ids);
        });
    }

検証用につくったリポジトリ

39
35
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
39
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?