Passed
Push — develop ( ef53f3...80bc53 )
by BENARD
08:34
created

GetHome::getUnreadTopicsCountByForum()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 53
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 30
c 1
b 0
f 0
dl 0
loc 53
rs 9.44
cc 4
nc 8
nop 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace ProjetNormandie\ForumBundle\Controller;
6
7
use Doctrine\DBAL\Exception;
8
use Doctrine\ORM\EntityManagerInterface;
9
use ProjetNormandie\ForumBundle\ValueObject\ForumStatus;
10
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
11
12
class GetHome extends AbstractController
13
{
14
    private EntityManagerInterface $em;
15
16
    public function __construct(EntityManagerInterface $em)
17
    {
18
        $this->em = $em;
19
    }
20
21
    /**
22
     * @return mixed
23
     * @throws Exception
24
     */
25
    public function __invoke(): mixed
26
    {
27
        $user = $this->getUser();
28
29
        $queryBuilder = $this->em->createQueryBuilder()
30
            ->from('ProjetNormandie\ForumBundle\Entity\Category', 'c')
31
            ->select('c')
32
            ->join('c.forums', 'f')
33
            ->join('f.lastMessage', 'm')
34
            ->join('m.user', 'u')
35
            ->addSelect('f')
36
            ->addSelect('m')
37
            ->addSelect('u');
38
39
        if ($user !== null) {
40
            // Jointure LEFT pour les visites de forum (optionnelle)
41
            $queryBuilder
42
                ->leftJoin(
43
                    'f.userLastVisits',
44
                    'fuv',
45
                    'WITH',
46
                    'fuv.user = :user'
47
                )
48
                ->addSelect('fuv')
49
                ->where(
50
                    $queryBuilder->expr()->orX(
51
                        'f.status = :status1',
52
                        '(f.status = :status2) AND (f.role IN (:roles))'
53
                    )
54
                )
55
                ->setParameter('status1', ForumStatus::PUBLIC)
56
                ->setParameter('status2', ForumStatus::PRIVATE)
57
                ->setParameter('user', $user)
58
                ->setParameter('roles', $user->getRoles());
59
        } else {
60
            $queryBuilder->where('f.status = :status')
61
                ->setParameter('status', ForumStatus::PUBLIC);
62
        }
63
64
        // Filtrer les catégories qui doivent être affichées sur la home
65
        $queryBuilder->andWhere('c.displayOnHome = :displayOnHome')
66
            ->setParameter('displayOnHome', true);
67
68
        $queryBuilder->orderBy('c.position', 'ASC')
69
            ->addOrderBy('f.position', 'ASC');
70
71
        $categories = $queryBuilder->getQuery()->getResult();
72
73
        // Si utilisateur connecté, enrichir avec les compteurs de topics non lus
74
        if ($user !== null) {
75
            $this->enrichWithReadStatus($categories, $user);
76
        }
77
78
        return $categories;
79
    }
80
81
    /**
82
     * Enrichit les catégories avec les informations de lecture
83
     */
84
    private function enrichWithReadStatus(array $categories, $user): void
85
    {
86
        // Récupérer tous les forums des catégories
87
        $forumIds = [];
88
        $forumsById = [];
89
90
        foreach ($categories as $category) {
91
            foreach ($category->getForums() as $forum) {
92
                $forumIds[] = $forum->getId();
93
                $forumsById[$forum->getId()] = $forum;
94
            }
95
        }
96
97
        if (empty($forumIds)) {
98
            return;
99
        }
100
101
        // Récupérer les compteurs de topics non lus pour tous les forums
102
        $unreadCounts = $this->getUnreadTopicsCountByForum($user, $forumIds);
103
104
        // Enrichir chaque forum avec ses informations
105
        foreach ($forumsById as $forumId => $forum) {
106
            // Compter les topics non lus
107
            $forum->unreadTopicsCount = $unreadCounts[$forumId] ?? 0;
108
            $forum->isUnread = $forum->unreadTopicsCount > 0;
109
        }
110
    }
111
112
113
    /**
114
     * Récupère le nombre de topics non lus par forum en une seule requête optimisée
115
     */
116
    private function getUnreadTopicsCountByForum($user, array $forumIds): array
117
    {
118
        // Requête 1: Topics visités mais avec nouveaux messages
119
        $visitedUnreadQuery = $this->em->createQueryBuilder()
120
            ->select('IDENTITY(t.forum) as forum_id, COUNT(t.id) as unread_count')
121
            ->from('ProjetNormandie\ForumBundle\Entity\TopicUserLastVisit', 'tuv')
122
            ->join('tuv.topic', 't')
123
            ->join('t.lastMessage', 'lm')
124
            ->where('t.forum IN (:forumIds)')
125
            ->andWhere('tuv.user = :user')
126
            ->andWhere('lm.createdAt > tuv.lastVisitedAt')
127
            ->groupBy('t.forum')
128
            ->setParameter('forumIds', $forumIds)
129
            ->setParameter('user', $user);
130
131
        $visitedUnread = $visitedUnreadQuery->getQuery()->getResult();
132
133
        // Requête 2: Topics jamais visités avec des messages
134
        $neverVisitedQuery = $this->em->createQueryBuilder()
135
            ->select('IDENTITY(t.forum) as forum_id, COUNT(t.id) as unread_count')
136
            ->from('ProjetNormandie\ForumBundle\Entity\Topic', 't')
137
            ->where('t.forum IN (:forumIds)')
138
            ->andWhere('t.lastMessage IS NOT NULL')
139
            ->andWhere('t.id NOT IN (
140
                SELECT IDENTITY(tuv2.topic) 
141
                FROM ProjetNormandie\ForumBundle\Entity\TopicUserLastVisit tuv2 
142
                WHERE tuv2.user = :user
143
            )')
144
            ->groupBy('t.forum')
145
            ->setParameter('forumIds', $forumIds)
146
            ->setParameter('user', $user);
147
148
        $neverVisited = $neverVisitedQuery->getQuery()->getResult();
149
150
        // Fusionner les résultats
151
        $result = [];
152
153
        // Initialiser tous les forums à 0
154
        foreach ($forumIds as $forumId) {
155
            $result[$forumId] = 0;
156
        }
157
158
        // Ajouter les topics visités mais non lus
159
        foreach ($visitedUnread as $row) {
160
            $result[(int)$row['forum_id']] += (int)$row['unread_count'];
161
        }
162
163
        // Ajouter les topics jamais visités
164
        foreach ($neverVisited as $row) {
165
            $result[(int)$row['forum_id']] += (int)$row['unread_count'];
166
        }
167
168
        return $result;
169
    }
170
}
171