Laravel Eloquent ORM - removing rows and all the child relationship, with event deleting
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Laravel Eloquent ORM: Removing Nested Relationships with Event Deleting
As senior developers working with relational data in Laravel, one of the most common challenges we face is managing cascading deletions across deeply nested Eloquent relationships. When you delete a parent record, you often need to ensure that all related child records—and their subsequent children—are also meticulously removed. Manually chaining these deletions can become complex and error-prone.
This post dives deep into how to correctly handle the removal of multi-level relationships (like Country $\rightarrow$ Region $\rightarrow$ City) using Eloquent events, providing a robust solution that avoids relying solely on database-level cascading constraints.
The Challenge: Nested Deletions in Eloquent
Let's examine the structure we are working with:
- Country has many Regions.
- Region has many Cities.
When we delete a Country, we need to ensure that all associated Regions and subsequently all their related Cities are also deleted. The naive approach often involves trying to call relationship methods directly within the deleting event, as seen in the initial attempts: $country->region()->delete(). As you discovered, this method often fails or only handles the immediate parent gracefully, leaving orphaned child records behind.
The core issue is that Eloquent’s built-in relationship deletion mechanisms are designed for simple one-to-many scenarios and don't automatically traverse complex, multi-level structures efficiently during a parent deletion event. We need to explicitly query and delete the related models.
The Solution: Utilizing the deleting Event with Query Builder
The most reliable way to solve this is by leveraging the Eloquent model events, specifically the deleting hook, which fires just before a model is actually deleted from the database. Inside this hook, we can utilize the query builder to fetch all related models and delete them in bulk. This approach is highly efficient because it minimizes the number of database queries compared to iterating over results one by one.
We will implement this logic on the parent model (Country) to manage its descendants.
Implementing Cascading Deletion Logic
Here is how we can refactor the boot method in our models to achieve true cascading deletion:
// App\Models\Country.php
use Illuminate\Database\Eloquent\Model;
class Country extends Model
{
protected $fillable = ['name', 'sort'];
public $timestamps = false;
public function region()
{
return $this->hasMany('App\Models\Region');
}
}
// ... (Similar logic for Region and City models)
For the Country model, we implement the cascading logic:
// App\Models\Country.php
public static function boot()
{
parent::boot();
static::deleting(function ($country) {
// 1. Delete all related Regions
$country->region()->delete();
// 2. Delete all related Cities (which are children of the regions we just deleted)
// We must re-fetch the relationship or ensure the deletion logic handles nested calls correctly.
// A more robust approach for deeply nested structures is to iterate over the immediate children:
$regions = $country->region()->get();
foreach ($regions as $region) {
$region->city()->delete(); // Delete all cities belonging to this region
$region->delete(); // Then delete the region itself
}
// Finally, delete the country itself
$country->delete();
});
}
Note on Optimization: While iterating and deleting works for demonstration, in high-volume scenarios, it is often more efficient to use raw queries or specialized database operations if performance becomes critical. However, for maintaining application logic within Eloquent events, this explicit iteration provides the necessary control over the deletion sequence. This level of control is crucial when you need to override default constraints, ensuring that data integrity is maintained precisely as intended by your application logic.
Conclusion: Control Over Data Integrity
Managing nested relationships without relying on database-level cascading deletes requires us to step in and manage the deletion process explicitly within the Eloquent lifecycle hooks. By using the deleting event and combining relationship querying with bulk deletion, we gain precise control over how data is removed at every level of the hierarchy. This practice ensures that complex relational data remains consistent and clean, which is a fundamental principle when building scalable applications on frameworks like Laravel. For more advanced techniques on managing Eloquent interactions, always refer to the comprehensive documentation available at https://laravelcompany.com.