php 61 lines · 9 steps

Building a cached daily leaderboard in Laravel

A service that ranks the day's top scorers and caches the result until midnight.

Explained by highlit
1<?php
2 
3namespace App\Services;
4 
5use App\Models\Score;
6use Carbon\CarbonInterval;
7use Illuminate\Support\Carbon;
8use Illuminate\Support\Collection;
9use Illuminate\Support\Facades\Cache;
10 
11class DailyLeaderboard
12{
13 public function __construct(private readonly int $limit = 25)
14 {
15 }
16 
17 public function top(?Carbon $date = null): Collection
18 {
19 $date = ($date ?? now())->startOfDay();
20 
21 return Cache::remember(
22 $this->cacheKey($date),
23 $this->ttlUntilMidnight($date),
24 fn () => $this->rank($date),
25 );
26 }
27 
28 public function forget(?Carbon $date = null): void
29 {
30 Cache::forget($this->cacheKey($date ?? now()));
31 }
32 
33 private function rank(Carbon $date): Collection
34 {
35 return Score::query()
36 ->select('user_id')
37 ->selectRaw('SUM(points) AS total_points')
38 ->whereBetween('created_at', [$date, $date->copy()->endOfDay()])
39 ->groupBy('user_id')
40 ->orderByDesc('total_points')
41 ->with('user:id,name,avatar_url')
42 ->limit($this->limit)
43 ->get()
44 ->values()
45 ->map(fn (Score $score, int $index) => [
46 'rank' => $index + 1,
47 'user' => $score->user,
48 'points' => (int) $score->total_points,
49 ]);
50 }
51 
52 private function cacheKey(Carbon $date): string
53 {
54 return "leaderboard:daily:{$date->toDateString()}:{$this->limit}";
55 }
56 
57 private function ttlUntilMidnight(Carbon $date): CarbonInterval
58 {
59 return now()->diffAsCarbonInterval($date->copy()->addDay());
60 }
61}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Scoping a cache TTL to a natural boundary like midnight keeps derived data fresh without manual invalidation.
  2. 2Pushing aggregation into SQL with grouping and ordering avoids loading and summing rows in PHP.
  3. 3Encoding every query variable into the cache key prevents different configurations from colliding.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a cached daily leaderboard in Laravel — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code