Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
100 / 100
100.00% covered (success)
100.00%
8 / 8
CRAP
100.00% covered (success)
100.00%
1 / 1
QuestionRepository
100.00% covered (success)
100.00%
100 / 100
100.00% covered (success)
100.00%
8 / 8
18
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 add
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
2
 delete
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 getById
100.00% covered (success)
100.00%
19 / 19
100.00% covered (success)
100.00%
1 / 1
3
 getAll
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
6
 getVisibility
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
3
 setVisibility
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 updateQuestionAnswer
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * Question 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 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-10
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Question;
21
22use phpMyFAQ\Configuration;
23use phpMyFAQ\Database;
24use phpMyFAQ\Entity\QuestionEntity;
25
26readonly class QuestionRepository
27{
28    public function __construct(
29        private Configuration $configuration,
30    ) {
31    }
32
33    /**
34     * Adds a new question to the database.
35     */
36    public function add(QuestionEntity $questionEntity): bool
37    {
38        $query = sprintf(
39            "
40            INSERT INTO
41                %sfaqquestions
42            (id, lang, username, email, category_id, question, created, is_visible, answer_id)
43                VALUES
44            (%d, '%s', '%s', '%s', %d, '%s', '%s', '%s', %d)",
45            Database::getTablePrefix(),
46            $this->configuration->getDb()->nextId(Database::getTablePrefix() . 'faqquestions', column: 'id'),
47            $this->configuration->getDb()->escape($questionEntity->getLanguage()),
48            $this->configuration->getDb()->escape($questionEntity->getUsername()),
49            $this->configuration->getDb()->escape($questionEntity->getEmail()),
50            $questionEntity->getCategoryId(),
51            $this->configuration->getDb()->escape($questionEntity->getQuestion()),
52            date(format: 'YmdHis'),
53            $questionEntity->isVisible() ? 'Y' : 'N',
54            0,
55        );
56
57        return (bool) $this->configuration->getDb()->query($query);
58    }
59
60    /**
61     * Deletes a question from the database.
62     *
63     * @param int $questionId Question ID
64     * @param string $language Language code
65     */
66    public function delete(int $questionId, string $language): bool
67    {
68        $db = $this->configuration->getDb();
69        $delete = sprintf(
70            "DELETE FROM %sfaqquestions WHERE id = %d AND lang = '%s'",
71            Database::getTablePrefix(),
72            $questionId,
73            $db->escape($language),
74        );
75
76        return (bool) $db->query($delete);
77    }
78
79    /**
80     * Returns a question by ID and language.
81     *
82     * @param int $questionId Question ID
83     * @param string $language Language code
84     * @return array<string, int|string>
85     */
86    public function getById(int $questionId, string $language): array
87    {
88        $question = [];
89        $db = $this->configuration->getDb();
90
91        $query = sprintf("
92            SELECT
93                 id, lang, username, email, category_id, question, created, is_visible
94            FROM
95                %sfaqquestions
96            WHERE
97                id = %d
98            AND
99                lang = '%s'", Database::getTablePrefix(), $questionId, $db->escape($language));
100
101        $result = $db->query($query);
102        if ($result !== false) {
103            $row = $db->fetchObject($result);
104            if ($row instanceof \stdClass) {
105                return [
106                    'id' => (int) $row->id,
107                    'lang' => (string) $row->lang,
108                    'username' => (string) $row->username,
109                    'email' => (string) $row->email,
110                    'category_id' => (int) $row->category_id,
111                    'question' => (string) $row->question,
112                    'created' => (string) $row->created,
113                    'is_visible' => (string) $row->is_visible,
114                ];
115            }
116        }
117
118        return $question;
119    }
120
121    /**
122     * Returns all questions for a given language.
123     *
124     * @param string $language Language code
125     * @param bool $showAll Whether to show all questions or only visible ones
126     * @return array<int, array<string, int|string>>
127     */
128    public function getAll(string $language, bool $showAll = true): array
129    {
130        $questions = [];
131        $db = $this->configuration->getDb();
132        $langFilter = $language !== '' ? sprintf("AND lang = '%s'", $db->escape($language)) : '';
133
134        $query = sprintf(
135            '
136            SELECT
137                id, lang, username, email, category_id, question, created, answer_id, is_visible
138            FROM
139                %sfaqquestions
140            WHERE
141                1 = 1 %s %s
142            ORDER BY
143                created ASC',
144            Database::getTablePrefix(),
145            $langFilter,
146            $showAll === false ? " AND is_visible = 'Y'" : '',
147        );
148
149        $result = $this->configuration->getDb()->query($query);
150        if ($result !== false) {
151            while (true) {
152                $row = $this->configuration->getDb()->fetchObject($result);
153                if (!is_object($row)) {
154                    break;
155                }
156
157                $questions[] = [
158                    'id' => (int) $row->id,
159                    'lang' => (string) $row->lang,
160                    'username' => (string) $row->username,
161                    'email' => (string) $row->email,
162                    'category_id' => (int) $row->category_id,
163                    'question' => (string) $row->question,
164                    'created' => (string) $row->created,
165                    'answer_id' => (int) $row->answer_id,
166                    'is_visible' => (string) $row->is_visible,
167                ];
168            }
169        }
170
171        return $questions;
172    }
173
174    /**
175     * Returns the visibility of a question.
176     *
177     * @param int $questionId Question ID
178     * @param string $language Language code
179     */
180    public function getVisibility(int $questionId, string $language): string
181    {
182        $db = $this->configuration->getDb();
183        $query = sprintf(
184            "SELECT is_visible FROM %sfaqquestions WHERE id = %d AND lang = '%s'",
185            Database::getTablePrefix(),
186            $questionId,
187            $db->escape($language),
188        );
189
190        $result = $db->query($query);
191        if ($db->numRows($result) > 0) {
192            $row = $db->fetchObject($result);
193
194            return $row instanceof \stdClass ? (string) $row->is_visible : '';
195        }
196
197        return '';
198    }
199
200    /**
201     * Sets the visibility of a question.
202     *
203     * @param int $questionId Question ID
204     * @param string $isVisible Visibility status
205     * @param string $language Language code
206     */
207    public function setVisibility(int $questionId, string $isVisible, string $language): bool
208    {
209        $db = $this->configuration->getDb();
210        $query = sprintf(
211            "UPDATE %sfaqquestions SET is_visible = '%s' WHERE id = %d AND lang = '%s'",
212            Database::getTablePrefix(),
213            $db->escape($isVisible),
214            $questionId,
215            $db->escape($language),
216        );
217
218        return (bool) $db->query($query);
219    }
220
221    /**
222     * Updates the answer_id field for a question.
223     *
224     * @param int $openQuestionId Question ID
225     * @param int $faqId FAQ ID
226     * @param int $categoryId Category ID
227     */
228    public function updateQuestionAnswer(int $openQuestionId, int $faqId, int $categoryId): bool
229    {
230        $query = sprintf(
231            'UPDATE %sfaqquestions SET answer_id = %d, category_id= %d WHERE id= %d',
232            Database::getTablePrefix(),
233            $faqId,
234            $categoryId,
235            $openQuestionId,
236        );
237
238        return (bool) $this->configuration->getDb()->query($query);
239    }
240}