File upload in Laravel is pretty straightforward, but deleting obsolete files is often overlooked. When you update Eloquent models or change data, the files stay on the server. How to clean them up? We will discuss several ways.
There are several cases of how and when to delete them. However, this may apply not only to files but to relationships too. So, let's dig in.
Case 1: Delete file when deleting Model
Usually, when you delete a Model file, associations are deleted alongside, but files on the filesystem are left orphan. The following options are Filament-compatible.
Option 1: Deleted Listener on Model
Eloquent models dispatch several Events, allowing you to hook into the following moments in a model's lifecycle: retrieved
, creating
, created
, updating
, updated
, saving
, saved
, deleting
, deleted
, trashed
, forceDeleting
, forceDeleted
, restoring
, restored
, and replicating
.
Here, we have two hooks to consider: deleting
and deleted
. The deleting
event fires before Model is deleted. That may look sufficient at first glance, but if the database fails (and such things happen), the model will be left present without a file, and this result is not optimal.
Conversely, the deleted
event fires after Model is deleted. The same logic applies to all -ing
-ed
endings.
One way to do that is to define the booted()
function with deleted
Listener your models.
Given that we store the path to document in the Customer
Model document
property, such a function would look like this.
app/Models/Customer.php
namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Illuminate\Support\Facades\Storage; class Customer extends Model{ use HasFactory; protected $guarded = []; public static function booted(): void { self::deleted(function (self $model) { if ($model->document !== null) { Storage::disk('public')->delete($model->document); } }); }}
If document
has a value present file, we instruct to delete a file from the public
disk using the Storage
facade.
This method offers us a quick implementation, but if you have many more things going on, it may be better to implement that using Observer so as not to overcrowd the Model.
Option 2: Model Observer with deleted method
First, we need to...