Passed
Push — develop ( 9c991d...498af7 )
by BENARD
02:44
created

GetStats   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 368
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 34
eloc 197
c 1
b 0
f 0
dl 0
loc 368
rs 9.68

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getTopTopics() 0 17 2
A countActiveUsers() 0 35 2
A countMessages() 0 13 2
A getRecentActivity() 0 26 2
A getTopActiveUsers() 0 22 2
A getWeekActivity() 0 30 2
A countNewMessagesToday() 0 15 2
A calculateStats() 0 16 1
A __invoke() 0 12 2
A calculateExtendedStats() 0 9 1
A __construct() 0 4 1
A countTopics() 0 14 2
A invalidateCache() 0 8 2
A getLastMessageInfo() 0 14 3
A getExtendedStats() 0 11 3
A countNewTopicsToday() 0 14 2
A getStats() 0 11 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ProjetNormandie\ForumBundle\Controller\Forum;
6
7
use Doctrine\ORM\EntityManagerInterface;
8
use ProjetNormandie\ForumBundle\Entity\Forum;
9
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
10
use Symfony\Component\HttpFoundation\JsonResponse;
11
use Symfony\Component\HttpFoundation\Request;
12
use Symfony\Contracts\Cache\CacheInterface;
13
use Symfony\Contracts\Cache\ItemInterface;
14
15
class GetStats extends AbstractController
16
{
17
    public function __construct(
18
        private readonly EntityManagerInterface $em,
19
        private readonly ?CacheInterface $cache = null
20
    ) {
21
    }
22
23
    /**
24
     * Retourne les statistiques d'un forum spécifique
25
     */
26
    public function __invoke(Forum $forum, Request $request): JsonResponse
27
    {
28
        $extended = $request->query->getBoolean('extended', false);
29
        $forceRefresh = $request->query->getBoolean('refresh', false);
30
31
        if ($extended) {
32
            $stats = $this->getExtendedStats($forum, $forceRefresh);
33
        } else {
34
            $stats = $this->getStats($forum, $forceRefresh);
35
        }
36
37
        return new JsonResponse($stats);
38
    }
39
40
    /**
41
     * Récupère les statistiques du forum avec mise en cache
42
     */
43
    public function getStats(Forum $forum, bool $forceRefresh = false): array
44
    {
45
        $cacheKey = 'forum_stats_' . $forum->getId();
46
47
        if (!$this->cache || $forceRefresh) {
48
            return $this->calculateStats($forum);
49
        }
50
51
        return $this->cache->get($cacheKey, function (ItemInterface $item) use ($forum) {
52
            $item->expiresAfter(300); // Cache pendant 5 minutes
53
            return $this->calculateStats($forum);
54
        });
55
    }
56
57
    /**
58
     * Calcule les statistiques du forum
59
     */
60
    private function calculateStats(Forum $forum): array
61
    {
62
        $now = new \DateTime();
63
        $yesterday = (clone $now)->modify('-1 day');
64
        $today = (clone $now)->setTime(0, 0, 0);
65
66
        return [
67
            'nbTopic' => $this->countTopics($forum),
68
            'nbMessage' => $this->countMessages($forum),
69
            'activeUsers' => $this->countActiveUsers($forum, $yesterday),
70
            'todayActivity' => [
71
                'nbNewTopic' => $this->countNewTopicsToday($forum, $today),
72
                'nbNewMessage' => $this->countNewMessagesToday($forum, $today),
73
            ],
74
            'lastMessage' => $this->getLastMessageInfo($forum),
75
            'lastUpdate' => $now->format('c'),
76
        ];
77
    }
78
79
    /**
80
     * Invalide le cache des statistiques pour un forum
81
     */
82
    public function invalidateCache(Forum $forum): void
83
    {
84
        if ($this->cache) {
85
            $cacheKey = 'forum_stats_' . $forum->getId();
86
            $extendedCacheKey = 'forum_stats_extended_' . $forum->getId();
87
88
            $this->cache->delete($cacheKey);
89
            $this->cache->delete($extendedCacheKey);
90
        }
91
    }
92
93
    /**
94
     * Compte le nombre de topics non archivés dans le forum
95
     */
96
    private function countTopics(Forum $forum): int
97
    {
98
        try {
99
            $query = $this->em->createQueryBuilder()
100
                ->select('COUNT(t.id)')
101
                ->from('ProjetNormandie\ForumBundle\Entity\Topic', 't')
102
                ->where('t.forum = :forum')
103
                ->andWhere('t.boolArchive = :archived')
104
                ->setParameter('forum', $forum)
105
                ->setParameter('archived', false);
106
107
            return (int) $query->getQuery()->getSingleScalarResult();
108
        } catch (\Exception) {
109
            return 0;
110
        }
111
    }
112
113
    /**
114
     * Compte le nombre de messages dans le forum
115
     */
116
    private function countMessages(Forum $forum): int
117
    {
118
        try {
119
            $query = $this->em->createQueryBuilder()
120
                ->select('COUNT(m.id)')
121
                ->from('ProjetNormandie\ForumBundle\Entity\Message', 'm')
122
                ->join('m.topic', 't')
123
                ->where('t.forum = :forum')
124
                ->setParameter('forum', $forum);
125
126
            return (int) $query->getQuery()->getSingleScalarResult();
127
        } catch (\Exception) {
128
            return 0;
129
        }
130
    }
131
132
    /**
133
     * Compte le nombre d'utilisateurs actifs dans le forum (dernières 24h)
134
     */
135
    private function countActiveUsers(Forum $forum, \DateTime $since): int
136
    {
137
        try {
138
            // Utilisateurs actifs basés sur les visites de topics du forum
139
            $topicVisitsQuery = $this->em->createQueryBuilder()
140
                ->select('DISTINCT IDENTITY(tuv.user)')
141
                ->from('ProjetNormandie\ForumBundle\Entity\TopicUserLastVisit', 'tuv')
142
                ->join('tuv.topic', 't')
143
                ->where('t.forum = :forum')
144
                ->andWhere('tuv.lastVisitedAt >= :since')
145
                ->setParameter('forum', $forum)
146
                ->setParameter('since', $since);
147
148
            $topicActiveUsers = $topicVisitsQuery->getQuery()->getResult();
149
150
            // Utilisateurs actifs basés sur les visites du forum directement
151
            $forumVisitsQuery = $this->em->createQueryBuilder()
152
                ->select('DISTINCT IDENTITY(fuv.user)')
153
                ->from('ProjetNormandie\ForumBundle\Entity\ForumUserLastVisit', 'fuv')
154
                ->where('fuv.forum = :forum')
155
                ->andWhere('fuv.lastVisitedAt >= :since')
156
                ->setParameter('forum', $forum)
157
                ->setParameter('since', $since);
158
159
            $forumActiveUsers = $forumVisitsQuery->getQuery()->getResult();
160
161
            // Fusionner et dédupliquer les utilisateurs
162
            $allActiveUsers = array_unique(array_merge(
163
                array_column($topicActiveUsers, 1),
164
                array_column($forumActiveUsers, 1)
165
            ));
166
167
            return count($allActiveUsers);
168
        } catch (\Exception) {
169
            return 0;
170
        }
171
    }
172
173
    /**
174
     * Compte le nombre de nouveaux topics créés aujourd'hui dans le forum
175
     */
176
    private function countNewTopicsToday(Forum $forum, \DateTime $today): int
177
    {
178
        try {
179
            $query = $this->em->createQueryBuilder()
180
                ->select('COUNT(t.id)')
181
                ->from('ProjetNormandie\ForumBundle\Entity\Topic', 't')
182
                ->where('t.forum = :forum')
183
                ->andWhere('t.createdAt >= :today')
184
                ->setParameter('forum', $forum)
185
                ->setParameter('today', $today);
186
187
            return (int) $query->getQuery()->getSingleScalarResult();
188
        } catch (\Exception) {
189
            return 0;
190
        }
191
    }
192
193
    /**
194
     * Compte le nombre de nouveaux messages créés aujourd'hui dans le forum
195
     */
196
    private function countNewMessagesToday(Forum $forum, \DateTime $today): int
197
    {
198
        try {
199
            $query = $this->em->createQueryBuilder()
200
                ->select('COUNT(m.id)')
201
                ->from('ProjetNormandie\ForumBundle\Entity\Message', 'm')
202
                ->join('m.topic', 't')
203
                ->where('t.forum = :forum')
204
                ->andWhere('m.createdAt >= :today')
205
                ->setParameter('forum', $forum)
206
                ->setParameter('today', $today);
207
208
            return (int) $query->getQuery()->getSingleScalarResult();
209
        } catch (\Exception) {
210
            return 0;
211
        }
212
    }
213
214
    /**
215
     * Récupère les informations du dernier message du forum
216
     */
217
    private function getLastMessageInfo(Forum $forum): ?array
218
    {
219
        try {
220
            $message = $forum->getLastMessage();
221
            if (null !== $message) {
222
                return [
223
                    'createdAt' => $message->getCreatedAt()->format('Y-m-d H:i:s'),
224
                    'username' => $message->getUser()->getUsername(),
225
                ];
226
            } else {
227
                return null;
228
            }
229
        } catch (\Exception) {
230
            return null;
231
        }
232
    }
233
234
    /**
235
     * Récupère des statistiques étendues pour le forum
236
     */
237
    public function getExtendedStats(Forum $forum, bool $forceRefresh = false): array
238
    {
239
        $cacheKey = 'forum_stats_extended_' . $forum->getId();
240
241
        if (!$this->cache || $forceRefresh) {
242
            return $this->calculateExtendedStats($forum);
243
        }
244
245
        return $this->cache->get($cacheKey, function (ItemInterface $item) use ($forum) {
246
            $item->expiresAfter(300); // Cache pendant 5 minutes
247
            return $this->calculateExtendedStats($forum);
248
        });
249
    }
250
251
    /**
252
     * Calcule les statistiques étendues pour le forum
253
     */
254
    private function calculateExtendedStats(Forum $forum): array
255
    {
256
        $baseStats = $this->calculateStats($forum);
257
258
        return array_merge($baseStats, [
259
            'weekActivity' => $this->getWeekActivity($forum),
260
            'topActiveUsers' => $this->getTopActiveUsers($forum),
261
            'topTopics' => $this->getTopTopics($forum),
262
            'recentActivity' => $this->getRecentActivity($forum),
263
        ]);
264
    }
265
266
    /**
267
     * Récupère l'activité de la semaine pour le forum
268
     */
269
    private function getWeekActivity(Forum $forum): array
270
    {
271
        $weekAgo = (new \DateTime())->modify('-7 days');
272
273
        try {
274
            $topicQuery = $this->em->createQueryBuilder()
275
                ->select('COUNT(t.id)')
276
                ->from('ProjetNormandie\ForumBundle\Entity\Topic', 't')
277
                ->where('t.forum = :forum')
278
                ->andWhere('t.createdAt >= :weekAgo')
279
                ->setParameter('forum', $forum)
280
                ->setParameter('weekAgo', $weekAgo);
281
282
            $messageQuery = $this->em->createQueryBuilder()
283
                ->select('COUNT(m.id)')
284
                ->from('ProjetNormandie\ForumBundle\Entity\Message', 'm')
285
                ->join('m.topic', 't')
286
                ->where('t.forum = :forum')
287
                ->andWhere('m.createdAt >= :weekAgo')
288
                ->setParameter('forum', $forum)
289
                ->setParameter('weekAgo', $weekAgo);
290
291
            return [
292
                'nbNewTopicWeek' => (int) $topicQuery->getQuery()->getSingleScalarResult(),
293
                'nbNewMessageWeek' => (int) $messageQuery->getQuery()->getSingleScalarResult(),
294
            ];
295
        } catch (\Exception) {
296
            return [
297
                'nbNewTopicWeek' => 0,
298
                'nbNewMessageWeek' => 0,
299
            ];
300
        }
301
    }
302
303
    /**
304
     * Récupère les utilisateurs les plus actifs dans le forum (dernières 24h)
305
     */
306
    private function getTopActiveUsers(Forum $forum, int $limit = 5): array
307
    {
308
        $yesterday = (new \DateTime())->modify('-1 day');
309
310
        try {
311
            // Activité basée sur les messages postés
312
            $query = $this->em->createQueryBuilder()
313
                ->select('IDENTITY(m.user) as user_id, u.pseudo, COUNT(m.id) as activity_count')
314
                ->from('ProjetNormandie\ForumBundle\Entity\Message', 'm')
315
                ->join('m.topic', 't')
316
                ->join('m.user', 'u')
317
                ->where('t.forum = :forum')
318
                ->andWhere('m.createdAt >= :since')
319
                ->groupBy('m.user, u.pseudo')
320
                ->orderBy('activity_count', 'DESC')
321
                ->setMaxResults($limit)
322
                ->setParameter('forum', $forum)
323
                ->setParameter('since', $yesterday);
324
325
            return $query->getQuery()->getResult();
326
        } catch (\Exception) {
327
            return [];
328
        }
329
    }
330
331
    /**
332
     * Récupère les topics les plus actifs du forum
333
     */
334
    private function getTopTopics(Forum $forum, int $limit = 5): array
335
    {
336
        try {
337
            $query = $this->em->createQueryBuilder()
338
                ->select('t.id, t.name, t.nbMessage, t.createdAt')
339
                ->from('ProjetNormandie\ForumBundle\Entity\Topic', 't')
340
                ->where('t.forum = :forum')
341
                ->andWhere('t.boolArchive = :archived')
342
                ->orderBy('t.nbMessage', 'DESC')
343
                ->addOrderBy('t.createdAt', 'DESC')
344
                ->setMaxResults($limit)
345
                ->setParameter('forum', $forum)
346
                ->setParameter('archived', false);
347
348
            return $query->getQuery()->getResult();
349
        } catch (\Exception) {
350
            return [];
351
        }
352
    }
353
354
    /**
355
     * Récupère l'activité récente du forum
356
     */
357
    private function getRecentActivity(Forum $forum, int $limit = 10): array
358
    {
359
        try {
360
            $query = $this->em->createQueryBuilder()
361
                ->select('m.id, m.createdAt, t.id as topicId, t.name as topicName, u.pseudo')
362
                ->from('ProjetNormandie\ForumBundle\Entity\Message', 'm')
363
                ->join('m.topic', 't')
364
                ->join('m.user', 'u')
365
                ->where('t.forum = :forum')
366
                ->orderBy('m.createdAt', 'DESC')
367
                ->setMaxResults($limit)
368
                ->setParameter('forum', $forum);
369
370
            $results = $query->getQuery()->getResult();
371
372
            return array_map(function ($result) {
373
                return [
374
                    'messageId' => $result['id'],
375
                    'topicId' => $result['topicId'],
376
                    'topicName' => $result['topicName'],
377
                    'userPseudo' => $result['pseudo'],
378
                    'createdAt' => $result['createdAt']->format('c'),
379
                ];
380
            }, $results);
381
        } catch (\Exception) {
382
            return [];
383
        }
384
    }
385
}
386