If the relationship of your database does not go any further than only one layer, then you could simply use Laravel events to handle your soft-deletes within the Model boot()
method as follow:
<?php
//...
protected static boot() {
parent::boot();
static::deleting(function($invoice) {
$invoice->payments()->delete();
});
}
If, however, your structure goes deeper than only one layer, you will have to tweak that piece of code.
Let's say for example you don't want to remove the payments of an invoice but rather the whole payment history of a given user.
<?php
// ...
class Invoice extends Model
{
// ...
/**
* Holds the methods names of Eloquent Relations
* to fall on delete cascade or on restoring
*
* @var array
*/
protected static $relations_to_cascade = ['payments'];
protected static 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();
}
}
});
}
public function payments()
{
return $this->hasMany(Payment::class);
}
}
<?php
// ...
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 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();
}
}
});
}
public function invoices()
{
return $this->hasMany(Invoice::class);
}
}
This paradigm ensures Laravel to follow the rabbit hole no matter how deep it goes.