diff --git a/app/Http/Controllers/Backend/LeaderboardController.php b/app/Http/Controllers/Backend/LeaderboardController.php index 3cb2d9e2a..4aff565af 100644 --- a/app/Http/Controllers/Backend/LeaderboardController.php +++ b/app/Http/Controllers/Backend/LeaderboardController.php @@ -1,5 +1,7 @@ ttl, fn () => $this->getLeaderboard() - )->filter(function (stdClass $row) { - return Gate::allows('view', $row->user); - }); + )->filter(fn (stdClass $row) => Gate::allows('view', $row->user)); } public function getCachedFriendsLeaderboard(): ?Collection @@ -53,9 +53,7 @@ public function getCachedDistanceLeaderboard(): Collection CacheKey::LEADERBOARD_GLOBAL_DISTANCE, $this->ttl, fn () => $this->getLeaderboard(orderBy: 'distance') - )->filter(function (stdClass $row) { - return Gate::allows('view', $row->user); - }); + )->filter(fn (stdClass $row) => Gate::allows('view', $row->user)); } private function getLeaderboard( @@ -85,22 +83,16 @@ private function getLeaderboard( } $sumDistance = 'SUM(train_checkins.distance)'; - - $query = DB::table('statuses') - ->join('train_checkins', 'train_checkins.status_id', '=', 'statuses.id') - ->join('users', 'statuses.user_id', '=', 'users.id') - ->where('train_checkins.departure', '>=', $since->toIso8601String()) - ->where('train_checkins.departure', '<=', $until->toIso8601String()) - ->where(function (Builder $query) { - $query->where('users.private_profile', 0); - if (auth()->check()) { - $query->orWhereIn('users.id', auth()->user()->follows->pluck('id')) - ->orWhere('users.id', auth()->user()->id); - } - }) - ->groupBy('statuses.user_id') + $followIds = auth()->check() ? auth()->user()->follows->pluck('id') : collect(); + + $query = DB::table('train_checkins') + ->join('users', 'train_checkins.user_id', '=', 'users.id') + ->where('train_checkins.departure', '>=', $since->utc()->format('Y-m-d H:i:s')) + ->where('train_checkins.departure', '<=', $until->utc()->format('Y-m-d H:i:s')) + ->where(fn (Builder $q) => $this->applyPrivacyFilter($q, $followIds)) + ->groupBy('train_checkins.user_id') ->select([ - 'statuses.user_id', + 'train_checkins.user_id', DB::raw('SUM(train_checkins.points) AS points'), DB::raw($sumDistance . ' AS distance'), DB::raw(self::getDurationSelector() . ' AS duration'), @@ -110,16 +102,18 @@ private function getLeaderboard( ->limit($limit); if ($onlyFollowings && auth()->check()) { - $query->where(function ($query) { - $query->whereIn('statuses.user_id', auth()->user()->follows->pluck('id')) - ->orWhere('statuses.user_id', auth()->user()->id); + $query->where(function (Builder $q) use ($followIds): void { + $q->whereIn('train_checkins.user_id', $followIds) + ->orWhere('train_checkins.user_id', auth()->id()); }); } $data = $query->get(); // Fetch user models in ONE query and map it to the collection - $userCache = User::with(['blockedByUsers', 'blockedUsers'])->whereIn('id', $data->pluck('user_id'))->get(); + $userCache = User::with(['blockedByUsers', 'blockedUsers']) + ->whereIn('id', $data->pluck('user_id')) + ->get(); return $data->map(function ($row) use ($userCache) { $row->user = $userCache->where('id', $row->user_id)->first(); @@ -134,40 +128,29 @@ public static function getMonthlyLeaderboard(Carbon $date): Collection return collect(); } - $data = DB::table('statuses') - ->join('train_checkins', 'train_checkins.status_id', '=', 'statuses.id') - ->join('users', 'statuses.user_id', '=', 'users.id') - ->where( - 'train_checkins.departure', - '>=', - $date->clone()->firstOfMonth()->toIso8601String() - ) - ->where( - 'train_checkins.departure', - '<=', - $date->clone()->lastOfMonth()->endOfDay()->toIso8601String() - ) - ->where(function (Builder $query) { - $query->where('users.private_profile', 0); - if (auth()->check()) { - $query->orWhereIn('users.id', auth()->user()->follows->pluck('id')) - ->orWhere('users.id', auth()->user()->id); - } - }) + $followIds = auth()->check() ? auth()->user()->follows->pluck('id') : collect(); + + $data = DB::table('train_checkins') + ->join('users', 'train_checkins.user_id', '=', 'users.id') + ->where('train_checkins.departure', '>=', $date->clone()->firstOfMonth()->utc()->format('Y-m-d H:i:s')) + ->where('train_checkins.departure', '<=', $date->clone()->lastOfMonth()->endOfDay()->utc()->format('Y-m-d H:i:s')) + ->where(fn (Builder $q) => self::applyPrivacyFilter($q, $followIds)) ->select([ - 'statuses.user_id', + 'train_checkins.user_id', DB::raw('SUM(train_checkins.points) AS points'), DB::raw('SUM(train_checkins.distance) AS distance'), DB::raw(self::getDurationSelector() . ' AS duration'), DB::raw('SUM(train_checkins.distance) / (' . self::getDurationSelector() . ' / 60) AS speed'), ]) - ->groupBy('user_id') + ->groupBy('train_checkins.user_id') ->orderByDesc('points') ->limit(100) ->get(); // Fetch user models in ONE query and map it to the collection - $userCache = User::whereIn('id', $data->pluck('user_id'))->get(); + $userCache = User::with(['blockedByUsers', 'blockedUsers']) + ->whereIn('id', $data->pluck('user_id')) + ->get(); return $data->map(function ($row) use ($userCache) { $row->user = $userCache->where('id', $row->user_id)->first(); @@ -176,6 +159,15 @@ public static function getMonthlyLeaderboard(Carbon $date): Collection }); } + private static function applyPrivacyFilter(Builder $query, Collection $followIds): void + { + $query->where('users.private_profile', 0); + if (auth()->check()) { + $query->orWhereIn('users.id', $followIds) + ->orWhere('users.id', auth()->id()); + } + } + private static function getDurationSelector(): string { $driver = config('database.default');