Skip to content

Instantly share code, notes, and snippets.

@alesf
Last active September 16, 2023 07:28
Show Gist options
  • Save alesf/89b3b5fdd2eb73afe227 to your computer and use it in GitHub Desktop.
Save alesf/89b3b5fdd2eb73afe227 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
@nhitze
Copy link

nhitze commented Jan 18, 2019

Thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment