pdo->prepare( 'INSERT INTO article_ratings (article_uuid, user_email, rating) VALUES (:uuid, :email, :r) ON CONFLICT (article_uuid, user_email) DO UPDATE SET rating = :r, rated_at = NOW()' ); $st->execute([':uuid' => $uuid, ':email' => strtolower($email), ':r' => $rating]); } /** @return array{avg: float|null, count: int} */ public function statsForArticle(string $uuid): array { $st = $this->pdo->prepare( 'SELECT ROUND(AVG(rating)::numeric, 1) as avg, COUNT(*) as count FROM article_ratings WHERE article_uuid = :uuid' ); $st->execute([':uuid' => $uuid]); $row = $st->fetch(PDO::FETCH_ASSOC); return [ 'avg' => $row && $row['avg'] !== null ? (float)$row['avg'] : null, 'count' => $row ? (int)$row['count'] : 0, ]; } public function userRating(string $uuid, string $email): ?int { $st = $this->pdo->prepare( 'SELECT rating FROM article_ratings WHERE article_uuid = :uuid AND user_email = :email' ); $st->execute([':uuid' => $uuid, ':email' => strtolower($email)]); $v = $st->fetchColumn(); return $v !== false ? (int)$v : null; } }