livewire #10485: Fix _removeUpload for Collection properties
Contributed by hamdyelbatal122
Part of the livewire v4.4.7 release
Category: Fix · Pull request: #10485 · Fixes #10459
Livewire's file-upload feature has long supported storing multiple uploads in a Collection property, but one of its internal actions never got the memo. Pull request #10485 patches _removeUpload inside WithFileUploads so removing an upload works correctly when your property is an Illuminate\Support\Collection instead of a plain array. Previously the removal failed silently — no event, no file deletion, no error. There are no API changes in this fix, so if you're upgrading from v3.8.10, no code changes are required: behavior simply becomes correct.
What changed
The _removeUpload action in src/Features/SupportFileUploads/WithFileUploads.php previously gated its entire multi-file logic behind is_array($uploads). If your property held a Collection, the check failed and the method fell through without doing anything.
The patched version now detects the collection up front:
$isCollection = $uploads instanceof \Illuminate\Support\Collection;
$items = $isCollection ? $uploads->all() : $uploads;
It then filters the items as before — deleting the matching TemporaryUploadedFile from temporary storage and dispatching the upload:removed event to the component — and critically, writes the filtered value back in the correct type:
app('livewire')->updateProperty($this, $name, $isCollection ? collect($filtered) : $filtered);
That last detail matters: the property's original Collection type is preserved when Livewire updates it, which keeps typed properties like public Collection $photos intact. A unit test, test_can_remove_a_file_from_a_collection_of_files_property, was added to lock the behavior in — it asserts the property stays a Collection, the upload:removed event fires, and only one file remains after a refresh.
Why it matters
Livewire's upload pipeline already supported collections in its other steps: _finishUpload (which stores the uploads once validation passes) and validateIncomingUpload both handled Collection instances. _removeUpload was the odd one out.
The result was a mismatch that produced a silent failure. If you kept uploads in a collection and called removeUpload, the temp file stayed on disk until garbage collection, the upload:removed event never fired, and the UI gave no feedback. Because nothing threw an exception, the bug was easy to misdiagnose as a frontend problem or a bad temp filename. This change makes the three upload actions consistent: they all accept either arrays or collections, and removal now behaves identically for both.
Example: removing an upload from a Collection property
Here's the pattern that used to break and now works:
use Illuminate\Support\Collection;
use Livewire\Component;
use Livewire\WithFileUploads;
class PhotoUploader extends Component
{
use WithFileUploads;
public Collection $photos;
public function mount()
{
$this->photos = collect();
}
public function removePhoto(string $tmpFilename)
{
$this->removeUpload('photos', $tmpFilename);
}
}
<div>
@foreach($photos as $photo)
<div wire:key="{{ $photo->getFilename() }}">
{{ $photo->getClientOriginalName() }}
<button wire:click="removePhoto('{{ $photo->getFilename() }}')">Remove</button>
</div>
@endforeach
</div>
Calling removePhoto deletes the temp file behind that upload, dispatches upload:removed with the property name and temp filename, and — thanks to the fix — leaves $photos as a Collection containing the remaining files. You can also invoke it directly from the browser with $wire.removeUpload('photos', filename).
Upgrade impact
Coming from v3.8.10, the impact is minimal and entirely positive:
- No action required. The fix is behavioral and touches an internal, underscore-prefixed action. Your existing components don't need edits.
- Workarounds can go. If you worked around the bug — storing uploads in plain arrays instead of collections, deleting temp files manually in your own method, or converting collections back and forth — you can simplify that code now.
- Expect the event. If any component listens for
upload:removed, be aware it will now also fire for collection-based properties where it previously never did. - Typed properties are safe. The re-wrap into
collect(...)means removal no longer risks writing a bare array back into a typedCollectionproperty, which would have raised a type error if you'd attempted the removal yourself.
The fix was created against the 4.x branch (it was re-targeted as part of the discussion in #10460), so it ships with the release train that includes this pull request.
Takeaways
_removeUploadnow supportsCollectionproperties, matching the existing behavior of_finishUploadandvalidateIncomingUpload.- Before the fix, removing an upload from a collection failed silently: no file deletion, no
upload:removedevent, no error. - The property's original collection type is preserved after removal, so typed
Collectionproperties keep working. - A regression test covers the full removal flow, including the dispatched event and the post-refresh file count.
- If you're upgrading from v3.8.10, there's nothing to change — just remove any collection-related workarounds you added to dodge the bug.