I am using Laravel Breeze to handle profile updates, and I have added functionality to allow users to update their avatar. Everything works fine, but the issue is that when a user uploads a new avatar, the old one isn't deleted from the filesystem.
I store avatars in storage/app/public/avatars/ and use php artisan storage:link to make them accessible. Here is my update method:
public function update(ProfileUpdateRequest $request): RedirectResponse
{
$request->user()->fill($request->validated());
if ($request->user()->isDirty('email')) {
$request->user()->email_verified_at = null;
}
if ($request->hasFile('avatar')) {
// Attempt to delete the old avatar
Storage::disk('public')->delete('avatars/' . $request->user()->avatar);
$file = $request->file('avatar');
$fileName = Str::uuid() . '.' . $file->getClientOriginalExtension();
$file->storeAs('avatars', $fileName, 'public');
$request->user()->avatar = $fileName;
}
$request->user()->save();
return Redirect::route('profile.edit');
}
However, after uploading a new avatar, the old avatar remains in the storage/app/public/avatars/ directory instead of being deleted.
What I’ve tried:
Checked that
Storage::disk('public')->delete('avatars/' . $request->user()->avatar);
runs before the new file is stored. Verified that the file exists in storage/app/public/avatars/ before deleting. Debugged withdd(Storage::disk('public')->exists('avatars/' . $request->user()->avatar));
before the delete call. Confirmed that$request->hasFile('avatar') returns true
, so the condition is being met. Removed everything from the function and tested only deletion:
dd(Storage::disk('public')->delete('avatars/' . $request->user()->avatar));
In this case, the avatar was successfully deleted, which makes this issue even stranger.
Questions: Why isn't the old avatar getting deleted in the full function? Could there be a reason why Storage::delete() works in isolation but not when used before storing a new file? How can I ensure that the old avatar is properly removed before saving the new one? Any insights would be greatly appreciated!
I suspect the issue is when you call $request->user()->fill($request->validated());
before handle the avatar file, it will overwrite the user's existing avatar
attribute.
Try this
public function update(ProfileUpdateRequest $request): RedirectResponse
{
# Capture the current avatar filename
$oldAvatar = $request->user()->avatar;
# Update user attributes except the avatar file
$request->user()->fill($request->validated());
# ...
if ($request->hasFile('avatar')) {
# Delete the old avatar using the stored filename
Storage::disk('public')->delete('avatars/' . $oldAvatar);
# ...
}
$request->user()->save();
return Redirect::route('profile.edit');
}