Laravel: Softdelete Eloquent Relationships onDelete Cascade
Tags: laravel, webdev, tutorial
Published: 2020-07-10 01:07:15

Eloquent is one of many Laravel features that you just can't deny how cool and useful they are. However, sometimes even the very best things have their flaws. And one of Eloquent's is not being able to autodelete related objects through `SoftDelete` when a model has a relationship to another with `onDelete('cascade')`.

For this article, I am supposing that you are using `Laravel 5`. So let's say you have your default `users` table related to the `User` model and you have another table called `invoices` which is related to the `Invoice` model. `User` and `Invoice` are linked through an Eloquent relationship as followed:

```
// app/User.php
<?php

namespace App;

class User extends Authenticatable {

    // ...

    public function invoices() {
        return $this->hasMany(Invoice::class);
    }
}

```

I think the code above does not need any explanations. It's a simple Eloquent relationship linking `User` and `Invoice`.

```
// app/User.php

<?php

namespace App;

class User extends Model {

    // ...

    /**
     * Holds the methods' names of Eloquent Relations 
     * to fall on delete cascade or on restoring
     * 
     * @var array
     */
    protected static $relations_to_cascade = ['invoices']; 

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

        static::deleting(function($resource) {
            foreach (static::$relations_to_cascade as $relation) {
                foreach ($resource->{$relation}()->get() as $item) {
                    $item->delete();
                }
            }
        });

        static::restoring(function($resource) {
            foreach (static::$relations_to_cascade as $relation) {
                foreach ($resource->{$relation}()->get() as $item) {
                    $item->withTrashed()->restore();
                }
            }
        });
    }
}
```
Let's now break down this code:

`protected static $relations_to_cascade = ['invoices'];` in this array we store the methods' names or relationships we wish to delete (or restore) when a `SoftDelete` occurs. If we have more than one relationship, we should push them to the array. For instance:

`protected static $relations_to_cascade = ['invoices', 'roles'];` _Note that we must have a `roles()` relationship declared in our `Invoice` model as well in this case or our app won't recognize the `roles` method later on the code._

Now that we have defined the Eloquent relationships we want deleted/restored we move on to actually deleting them and/or restoring them. 

```
        static::deleting(function($resource) {
            foreach (static::$relations_to_cascade as $relation) {
                foreach ($resource->{$relation}()->get() as $item) {
                    $item->delete();
                }
            }
        });
```

This piece of code calls the `deleting` method. Inside this method, We loop through our `$relations_to_cascade` array. We then get all the concerned items from our database and delete them.

Now once you understand this process, the next block will make sense

```

        static::restoring(function($resource) {
            foreach (static::$relations_to_cascade as $relation) {
                foreach ($resource->{$relation}()->get() as $item) {
                    $item->withTrashed()->restore();
                }
            }
        });

```

Yes exactly, it does the same thing but this time retrieves the concerned item `withTrashed()` (because it was deleted and must include trashed items, duh!) and finally restore the item.

And _voilà, c'est fait!_

You can repeat the same logic for other models and other relationships. 

If you are starting a new Laravel project, it would be a good idea to keep this in mind and apply this paradigm with every model you create in order not to have to get back and change every model of your code — things can get messy, and trust me they will!