feat: Add complete reviews system with star ratings

 New Features:
- Reviews system with 1-5 star ratings
- User can add, edit, and delete their own reviews
- One review per product per user (DB constraint)
- Automatic average rating calculation
- Review count tracking
- Interactive star selection UI
- AJAX-powered review submission
- Responsive design for all devices

🗄️ Database:
- New 'reviews' table with full structure
- Added 'rating' and 'review_count' fields to products
- PostgreSQL triggers for automatic rating updates
- Database functions for rating calculations
- Indexes for performance optimization

📦 Backend (PHP):
- Review model with 15+ methods
- ReviewController with 5 actions
- Updated Product model to include ratings
- Updated ProductController to load reviews
- 5 new API endpoints

🎨 Frontend:
- Reviews list component (_reviews_list.php)
- Review form component (_review_form.php)
- Reviews sechow page
- Star ratings in catalog view
- Interactive JavaScript (200+ lines)
- Adaptive styles (400+ lines)

🔒 Security:
- Server-side authorization checks
- XSS protection (htmlspecialchars)
- SQL injection protection (PDO prepared)
- Input validation (client + server)
- Access control for review editing

📝 Modified Files:
- app/Models/Product.php - added rating fields to queries
- app/Controllers/ProductController.php - loads reviews
- app/Views/products/show.php - reviews section
- app/Views/products/catalog.php - star ratings
- config/routes.php - review endpoints
- public/style_for_cite.less - rating styles

🆕 New Files:
- app/Models/Review.php
- app/Controllers/ReviewController.php
- app/Views/products/_reviews_list.php
- app/Views/products/_review_form.php
This commit is contained in:
kirill.khorkov
2026-01-06 17:04:09 +03:00
parent 547c561ed0
commit a4092adf2e
17 changed files with 1646 additions and 59 deletions

View File

@@ -11,7 +11,9 @@ class Product extends Model
public function findWithCategory(int $id): ?array
{
$sql = "SELECT p.*, c.name as category_name, c.slug as category_slug
$sql = "SELECT p.*, c.name as category_name, c.slug as category_slug,
COALESCE(p.rating, 0) as rating,
COALESCE(p.review_count, 0) as review_count
FROM {$this->table} p
LEFT JOIN categories c ON p.category_id = c.category_id
WHERE p.product_id = ?";
@@ -20,7 +22,9 @@ class Product extends Model
public function getAvailable(array $filters = [], int $limit = 50): array
{
$sql = "SELECT p.*, c.name as category_name
$sql = "SELECT p.*, c.name as category_name,
COALESCE(p.rating, 0) as rating,
COALESCE(p.review_count, 0) as review_count
FROM {$this->table} p
LEFT JOIN categories c ON p.category_id = c.category_id
WHERE p.is_available = TRUE";
@@ -67,7 +71,9 @@ class Product extends Model
public function getAllForAdmin(bool $showAll = true): array
{
$sql = "SELECT p.*, c.name as category_name
$sql = "SELECT p.*, c.name as category_name,
COALESCE(p.rating, 0) as rating,
COALESCE(p.review_count, 0) as review_count
FROM {$this->table} p
LEFT JOIN categories c ON p.category_id = c.category_id";
@@ -82,7 +88,10 @@ class Product extends Model
public function getSimilar(int $productId, int $categoryId, int $limit = 3): array
{
$sql = "SELECT * FROM {$this->table}
$sql = "SELECT *,
COALESCE(rating, 0) as rating,
COALESCE(review_count, 0) as review_count
FROM {$this->table}
WHERE category_id = ?
AND product_id != ?
AND is_available = TRUE