Table of Contents
Ever get a bug report that sounds impossible? “I filtered by SGD, but I’m still seeing USD records in the table.”
You double-check the filter. It works. You check the data. It’s correct. But the user is right โ something’s broken.
Here’s what happened: the filter queries one field, but the UI displays a different field.
The Bug Pattern
In our case, we had a pricing dashboard with a currency filter. The filter queried pricing_configs.currency, but the table’s Currency column displayed pricing_batches.default_currency.
Different fields. Different data sources. Completely different results.
A record might have pricing_configs.currency = 'SGD' but pricing_batches.default_currency = 'USD'. When you filter by SGD, the filter includes it (because pricing_configs.currency matches). But the Currency column shows USD (because that’s what pricing_batches.default_currency contains).
From the user’s perspective: “I filtered by SGD. Why am I seeing USD?”
The Fix
Make the filter query the exact same field that’s displayed in the UI.
If your Currency column shows project.budget_currency, your filter must query project.budget_currency. Not client.default_currency. Not invoice.currency. The exact same field.
// โ BAD: Filter and display query different sources
$query->where('client.default_currency', $request->currency);
// But the table shows: $record->project->budget_currency
// โ
GOOD: Filter matches display logic
$query->whereHas('project', function ($q) use ($request) {
$q->where('budget_currency', $request->currency);
});
// And table shows: $record->project->budget_currency
Why This Happens
Usually because the table evolved over time:
- Original implementation showed
client.default_currency - Later, someone changed the display to show
project.budget_currency(better UX) - But nobody updated the filter logic to match
Or because the data model is complex โ multiple currency fields across relationships, and different parts of the code made different assumptions about which one to use.
How to Avoid It
1. Document what each column displays.
Don’t just write “Currency” in the table header. Document it:
// In your table config or component
'columns' => [
'currency' => [
'label' => 'Currency',
'source' => 'project.budget_currency', // โ This!
],
]
2. Test with mismatched data.
Create test records where client.default_currency = 'SGD' but project.budget_currency = 'USD'. Filter by SGD. What shows up in the Currency column? If you see USD, your filter doesn’t match your display.
3. Make an intentional decision.
If you have multiple currency fields, pick one as the canonical “display currency” for this view. Then use that everywhere โ filters, sorting, exports, everything.
Bonus: Complex Fields
What if the displayed field comes from a JSON column or computed value?
// Displayed value is extracted from JSON
'currency' => $record->task_config['payment']['currency']
// Filter must extract the same way
$query->whereJsonContains('task_config->payment->currency', $request->currency);
Or better yet, extract it consistently in your model:
// Model accessor
public function getDisplayCurrencyAttribute()
{
return $this->task_config['payment']['currency'] ?? 'USD';
}
// Now both filter and display use the same accessor
$query->whereRaw("JSON_EXTRACT(task_config, '$.payment.currency') = ?", [$request->currency]);
// Display: $record->display_currency
The Takeaway
When users report “the filter doesn’t work,” don’t just check if the filter query is valid. Check if it matches what’s actually displayed in the table.
Because a working filter that queries the wrong field is worse than a broken one โ it silently returns the wrong results.
Leave a Reply