I recently filtered a Laravel collection that was due to be output as JSON. To my surprise, it didn't work as intended. Here's what happened, and how to resolve it!
First up, let's take a look at a basic filtered Collection.
$collection = collect([1, 2, 3, 4, 5]);
dd(json_encode($collection));
The result of this when using json_encode
is the following:
[1,2,3,4,5]
Pretty basic stuff, and this is to be expected. However, let's filter this and see what changes.
$collection = collect([1, 2, 3, 4, 5])->filter(function ($number) {
return $number !== 1;
});
dd(json_encode($collection));
Now, here's the result of the filtered version.
{"1":2,"2":3,"3":4,"4":5}
This can cause unintended side effects if you're passing a collection down to use in JavaScript. In my case, I was directly using the output in Alpine.js and yeah... stuff broke.
The reason for the difference in the structure of the keys in a filtered collection after we've removed an item — they're no longer sequential and therefore treated like properties and values.
The fix is pretty simple. Laravel's Collection class has a handy values
method to effectively just return the values of the Collection, and therefore reset the keys back to a sequential order.
So, here's how we'd reset the collection keys after sorting.
$collection = collect([1, 2, 3, 4, 5])->filter(function ($number) {
return $number !== 2;
})
->values();
dd(json_encode($collection));
And now we're back to our familiar output.
[1,3,4,5]
Next time you're doing anything to a Laravel Collection to reduce its size, reach for the values
method to reset everything back to normal.