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

TopicReadService::markTopicAsRead()   B

Complexity

Conditions 8
Paths 26

Size

Total Lines 63
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 40
c 1
b 0
f 0
dl 0
loc 63
rs 8.0355
cc 8
nc 26
nop 3

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\Service;
6
7
use Doctrine\ORM\EntityManagerInterface;
8
use ProjetNormandie\ForumBundle\Entity\Forum;
9
use ProjetNormandie\ForumBundle\Entity\Topic;
10
use ProjetNormandie\ForumBundle\Entity\TopicUserLastVisit;
11
use ProjetNormandie\ForumBundle\Entity\ForumUserLastVisit;
12
13
class TopicReadService
14
{
15
    private EntityManagerInterface $em;
16
17
    public function __construct(EntityManagerInterface $em)
18
    {
19
        $this->em = $em;
20
    }
21
22
    /**
23
     * Marque un topic comme lu pour un utilisateur
24
     *
25
     * @param mixed $user L'utilisateur
26
     * @param Topic $topic Le topic à marquer comme lu
27
     * @param bool $flush Si true, fait le flush automatiquement
28
     * @return array Informations sur l'opération
29
     */
30
    public function markTopicAsRead($user, Topic $topic, bool $flush = true): array
31
    {
32
        $now = new \DateTime();
33
        $forum = $topic->getForum();
34
35
        // 1. Vérifier si le topic est déjà lu
36
        $topicVisit = $this->em->getRepository(TopicUserLastVisit::class)
37
            ->findOneBy(['user' => $user, 'topic' => $topic]);
38
39
        $wasAlreadyRead = false;
40
        if ($topicVisit && $topic->getLastMessage()) {
41
            $wasAlreadyRead = $topicVisit->getLastVisitedAt() >= $topic->getLastMessage()->getCreatedAt();
42
        }
43
44
        // Si déjà lu, pas besoin de continuer
45
        if ($wasAlreadyRead) {
46
            return [
47
                'topicMarkedAsRead' => false,
48
                'forumMarkedAsRead' => false,
49
                'wasAlreadyRead' => true
50
            ];
51
        }
52
53
        // 2. Mettre à jour ou créer la visite du topic
54
        if ($topicVisit) {
55
            $topicVisit->setLastVisitedAt($now);
56
        } else {
57
            $topicVisit = new TopicUserLastVisit();
58
            $topicVisit->setUser($user);
59
            $topicVisit->setTopic($topic);
60
            $topicVisit->setLastVisitedAt($now);
61
            $this->em->persist($topicVisit);
62
        }
63
64
        // 3. Vérifier si tous les topics du forum sont maintenant lus
65
        $unreadTopicsCount = $this->countUnreadTopicsInForum($user, $forum);
66
        $forumMarkedAsRead = false;
67
68
        // 4. Si aucun topic non lu, marquer le forum comme lu
69
        if ($unreadTopicsCount === 0) {
70
            $forumVisit = $this->em->getRepository(ForumUserLastVisit::class)
71
                ->findOneBy(['user' => $user, 'forum' => $forum]);
72
73
            if ($forumVisit) {
74
                $forumVisit->setLastVisitedAt($now);
75
            } else {
76
                $forumVisit = new ForumUserLastVisit();
77
                $forumVisit->setUser($user);
78
                $forumVisit->setForum($forum);
79
                $forumVisit->setLastVisitedAt($now);
80
                $this->em->persist($forumVisit);
81
            }
82
            $forumMarkedAsRead = true;
83
        }
84
85
        if ($flush) {
86
            $this->em->flush();
87
        }
88
89
        return [
90
            'topicMarkedAsRead' => true,
91
            'forumMarkedAsRead' => $forumMarkedAsRead,
92
            'wasAlreadyRead' => false
93
        ];
94
    }
95
96
    /**
97
     * Compte le nombre de topics non lus dans un forum
98
     */
99
    private function countUnreadTopicsInForum($user, Forum $forum): int
100
    {
101
        try {
102
            // Topics visités mais avec nouveaux messages
103
            $visitedUnreadQuery = $this->em->createQueryBuilder()
104
                ->select('COUNT(t.id)')
105
                ->from('ProjetNormandie\ForumBundle\Entity\TopicUserLastVisit', 'tuv')
106
                ->join('tuv.topic', 't')
107
                ->join('t.lastMessage', 'lm')
108
                ->where('t.forum = :forum')
109
                ->andWhere('tuv.user = :user')
110
                ->andWhere('lm.createdAt > tuv.lastVisitedAt')
111
                ->setParameter('forum', $forum)
112
                ->setParameter('user', $user);
113
114
            $visitedUnread = (int) $visitedUnreadQuery->getQuery()->getSingleScalarResult();
115
116
            // Topics jamais visités avec des messages
117
            $neverVisitedQuery = $this->em->createQueryBuilder()
118
                ->select('COUNT(t.id)')
119
                ->from('ProjetNormandie\ForumBundle\Entity\Topic', 't')
120
                ->where('t.forum = :forum')
121
                ->andWhere('t.lastMessage IS NOT NULL')
122
                ->andWhere('t.id NOT IN (
123
                    SELECT IDENTITY(tuv2.topic) 
124
                    FROM ProjetNormandie\ForumBundle\Entity\TopicUserLastVisit tuv2 
125
                    WHERE tuv2.user = :user
126
                )')
127
                ->setParameter('forum', $forum)
128
                ->setParameter('user', $user);
129
130
            $neverVisited = (int) $neverVisitedQuery->getQuery()->getSingleScalarResult();
131
132
            return $visitedUnread + $neverVisited;
133
134
        } catch (\Exception) {
135
            return 0;
136
        }
137
    }
138
}
139