1 | <?php |
||||
2 | |||||
3 | namespace App\Services; |
||||
4 | |||||
5 | use App\Models\User; |
||||
6 | use App\Models\UserDownload; |
||||
7 | use App\Models\UserRequest; |
||||
8 | use Carbon\Carbon; |
||||
9 | use Illuminate\Support\Facades\DB; |
||||
10 | |||||
11 | class UserStatsService |
||||
12 | { |
||||
13 | /** |
||||
14 | * Get user statistics by role |
||||
15 | */ |
||||
16 | public function getUsersByRole(): array |
||||
17 | { |
||||
18 | $usersByRole = User::query() |
||||
19 | ->join('roles', 'users.roles_id', '=', 'roles.id') |
||||
20 | ->select('roles.name as role_name', DB::raw('COUNT(users.id) as count')) |
||||
21 | ->whereNull('users.deleted_at') |
||||
22 | ->groupBy('roles.id', 'roles.name') |
||||
23 | ->get(); |
||||
24 | |||||
25 | return $usersByRole->map(function ($item) { |
||||
26 | return [ |
||||
27 | 'role' => $item->role_name, |
||||
28 | 'count' => $item->count, |
||||
29 | ]; |
||||
30 | })->toArray(); |
||||
31 | } |
||||
32 | |||||
33 | /** |
||||
34 | * Get downloads per day for the last 7 days |
||||
35 | */ |
||||
36 | public function getDownloadsPerDay(int $days = 7): array |
||||
37 | { |
||||
38 | $startDate = Carbon::now()->subDays($days - 1)->startOfDay(); |
||||
39 | |||||
40 | $downloads = UserDownload::query() |
||||
41 | ->select(DB::raw('DATE(timestamp) as date'), DB::raw('COUNT(*) as count')) |
||||
42 | ->where('timestamp', '>=', $startDate) |
||||
43 | ->groupBy(DB::raw('DATE(timestamp)')) |
||||
44 | ->orderBy('date', 'asc') |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
45 | ->get(); |
||||
46 | |||||
47 | // Fill in missing days with zero counts |
||||
48 | $result = []; |
||||
49 | for ($i = $days - 1; $i >= 0; $i--) { |
||||
50 | $date = Carbon::now()->subDays($i)->format('Y-m-d'); |
||||
51 | $found = $downloads->firstWhere('date', $date); |
||||
52 | $result[] = [ |
||||
53 | 'date' => Carbon::parse($date)->format('M d'), |
||||
54 | 'count' => $found ? $found->count : 0, |
||||
55 | ]; |
||||
56 | } |
||||
57 | |||||
58 | return $result; |
||||
59 | } |
||||
60 | |||||
61 | /** |
||||
62 | * Get API hits per day for the last 7 days |
||||
63 | * Note: This tracks actual API requests from user_requests table |
||||
64 | */ |
||||
65 | public function getApiHitsPerDay(int $days = 7): array |
||||
66 | { |
||||
67 | $startDate = Carbon::now()->subDays($days - 1)->startOfDay(); |
||||
68 | |||||
69 | // Track actual API requests from user_requests table |
||||
70 | $apiHits = UserRequest::query() |
||||
71 | ->select(DB::raw('DATE(timestamp) as date'), DB::raw('COUNT(*) as count')) |
||||
72 | ->where('timestamp', '>=', $startDate) |
||||
73 | ->groupBy(DB::raw('DATE(timestamp)')) |
||||
74 | ->orderBy('date', 'asc') |
||||
0 ignored issues
–
show
'date' of type string is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $column of Illuminate\Database\Query\Builder::orderBy() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
75 | ->get(); |
||||
76 | |||||
77 | // Fill in missing days with zero counts |
||||
78 | $result = []; |
||||
79 | for ($i = $days - 1; $i >= 0; $i--) { |
||||
80 | $date = Carbon::now()->subDays($i)->format('Y-m-d'); |
||||
81 | $found = $apiHits->firstWhere('date', $date); |
||||
82 | $result[] = [ |
||||
83 | 'date' => Carbon::parse($date)->format('M d'), |
||||
84 | 'count' => $found ? $found->count : 0, |
||||
85 | ]; |
||||
86 | } |
||||
87 | |||||
88 | return $result; |
||||
89 | } |
||||
90 | |||||
91 | /** |
||||
92 | * Get summary statistics |
||||
93 | */ |
||||
94 | public function getSummaryStats(): array |
||||
95 | { |
||||
96 | $today = Carbon::now()->startOfDay(); |
||||
97 | |||||
98 | return [ |
||||
99 | 'total_users' => User::whereNull('deleted_at')->count(), |
||||
100 | 'downloads_today' => UserDownload::where('timestamp', '>=', $today)->count(), |
||||
101 | 'downloads_week' => UserDownload::where('timestamp', '>=', Carbon::now()->subDays(7))->count(), |
||||
102 | 'api_hits_today' => UserRequest::query()->where('timestamp', '>=', $today)->count(), |
||||
103 | 'api_hits_week' => UserRequest::query()->where('timestamp', '>=', Carbon::now()->subDays(7))->count(), |
||||
104 | ]; |
||||
105 | } |
||||
106 | |||||
107 | /** |
||||
108 | * Get top downloaders |
||||
109 | */ |
||||
110 | public function getTopDownloaders(int $limit = 5): array |
||||
111 | { |
||||
112 | $weekAgo = Carbon::now()->subDays(7); |
||||
113 | |||||
114 | return UserDownload::query() |
||||
115 | ->join('users', 'user_downloads.users_id', '=', 'users.id') |
||||
116 | ->select('users.username', DB::raw('COUNT(*) as download_count')) |
||||
117 | ->where('user_downloads.timestamp', '>=', $weekAgo) |
||||
118 | ->groupBy('users.id', 'users.username') |
||||
119 | ->orderByDesc('download_count') |
||||
0 ignored issues
–
show
'download_count' of type string is incompatible with the type Closure|Illuminate\Datab...\Database\Query\Builder expected by parameter $column of Illuminate\Database\Query\Builder::orderByDesc() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
120 | ->limit($limit) |
||||
121 | ->get() |
||||
122 | ->toArray(); |
||||
123 | } |
||||
124 | } |
||||
125 |