Skip to content

Instantly share code, notes, and snippets.

@Escarter
Forked from alesf/blog.md
Created June 17, 2019 11:34
Show Gist options
  • Save Escarter/bcc05ed965359fa101e9998f69456d0a to your computer and use it in GitHub Desktop.
Save Escarter/bcc05ed965359fa101e9998f69456d0a 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