cesargb/laravel-cascade-delete
Installation:
composer require cesargb/laravel-cascade-delete
No additional configuration is required beyond publishing the package (though none is needed by default).
First Use Case:
Apply the CascadeDelete trait to a model with polymorphic relationships:
use Cesargb\Database\Support\CascadeDelete;
class ParentModel extends Model
{
use CascadeDelete;
// Define polymorphic relationships
public function morphOneRelation()
{
return $this->morphOne(RelatedModel::class, 'parentable');
}
public function morphManyRelation()
{
return $this->morphMany(RelatedModel::class, 'parentable');
}
// Declare which polymorphic methods to cascade
protected $cascadeDeleteMorph = [
'morphOneRelation',
'morphManyRelation',
];
}
Triggering Cascade Delete:
Simply call delete() on the parent model:
$parent = ParentModel::find(1);
$parent->delete(); // Automatically deletes related polymorphic records
Trait Integration:
$cascadeDeleteMorph (string or array).Relationship Handling:
morphOne, morphMany, and morphToMany (via morphTo).Conditional Cascading:
Override the shouldCascadeDelete() method to add logic:
protected function shouldCascadeDelete($relation)
{
return $this->isActive(); // Only cascade if model is active
}
Dynamic Relationships:
For dynamically defined relationships, use the cascadeDeleteMorph array with closure-based method names:
protected $cascadeDeleteMorph = [
'morphManyRelation',
fn() => $this->dynamicMorphRelation(),
];
Soft Deletes:
Works seamlessly with Laravel’s soft deletes. Ensure related models also use SoftDeletes:
use Illuminate\Database\Eloquent\SoftDeletes;
class RelatedModel extends Model
{
use SoftDeletes;
// ...
}
Event Hooks: Extend the trait to add pre/post-cascade events:
protected static function bootCascadeDelete()
{
static::deleting(function ($model) {
Log::info("Cascading delete for {$model->id}");
});
}
Bulk Operations:
Cascade deletes work with Model::destroy() or Model::delete() on collections:
ParentModel::where('status', 'inactive')->delete();
Missing Polymorphic Key:
Ensure the related model has a parentable_id and parentable_type column. The package assumes standard Laravel polymorphic conventions.
Circular Dependencies:
Avoid cascading between models that reference each other polymorphically (e.g., A deletes B, which deletes A). This will cause infinite loops.
Non-Polymorphic Relationships:
The trait only works with polymorphic relationships. Attempting to cascade non-polymorphic relations (e.g., belongsTo, hasMany) will silently fail.
Transaction Conflicts:
If related models have database constraints (e.g., ON DELETE CASCADE), the package’s cascading may conflict. Disable native cascades or handle in a single transaction:
DB::transaction(function () use ($parent) {
$parent->delete();
});
Log Cascading: Enable query logging to verify cascades:
DB::enableQueryLog();
$parent->delete();
dd(DB::getQueryLog());
Check $cascadeDeleteMorph:
Typos or incorrect method names will prevent cascading. Validate with:
dd(get_class_methods($parent));
Soft Delete Quirks: If soft deletes aren’t working, ensure:
SoftDeletes.deleted_at column exists in the database.Custom Cascade Logic:
Override the cascadeDelete() method to add custom logic:
protected function cascadeDelete($relation)
{
if ($relation instanceof MorphMany) {
$relation->each(fn($model) => $model->forceDelete());
}
}
Exclude Specific Records: Filter records before deletion:
protected function cascadeDelete($relation)
{
$relation->where('status', '!=', 'archived')->delete();
}
Performance Optimization: For large datasets, batch deletions:
protected function cascadeDelete($relation)
{
$relation->cursor()->each->delete();
}
Custom Events: Dispatch events before/after cascading:
protected function cascadeDelete($relation)
{
event(new CascadingDelete($relation));
$relation->delete();
}
How can I help you explore Laravel packages today?