Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.08% covered (success)
98.08%
51 / 52
85.71% covered (success)
85.71%
6 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
RatingRepository
98.08% covered (success)
98.08%
51 / 52
85.71% covered (success)
85.71%
6 / 7
13
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fetchByRecordId
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 isVoteAllowed
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 getNumberOfVotings
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
5.03
 create
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
 update
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 deleteAll
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * Rating Repository.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2025-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2025-11-19
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Search\Rating;
21
22use phpMyFAQ\Configuration;
23use phpMyFAQ\Database;
24use phpMyFAQ\Entity\Vote;
25use Symfony\Component\HttpFoundation\Request;
26
27readonly class RatingRepository implements RatingRepositoryInterface
28{
29    public function __construct(
30        private Configuration $configuration,
31    ) {
32    }
33
34    public function fetchByRecordId(int $id): ?\stdClass
35    {
36        $sql = <<<SQL
37                SELECT
38                    (vote/usr) as voting,
39                    usr
40                FROM
41                    %sfaqvoting
42                WHERE
43                    artikel = %d
44            SQL;
45
46        $query = sprintf($sql, Database::getTablePrefix(), $id);
47        $result = $this->configuration->getDb()->query($query);
48
49        if ($this->configuration->getDb()->numRows($result) > 0) {
50            $row = $this->configuration->getDb()->fetchObject($result);
51
52            return $row instanceof \stdClass ? $row : null;
53        }
54
55        return null;
56    }
57
58    public function isVoteAllowed(int $id, string $ip): bool
59    {
60        $check = (int) Request::createFromGlobals()->server->get('REQUEST_TIME') - 300;
61
62        $sql = <<<SQL
63                SELECT
64                    id
65                FROM
66                    %sfaqvoting
67                WHERE
68                    artikel = %d
69                AND
70                    (ip = '%s' AND datum > '%s')
71            SQL;
72
73        $query = sprintf($sql, Database::getTablePrefix(), $id, $this->configuration->getDb()->escape($ip), $check);
74
75        $result = $this->configuration->getDb()->query($query);
76        return !$this->configuration->getDb()->numRows($result);
77    }
78
79    public function getNumberOfVotings(int $recordId): int
80    {
81        $sql = <<<SQL
82                SELECT
83                    usr
84                FROM
85                    %sfaqvoting
86                WHERE
87                    artikel = %d
88            SQL;
89
90        $query = sprintf($sql, Database::getTablePrefix(), $recordId);
91        $result = $this->configuration->getDb()->query($query);
92
93        if (!$result) {
94            return 0;
95        }
96
97        $row = $this->configuration->getDb()->fetchObject($result);
98        if ($row !== false && $row !== null && $row !== []) {
99            return (int) $row->usr;
100        }
101
102        return 0;
103    }
104
105    public function create(Vote $vote): bool
106    {
107        $sql = <<<SQL
108                INSERT INTO
109                    %sfaqvoting
110                VALUES
111                    (%d, %d, %d, 1, %d, '%s')
112            SQL;
113
114        $query = sprintf(
115            $sql,
116            Database::getTablePrefix(),
117            $this->configuration->getDb()->nextId(Database::getTablePrefix() . 'faqvoting', 'id'),
118            $vote->getFaqId(),
119            $vote->getVote(),
120            (int) Request::createFromGlobals()->server->get('REQUEST_TIME'),
121            $this->configuration->getDb()->escape($vote->getIp()),
122        );
123
124        return (bool) $this->configuration->getDb()->query($query);
125    }
126
127    public function update(Vote $vote): bool
128    {
129        $sql = <<<SQL
130                UPDATE
131                    %sfaqvoting
132                SET
133                    vote = vote + %d,
134                    usr = usr + 1,
135                    datum = %d,
136                    ip = '%s'
137                WHERE
138                    artikel = %d
139            SQL;
140
141        $query = sprintf(
142            $sql,
143            Database::getTablePrefix(),
144            $vote->getVote(),
145            (int) Request::createFromGlobals()->server->get('REQUEST_TIME'),
146            $this->configuration->getDb()->escape($vote->getIp()),
147            $vote->getFaqId(),
148        );
149
150        return (bool) $this->configuration->getDb()->query($query);
151    }
152
153    public function deleteAll(): bool
154    {
155        $sql = <<<SQL
156                DELETE FROM
157                    %sfaqvoting
158            SQL;
159
160        $query = sprintf($sql, Database::getTablePrefix());
161        return (bool) $this->configuration->getDb()->query($query);
162    }
163}