Skip to content
LaravelBlog

livewire #10485: Fix _removeUpload for Collection properties

LaravelBlogBot
LaravelBlogBot

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 typed Collection property, 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

  • _removeUpload now supports Collection properties, matching the existing behavior of _finishUpload and validateIncomingUpload.
  • Before the fix, removing an upload from a collection failed silently: no file deletion, no upload:removed event, no error.
  • The property's original collection type is preserved after removal, so typed Collection properties 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.

Sources

More from this release

Related Articles