perf: memoize PriceReliability + PriceClassification per row
Audit item #10. PriceReliability::fromUpdatedAt was being invoked ~10× per station per /api/stations response — twice in the sort comparator (once for $a, once for $b), once in the count groupBy, and once per resource render. PriceClassification::fromUpdatedAt was called twice inside the resource (value + label). The controller now computes the parsed datetime + reliability + classification once per row and stashes them on the row. Sort, groupBy, and StationResource read the cached values; the resource keeps a fresh-compute fallback for callers that bypass the controller. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Enums\PriceClassification;
|
||||
use App\Enums\PriceReliability;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Api\NearbyStationsRequest;
|
||||
@@ -59,19 +60,23 @@ class StationController extends Controller
|
||||
->where('stations.permanent_closure', false)
|
||||
->get();
|
||||
|
||||
// Compute reliability + classification once per row. The resource and
|
||||
// sort/groupBy below all read these cached attributes — without this,
|
||||
// PriceReliability::fromUpdatedAt is invoked ~10× per station per
|
||||
// response (sort comparator, count groupBy, resource value+label).
|
||||
$all->each(function ($s): void {
|
||||
$updatedAt = $s->price_effective_at ? Carbon::parse($s->price_effective_at) : null;
|
||||
$s->_updated_at = $updatedAt;
|
||||
$s->_reliability = PriceReliability::fromUpdatedAt($updatedAt);
|
||||
$s->_classification = PriceClassification::fromUpdatedAt($updatedAt);
|
||||
});
|
||||
|
||||
$filtered = $all->filter(fn ($s) => (float) $s->distance_km <= $radius);
|
||||
|
||||
$stations = $sort === 'reliable'
|
||||
? $filtered
|
||||
->sort(function ($a, $b) {
|
||||
$weightA = PriceReliability::fromUpdatedAt(
|
||||
$a->price_effective_at ? Carbon::parse($a->price_effective_at) : null
|
||||
)->weight();
|
||||
$weightB = PriceReliability::fromUpdatedAt(
|
||||
$b->price_effective_at ? Carbon::parse($b->price_effective_at) : null
|
||||
)->weight();
|
||||
|
||||
return $weightA <=> $weightB
|
||||
return $a->_reliability->weight() <=> $b->_reliability->weight()
|
||||
?: ((int) $a->price_pence <=> (int) $b->price_pence)
|
||||
?: ((float) $a->distance_km <=> (float) $b->distance_km);
|
||||
})
|
||||
@@ -85,9 +90,7 @@ class StationController extends Controller
|
||||
$prices = $stations->pluck('price_pence');
|
||||
|
||||
$reliabilityCounts = $stations
|
||||
->groupBy(fn ($s) => PriceReliability::fromUpdatedAt(
|
||||
$s->price_effective_at ? Carbon::parse($s->price_effective_at) : null
|
||||
)->value)
|
||||
->groupBy(fn ($s) => $s->_reliability->value)
|
||||
->map->count();
|
||||
|
||||
Search::create([
|
||||
|
||||
Reference in New Issue
Block a user