How to perform an action based on Eloquent 'deleting' event when executing a mass delete statement in Laravel 5.4?
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Mastering Mass Deletes: Handling Eloquent Events During Bulk Operations in Laravel
As senior developers working with Laravel, we often deal with complex data relationships and large-scale operations. One common challenge arises when performing mass deletions using Eloquent: how do we ensure that necessary side effects—like deleting files from external storage—are triggered correctly? This is especially true when custom logic resides within model observers.
This post dives into a specific scenario involving file management, focusing on why standard Eloquent events fail during mass deletes and provides robust solutions for executing actions based on these events in Laravel 5.4 environments.
The Eloquent Mass Delete Limitation
The core problem you've encountered is fundamental to how Eloquent handles bulk operations. As noted in the documentation, when you execute a mass delete statement (e.g., Model::where(...)->delete()), Eloquent skips firing the standard model events such as deleting and deleted.
This happens because the models are never instantiated or loaded into memory during the query execution. Consequently, observers registered on those models—like your StoredImageSizeObserver’s deleting method—never get a chance to execute. You correctly identified that running $file->sizes()->delete(); $file->delete(); does not trigger the observer, leaving your file storage intact when it should be cleaned up.
Solution 1: The Iterative Approach (For Specific Cases)
If you are dealing with a small number of records or need highly custom logic per item that relies strictly on model events, iterating through the collection is the most straightforward, albeit less performant, solution. This approach forces the event firing for each record individually.
$filesToDelete = StoredFile::where('some_condition')->get();
foreach ($filesToDelete as $file) {
// Manually trigger related deletions and file cleanup
$file->sizes()->delete(); // This will fire observers if configured correctly on the relationship
$file->delete();
}
While this guarantees your observer runs, it completely negates the efficiency of a true mass delete. For large datasets, this method should be reserved for operations where item-level integrity is paramount over raw speed.
Solution 2: Refactoring the Logic into a Service Layer (The Recommended Practice)
For bulk operations, the most scalable and maintainable solution is to move the complex transactional logic out of the model layer and into a dedicated service or repository class. This aligns perfectly with the principles of building robust applications, as promoted by Laravel best practices found on laravelcompany.com.
Instead of relying solely on Eloquent observers for bulk actions, we implement the deletion process explicitly within a controlled scope:
use App\Models\StoredFile;
use Illuminate\Support\Facades\Storage;
class FileDeletionService
{
public function deleteFilesAndRecords(array $fileIds)
{
foreach ($fileIds as $fileId) {
$file = StoredFile::findOrFail($fileId);
// 1. Handle related data (if needed, this is where you call the observer logic manually if necessary)
$file->sizes()->delete();
// 2. Perform the physical file deletion explicitly
Storage::delete($file->server_url);
// 3. Delete the parent record
$file->delete();
}
}
}
By centralizing this logic, you control exactly when and how external resources (like storage) are managed, regardless of Eloquent's mass delete restrictions. This pattern ensures that your application remains predictable and testable.
Conclusion
When tackling complex operations in Laravel, especially involving external dependencies like file storage, it is crucial to understand the limitations of ORM features like mass deletes. While observers are excellent for single-record changes, they are not designed for bulk operations. For mass deletions requiring side effects, refactoring your logic into a dedicated Service Layer—as demonstrated above—provides the necessary control, performance, and maintainability. Always prioritize explicit control when dealing with external system interactions to ensure data integrity across your application.