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:
Ovidiu U
2026-04-29 20:20:59 +01:00
parent 4ce5066596
commit 8e29980dfe
2 changed files with 24 additions and 18 deletions

View File

@@ -12,8 +12,13 @@ class StationResource extends JsonResource
{
public function toArray(Request $request): array
{
$updatedAt = $this->price_effective_at ? Carbon::parse($this->price_effective_at) : null;
$reliability = PriceReliability::fromUpdatedAt($updatedAt);
// The controller pre-computes _updated_at / _reliability / _classification
// per row. Falling back to fresh computation keeps the resource usable
// outside that path (e.g. tests or future callers).
$updatedAt = $this->_updated_at
?? ($this->price_effective_at ? Carbon::parse($this->price_effective_at) : null);
$reliability = $this->_reliability ?? PriceReliability::fromUpdatedAt($updatedAt);
$classification = $this->_classification ?? PriceClassification::fromUpdatedAt($updatedAt);
return [
'station_id' => $this->node_id,
@@ -32,11 +37,9 @@ class StationResource extends JsonResource
'open_today' => $this->openTodayPayload(),
'price_pence' => (int) $this->price_pence,
'price' => round((int) $this->price_pence / 100, 2),
'price_updated_at' => $this->price_effective_at
? Carbon::parse($this->price_effective_at)->toISOString()
: null,
'price_classification' => PriceClassification::fromUpdatedAt($updatedAt)->value,
'price_classification_label' => PriceClassification::fromUpdatedAt($updatedAt)->label(),
'price_updated_at' => $updatedAt?->toISOString(),
'price_classification' => $classification->value,
'price_classification_label' => $classification->label(),
'reliability' => $reliability->value,
'reliability_label' => $reliability->label(),
];