Today, our team encountered a bug reported in the group chat. After cloning the project locally, I spent some time reproducing the issue.
Code Overview
- The following code causes the first item removal to convert a
Collection
into an object (JSON data):
protected function transformJson($items)
{
return Collection::make($items)
- ->values()
->where('_remove_', '0')
+ ->values()
->map(fn($item) => Arr::except($item, '_remove_'))
->toJson();
- In the API data retrieval logic, this function call pattern caused issues:
public function index()
{
// $json={"1":{"id": 2}}
$data = Collection::make($json);
// Filtering converts indexes to 0-based
$index = $this->getRandomAd($data, [1]);
// Returns null due to original key mismatch
return $data->get($index);
}
protected function getRandomAd(Collection $data, $notIn=[])
{
$index = -1;
if (count($notIn) > 0) {
$data = $data->filter(fn($item) => !in_array($item['id'], $notIn))->values();
}
// Now working with 0-indexed array
foreach ($data as $index => $item) {
if ($item['id']) {
return $index;
}
}
// Returns filtered index incompatible with original collection
return $index;
}
Root Causes
• Late-added filtering logic in getRandomAd
unexpectedly converted the Collection to a 0-indexed array
• Non-destructive operations (filter
+ values
) created a new collection instead of modifying the original
• Index mismatch occurred between the filtered 0-based indexes and original collection keys
• Reference semantics were broken by reassignment rather than using mutable operations like transform
Key Insight: Using transform
instead of map
would have avoided this bug by modifying the original collection directly.