By default, in our QuickAdminPanel we generate all models with soft-deleting feature, from experience with clients we know – it’s better to save that deleted data “just in case”, than feel sorry for some weird accident. But then there is a question of how to delete data in relationships?

Imagine a scenario that we have two models: Project and Transaction, where one project has many transactions. And Project has soft-deletes enabled.

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Project extends Model
{
    use SoftDeletes;

    public function projectTransactions()
    {
        return $this->hasMany(Transaction::class, 'project_id', 'id');
    }

}

What should happen if someone wants to delete the project that has active transactions?


Behavior 1. Default: Empty Parent Record

What currently happens in the code, is that child record stays in the database, but doesn’t shot parent record anymore, it becomes empty in the table, by simply using this code:

{{ $transaction->project->name ?? '' }}

But what if you want to restrict deleting the parent?
Or, on the contrary, soft-delete (“cascade”) all transactions when deleting the project?


Behavior 2. Restrict to Delete Parent Record

To restrict the deleting process, you need to add some validation in Controller’s destroy() method.

So, currently in app/Http/Controllers/Admin/ProjectsController.php we have this:

public function destroy(Project $project)
{
    abort_if(Gate::denies('project_delete'), Response::HTTP_FORBIDDEN, '403 Forbidden');
    $project->delete();

    return back();
}

So, we’re doing validation only to check the access, but nothing more than that.

Let’s add the check if there are child records, and if we find something – restrict the deleting process.

There are multiple way to achieve this, but I will suggest the most straightforward one:

public function destroy(Project $project)
{
    abort_if(Gate::denies('project_delete'), Response::HTTP_FORBIDDEN, '403 Forbidden');

    if ($project->projectTransactions()->count()) {
        return back()->withMessage('Cannot delete: this project has transactions');
    }

    $project->delete();

    return back();
}

Then, that withMessage() part should be parsed in Blade file somewhere – extract the error message from session(‘message’).

In QuickAdminPanel, by default all the messages are shown like this in parent Blade:

@if(session('message'))
    <div class="col-lg-12">
        <div class="alert alert-success" role="alert">{{ session('message') }}</div>
    </div>
@endif

Here’s the visual result:

Laravel Soft-Deletes restrict delete

You can add as many checks as you want in that Controller file, and redirect back with error message on each of them.


Behavior 3. Cascade: Soft-Delete Children Records

The opposite way of dealing with situation is to delete children records, when deleting parent. For that, there is a great Laravel package called Cascade Soft Deletes.

All we need to do is install it:

composer require iatstuti/laravel-cascade-soft-deletes

And then add a few things in out app/Project.php model – see in bold:

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Iatstuti\Database\Support\CascadeSoftDeletes;

class Project extends Model
{
    use SoftDeletes, CascadeSoftDeletes;

    protected $cascadeDeletes = ['projectTransactions'];

And, that’s it! Whenever we delete a project, Transactions are soft-deleted automatically:

Laravel soft delete cascade

That’s it, hope that was helpful!