getByUser($data['user_id'], $data['product_id']); if ($existing) { return null; // User already reviewed this product } return $this->create([ 'product_id' => $data['product_id'], 'user_id' => $data['user_id'], 'rating' => $data['rating'], 'comment' => $data['comment'] ?? null, 'is_approved' => $data['is_approved'] ?? true ]); } /** * Get all reviews for a product */ public function getByProduct(int $productId, bool $approvedOnly = true): array { $sql = "SELECT r.*, u.full_name, u.email FROM {$this->table} r INNER JOIN users u ON r.user_id = u.user_id"; if ($approvedOnly) { $sql .= " WHERE r.product_id = ? AND r.is_approved = TRUE"; } else { $sql .= " WHERE r.product_id = ?"; } $sql .= " ORDER BY r.created_at DESC"; return $this->query($sql, [$productId]); } /** * Get review by specific user for a product */ public function getByUser(int $userId, int $productId): ?array { $sql = "SELECT * FROM {$this->table} WHERE user_id = ? AND product_id = ?"; return $this->queryOne($sql, [$userId, $productId]); } /** * Get review with user details */ public function findWithUser(int $reviewId): ?array { $sql = "SELECT r.*, u.full_name, u.email FROM {$this->table} r INNER JOIN users u ON r.user_id = u.user_id WHERE r.review_id = ?"; return $this->queryOne($sql, [$reviewId]); } /** * Update a review */ public function updateReview(int $reviewId, array $data): bool { $updateData = []; if (isset($data['rating'])) { $updateData['rating'] = $data['rating']; } if (isset($data['comment'])) { $updateData['comment'] = $data['comment']; } if (isset($data['is_approved'])) { $updateData['is_approved'] = $data['is_approved']; } if (empty($updateData)) { return false; } return $this->update($reviewId, $updateData); } /** * Delete a review */ public function deleteReview(int $reviewId): bool { return $this->delete($reviewId); } /** * Get average rating for a product */ public function getAverageRating(int $productId): float { $sql = "SELECT COALESCE(AVG(rating), 0.00) as avg_rating FROM {$this->table} WHERE product_id = ? AND is_approved = TRUE"; $result = $this->queryOne($sql, [$productId]); return round((float)$result['avg_rating'], 2); } /** * Get review count for a product */ public function getReviewCount(int $productId): int { return $this->count([ 'product_id' => $productId, 'is_approved' => true ]); } /** * Update product's rating and review count * This is called automatically by database triggers, * but can also be called manually if needed */ public function updateProductRating(int $productId): void { // Call the PostgreSQL function $sql = "SELECT update_product_rating(?)"; $this->execute($sql, [$productId]); } /** * Get rating distribution for a product (useful for charts) */ public function getRatingDistribution(int $productId): array { $sql = "SELECT rating, COUNT(*) as count FROM {$this->table} WHERE product_id = ? AND is_approved = TRUE GROUP BY rating ORDER BY rating DESC"; $results = $this->query($sql, [$productId]); // Initialize all ratings with 0 $distribution = [ 5 => 0, 4 => 0, 3 => 0, 2 => 0, 1 => 0 ]; // Fill in actual counts foreach ($results as $row) { $distribution[$row['rating']] = (int)$row['count']; } return $distribution; } /** * Get recent reviews across all products (for admin dashboard) */ public function getRecent(int $limit = 10, bool $approvedOnly = false): array { $sql = "SELECT r.*, u.full_name, u.email, p.name as product_name FROM {$this->table} r INNER JOIN users u ON r.user_id = u.user_id INNER JOIN products p ON r.product_id = p.product_id"; if ($approvedOnly) { $sql .= " WHERE r.is_approved = TRUE"; } $sql .= " ORDER BY r.created_at DESC LIMIT ?"; return $this->query($sql, [$limit]); } /** * Check if user can review (has purchased the product) * This is optional - you might want to allow reviews only from buyers */ public function userCanReview(int $userId, int $productId): bool { // Check if user already reviewed $existing = $this->getByUser($userId, $productId); if ($existing) { return false; } // Optional: Check if user has purchased this product // For now, we'll allow any authenticated user to review return true; /* Uncomment this to require purchase before review: $sql = "SELECT COUNT(*) as count FROM order_items oi INNER JOIN orders o ON oi.order_id = o.order_id WHERE o.user_id = ? AND oi.product_id = ? AND o.status = 'completed'"; $result = $this->queryOne($sql, [$userId, $productId]); return $result && $result['count'] > 0; */ } /** * Toggle review approval status (admin function) */ public function toggleApproval(int $reviewId): bool { $review = $this->find($reviewId); if (!$review) { return false; } return $this->update($reviewId, [ 'is_approved' => !$review['is_approved'] ]); } /** * Get all reviews by a specific user */ public function getUserReviews(int $userId, int $limit = 50): array { $sql = "SELECT r.*, p.name as product_name, p.image_url as product_image FROM {$this->table} r INNER JOIN products p ON r.product_id = p.product_id WHERE r.user_id = ? ORDER BY r.created_at DESC LIMIT ?"; return $this->query($sql, [$userId, $limit]); } }