本文目录导读:

实现PHP项目的商品评价功能,通常需要包含数据库设计、后端接口、前端展示以及安全校验四个核心部分。
下面是一个基于 MySQL + PHP 的经典实现方案,适用于大多数电商项目。
第一步:数据库设计
你需要至少两张表:orders(订单)和 reviews(评价)。
订单商品关联表(用于验证购买资格)
CREATE TABLE `order_items` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_id` int(11) NOT NULL, `product_id` int(11) NOT NULL, `user_id` int(11) NOT NULL, `is_reviewed` tinyint(1) DEFAULT 0, -- 0未评价,1已评价 PRIMARY KEY (`id`) );
评价主表
CREATE TABLE `reviews` ( `id` int(11) NOT NULL AUTO_INCREMENT, `product_id` int(11) NOT NULL, `user_id` int(11) NOT NULL, `order_id` int(11) NOT NULL, `rating` tinyint(1) NOT NULL, -- 评分(1-5星) `content` text, -- 评价内容 `images` json DEFAULT NULL, -- 评价图片(JSON数组) `status` tinyint(1) DEFAULT 1, -- 1显示,0隐藏(审核) `created_at` datetime DEFAULT CURRENT_TIMESTAMP, `updated_at` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `unique_user_product_order` (`user_id`,`product_id`,`order_id`), -- 防止重复评价 INDEX `idx_product_rating` (`product_id`, `rating`) );
第二步:后端PHP接口(核心逻辑)
提交评价(需要登录 + 购买验证)
<?php
// submit_review.php
session_start();
require_once 'db.php'; // 数据库连接
// 1. 验证用户登录
if (!isset($_SESSION['user_id'])) {
echo json_encode(['code' => 401, 'msg' => '请先登录']);
exit;
}
$user_id = $_SESSION['user_id'];
$product_id = intval($_POST['product_id']);
$order_id = intval($_POST['order_id']);
$rating = intval($_POST['rating']);
$content = trim($_POST['content']);
// 2. 参数校验
if ($rating < 1 || $rating > 5) {
echo json_encode(['code' => 400, 'msg' => '评分范围1-5']);
exit;
}
if (mb_strlen($content) < 10 || mb_strlen($content) > 500) {
echo json_encode(['code' => 400, 'msg' => '评价内容10-500字']);
exit;
}
// 3. 验证购买资格(检查订单商品表)
$stmt = $pdo->prepare("SELECT id FROM order_items
WHERE user_id = ? AND product_id = ? AND order_id = ? AND is_reviewed = 0");
$stmt->execute([$user_id, $product_id, $order_id]);
if (!$stmt->fetch()) {
echo json_encode(['code' => 403, 'msg' => '未购买该商品或已评价']);
exit;
}
// 4. 处理图片上传(可选)
$images = [];
if (!empty($_FILES['images'])) {
$allowed = ['image/jpeg', 'image/png', 'image/webp'];
foreach ($_FILES['images']['tmp_name'] as $key => $tmp_name) {
if ($_FILES['images']['error'][$key] === 0) {
if (!in_array($_FILES['images']['type'][$key], $allowed)) {
continue;
}
$filename = uniqid() . '.' . pathinfo($_FILES['images']['name'][$key], PATHINFO_EXTENSION);
move_uploaded_file($tmp_name, 'uploads/reviews/' . $filename);
$images[] = $filename;
}
}
}
// 5. 开启事务,写入评价 + 更新订单状态
$pdo->beginTransaction();
try {
// 写入评价
$stmt = $pdo->prepare("INSERT INTO reviews (product_id, user_id, order_id, rating, content, images)
VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$product_id, $user_id, $order_id, $rating, $content, json_encode($images)]);
// 更新订单商品状态为已评价
$stmt = $pdo->prepare("UPDATE order_items SET is_reviewed = 1
WHERE user_id = ? AND product_id = ? AND order_id = ?");
$stmt->execute([$user_id, $product_id, $order_id]);
$pdo->commit();
echo json_encode(['code' => 200, 'msg' => '评价成功']);
} catch (Exception $e) {
$pdo->rollBack();
echo json_encode(['code' => 500, 'msg' => '评价提交失败']);
}
获取商品评价列表
<?php
// get_reviews.php
$product_id = intval($_GET['product_id']);
$page = intval($_GET['page'] ?? 1);
$page_size = 10;
$offset = ($page - 1) * $page_size;
$stmt = $pdo->prepare("SELECT r.*, u.username, u.avatar
FROM reviews r
JOIN users u ON r.user_id = u.id
WHERE r.product_id = ? AND r.status = 1
ORDER BY r.created_at DESC
LIMIT $offset, $page_size");
$stmt->execute([$product_id]);
$reviews = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 统计评分分布
$stmt_rating = $pdo->prepare("SELECT rating, COUNT(*) as count
FROM reviews
WHERE product_id = ? AND status = 1
GROUP BY rating");
$stmt_rating->execute([$product_id]);
$rating_stats = $stmt_rating->fetchAll(PDO::FETCH_KEY_PAIR);
echo json_encode([
'list' => $reviews,
'rating_stats' => $rating_stats,
'total' => count($reviews)
]);
第三步:前端HTML + JavaScript(示例)
<!-- 评价表单 -->
<form id="reviewForm" enctype="multipart/form-data">
<input type="hidden" name="product_id" value="123">
<input type="hidden" name="order_id" value="456">
<div class="star-rating">
<span data-value="1">★</span>
<span data-value="2">★</span>
<span data-value="3">★</span>
<span data-value="4">★</span>
<span data-value="5">★</span>
<input type="hidden" name="rating" id="ratingInput">
</div>
<textarea name="content" placeholder="分享您的使用感受(10-500字)"></textarea>
<input type="file" name="images[]" multiple accept="image/*">
<button type="submit">提交评价</button>
</form>
<script>
document.getElementById('reviewForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch('submit_review.php', {
method: 'POST',
body: formData
})
.then(res => res.json())
.then(data => {
if (data.code === 200) {
alert('评价成功');
location.reload();
} else {
alert(data.msg);
}
});
});
// 星星选择逻辑(简化)
document.querySelectorAll('.star-rating span').forEach(span => {
span.addEventListener('click', function() {
document.getElementById('ratingInput').value = this.dataset.value;
});
});
</script>
第四步:扩展功能与注意事项
-
评价审核
- 新增
reviews.status字段(0=待审核,1=通过,2=拒绝) - 后台管理员审核后更新状态
- 新增
-
回复评价
- 新增
review_replies表,关联reviews.id - 商家回复显示在评价下方
- 新增
-
追评功能
- 允许用户在一定时间内追加评价
- 在
reviews表中增加additional_content和additional_time字段
-
性能优化
- 为
product_id和created_at建立复合索引 - 商品详情页缓存评价总数和平均分(Redis 或文件缓存)
- 为
-
安全注意事项
- 使用
htmlspecialchars()防止XSS攻击 - 对图片进行压缩和重命名,防止恶意文件上传
- 限制同一用户对一个商品的评价次数(通过唯一索引实现)
- 使用
-
SEO优化
- 建议使用
<div>包裹,不要直接输出用户输入内容 - 评价列表建议使用服务端渲染或预渲染
- 建议使用
如果使用框架(ThinkPHP / Laravel)
ThinkPHP 6 示例:
// 评价提交
public function submit(Request $request)
{
$data = $request->validate([
'product_id' => 'required|integer',
'order_id' => 'required|integer',
'rating' => 'required|integer|between:1,5',
'content' => 'required|length:10,500',
]);
// 使用事务
Db::transaction(function () use ($data) {
// 写入评价
Review::create($data);
// 更新订单状态
OrderItem::where(...)->update(['is_reviewed' => 1]);
});
}
Laravel 示例:
// 使用 Eloquent ORM + 事件
public function store(ReviewRequest $request)
{
$review = auth()->user()->reviews()->create($request->validated());
// 触发事件(更新商品评分缓存)
event(new ProductReviewed($review));
}
评价功能的核心逻辑:验证身份 → 校验购买资格 → 写入评价 → 更新订单状态,实际项目中建议根据上述结构逐步完善,注意防刷(唯一索引)、审核(状态字段)和性能(缓存与索引)三个关键点。