Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 113
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
Reaction
0.00% covered (danger)
0.00%
0 / 113
0.00% covered (danger)
0.00%
0 / 7
650
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 save
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
42
 delete
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 toggle
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
30
 getForArticle
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
20
 getUserReaction
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 getCountsForArticle
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2
3namespace BuyerKiosk\Support;
4
5use \PDO;
6use \PDOException;
7
8/**
9 * Reaction - Article reaction model (store-level)
10 *
11 * Manages employee reactions to support articles (like, helpful, etc.)
12 * Stored in individual store databases for multi-tenant isolation.
13 */
14class Reaction
15{
16    /**
17     * @var int|null Reaction ID
18     */
19    public $id;
20
21    /**
22     * @var int Article ID (from kiosk_buykiosk.support_articles)
23     */
24    public $articleId;
25
26    /**
27     * @var int Employee ID (from store database)
28     */
29    public $employeeId;
30
31    /**
32     * @var string Reaction type (like, helpful, heart, etc.)
33     */
34    public $reactionType;
35
36    /**
37     * @var string Created timestamp
38     */
39    public $createdAt;
40
41    /**
42     * @var PDO Database connection (store DB)
43     */
44    private $db;
45
46    /**
47     * Constructor
48     *
49     * @param PDO $db Store database connection
50     */
51    public function __construct(PDO $db)
52    {
53        $this->db = $db;
54    }
55
56    /**
57     * Save reaction (insert only - reactions are typically toggled)
58     *
59     * @return bool True if successful
60     */
61    public function save(): bool
62    {
63        if (!isset($this->articleId) || !isset($this->employeeId) || !isset($this->reactionType)) {
64            error_log("Reaction::save error: articleId, employeeId, and reactionType are required");
65            return false;
66        }
67
68        try {
69            $stmt = $this->db->prepare("
70                INSERT INTO support_article_reactions
71                (article_id, employee_id, reaction_type)
72                VALUES
73                (:articleId, :employeeId, :reactionType)
74            ");
75
76            $result = $stmt->execute([
77                ':articleId' => $this->articleId,
78                ':employeeId' => $this->employeeId,
79                ':reactionType' => $this->reactionType
80            ]);
81
82            if ($result) {
83                $this->id = (int) $this->db->lastInsertId();
84            }
85
86            return $result;
87        } catch (PDOException $e) {
88            error_log("Reaction::save error: " . $e->getMessage());
89            return false;
90        }
91    }
92
93    /**
94     * Delete reaction
95     *
96     * @return bool True if successful
97     */
98    public function delete(): bool
99    {
100        if (!isset($this->id)) {
101            error_log("Reaction::delete error: id is required");
102            return false;
103        }
104
105        try {
106            $stmt = $this->db->prepare("
107                DELETE FROM support_article_reactions
108                WHERE id = :id
109            ");
110
111            return $stmt->execute([':id' => $this->id]);
112        } catch (PDOException $e) {
113            error_log("Reaction::delete error: " . $e->getMessage());
114            return false;
115        }
116    }
117
118    /**
119     * Toggle reaction for an employee on an article
120     *
121     * If the employee has already reacted with this type, remove it.
122     * Otherwise, add the reaction.
123     *
124     * @param PDO $db Store database connection
125     * @param int $articleId Article ID
126     * @param int $employeeId Employee ID
127     * @param string $reactionType Reaction type
128     * @return array Result with 'action' (added/removed) and 'reaction' keys
129     */
130    public static function toggle(PDO $db, int $articleId, int $employeeId, string $reactionType): array
131    {
132        try {
133            // Check if reaction already exists
134            $stmt = $db->prepare("
135                SELECT * FROM support_article_reactions
136                WHERE article_id = :articleId
137                  AND employee_id = :employeeId
138                  AND reaction_type = :reactionType
139            ");
140
141            $stmt->execute([
142                ':articleId' => $articleId,
143                ':employeeId' => $employeeId,
144                ':reactionType' => $reactionType
145            ]);
146
147            $existing = $stmt->fetch(PDO::FETCH_ASSOC);
148
149            if ($existing) {
150                // Remove existing reaction
151                $reaction = new self($db);
152                $reaction->id = (int) $existing['id'];
153                $reaction->articleId = $articleId;
154                $reaction->employeeId = $employeeId;
155                $reaction->reactionType = $reactionType;
156
157                if ($reaction->delete()) {
158                    return [
159                        'action' => 'removed',
160                        'reaction' => null
161                    ];
162                }
163
164                return [
165                    'action' => 'error',
166                    'reaction' => null
167                ];
168            } else {
169                // Add new reaction
170                $reaction = new self($db);
171                $reaction->articleId = $articleId;
172                $reaction->employeeId = $employeeId;
173                $reaction->reactionType = $reactionType;
174
175                if ($reaction->save()) {
176                    return [
177                        'action' => 'added',
178                        'reaction' => [
179                            'id' => $reaction->id,
180                            'articleId' => $reaction->articleId,
181                            'employeeId' => $reaction->employeeId,
182                            'reactionType' => $reaction->reactionType,
183                            'createdAt' => $reaction->createdAt
184                        ]
185                    ];
186                }
187
188                return [
189                    'action' => 'error',
190                    'reaction' => null
191                ];
192            }
193        } catch (PDOException $e) {
194            error_log("Reaction::toggle error: " . $e->getMessage());
195            // Re-throw so controller can get the actual error
196            throw $e;
197        }
198    }
199
200    /**
201     * Get all reactions for an article
202     *
203     * @param PDO $db Store database connection
204     * @param int $articleId Article ID
205     * @return array Array of reaction data grouped by type
206     */
207    public static function getForArticle(PDO $db, int $articleId): array
208    {
209        try {
210            $stmt = $db->prepare("
211                SELECT
212                    r.*,
213                    e.employeeFirstName,
214                    e.employeeLastName
215                FROM support_article_reactions r
216                LEFT JOIN employees e ON r.employee_id = e.employeeID
217                WHERE r.article_id = :articleId
218                ORDER BY r.created_at DESC
219            ");
220
221            $stmt->execute([':articleId' => $articleId]);
222
223            $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
224            $reactions = [];
225
226            foreach ($rows as $row) {
227                $type = $row['reaction_type'];
228                if (!isset($reactions[$type])) {
229                    $reactions[$type] = [
230                        'count' => 0,
231                        'employees' => []
232                    ];
233                }
234
235                $reactions[$type]['count']++;
236                $reactions[$type]['employees'][] = [
237                    'id' => (int) $row['employee_id'],
238                    'name' => trim(($row['employeeFirstName'] ?? '') . ' ' . ($row['employeeLastName'] ?? ''))
239                ];
240            }
241
242            return $reactions;
243        } catch (PDOException $e) {
244            error_log("Reaction::getForArticle error: " . $e->getMessage());
245            return [];
246        }
247    }
248
249    /**
250     * Get user's reaction for an article
251     *
252     * Returns the reaction type if the employee has reacted, null otherwise
253     *
254     * @param PDO $db Store database connection
255     * @param int $articleId Article ID
256     * @param int $employeeId Employee ID
257     * @return string|null Reaction type or null
258     */
259    public static function getUserReaction(PDO $db, int $articleId, int $employeeId): ?string
260    {
261        try {
262            $stmt = $db->prepare("
263                SELECT reaction_type FROM support_article_reactions
264                WHERE article_id = :articleId AND employee_id = :employeeId
265                LIMIT 1
266            ");
267
268            $stmt->execute([
269                ':articleId' => $articleId,
270                ':employeeId' => $employeeId
271            ]);
272
273            $result = $stmt->fetchColumn();
274            return $result !== false ? $result : null;
275        } catch (PDOException $e) {
276            error_log("Reaction::getUserReaction error: " . $e->getMessage());
277            return null;
278        }
279    }
280
281    /**
282     * Get reaction counts for an article
283     *
284     * @param PDO $db Store database connection
285     * @param int $articleId Article ID
286     * @return array Associative array with reaction types as keys and counts as values
287     */
288    public static function getCountsForArticle(PDO $db, int $articleId): array
289    {
290        try {
291            $stmt = $db->prepare("
292                SELECT reaction_type, COUNT(*) as count
293                FROM support_article_reactions
294                WHERE article_id = :articleId
295                GROUP BY reaction_type
296            ");
297
298            $stmt->execute([':articleId' => $articleId]);
299
300            $counts = [];
301            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
302                $counts[$row['reaction_type']] = (int) $row['count'];
303            }
304
305            return $counts;
306        } catch (PDOException $e) {
307            error_log("Reaction::getCountsForArticle error: " . $e->getMessage());
308            return [];
309        }
310    }
311}