Skip to content

Instantly share code, notes, and snippets.

@dhruva81
Forked from alesf/blog.md
Created December 22, 2020 16:44
Show Gist options
  • Save dhruva81/c107595e027a45bed0f69332baca61eb to your computer and use it in GitHub Desktop.
Save dhruva81/c107595e027a45bed0f69332baca61eb to your computer and use it in GitHub Desktop.
Laravel - Eloquent: Cascading delete, forceDelete and restore

If you want to delete a model with related models you can use Laravel model events. There is also a special case if your models cascade.

Lets say you have Folder and File Eloquent models that are related and use SoftDeletes trait and when you delete a folder you also want to delete files in folder and all subfolders with files.

In the boot method or Folder model you catch delete and restore events (actually deleting and restoring events that trigger before restoring or deleting happens). You can delete/restore all files in folder you're deleting/restoring with $folder->files()->delete(); and $folder->files()->withTrashed()->restore();.

Folders on the other hand cascade (folder in a folder in a folder) and because events do not trigger if you don't pull the models (->get() method), the model events won't trigger for subfolders. That's why you need to pull the folders and iterate trough them (->each() method) and delete/restore them.

You could use database CASCADE feature but that does not work with soft deletes.

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use App\Models\File;

class Folder extends Model
{
    use SoftDeletes;

    protected $dates = ['deleted_at'];

    public function files()
    {
        return $this->hasMany(File::class);
    }

    public function subfolders()
    {
        return $this->hasMany(Folder::class, 'parent_id');
    }

    public static function boot()
    {
        parent::boot();

        // cause a delete of a folder to cascade
        // to children so they are also deleted
        static::deleting(function($folder) {
            if ($folder->forceDeleting) {
                $folder->subfolders()->withTrashed()->get()
                    ->each(function($subfolder) {
                        $subfolder->forceDelete();
                    });
                $folder->files()->withTrashed()->forceDelete();
            } else {
                $folder->subfolders()->get()
                    ->each(function($subfolder) {
                        $subfolder->delete();
                    });
                $folder->files()->delete();
            }
        });

        // cause a restore of a folder to cascade
        // to children so they are also restored
        static::restoring(function($folder) {
            $folder->subfolders()->withTrashed()->get()
                ->each(function($subfolder) {
                    $subfolder->restore();
                });
            $folder->files()->withTrashed()->restore();
        });
    }
}
published: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment