本文目录导读:

在 PHP 项目中实现商品评价筛选,核心是根据用户选择的条件(如评分、时间、内容关键词、是否有图/追评等)动态构建 SQL 查询。
以下是一个从简单到复杂的实现方案,包含前后端交互的完整逻辑。
核心思路:动态构建 SQL 查询
用户在前端选择筛选条件后将参数发送到后端,后端根据接收到的参数拼接 WHERE 子句。
典型的筛选条件:
- 评分范围 (1-5星)
- 评价时间 (最近一周/月/半年)
- 评价类型 (全部 / 有图 / 无图 / 有追评)
- 排序方式 (最新 / 最有帮助 / 评分最高/最低)
数据库设计示例
假设你有一个 reviews 表:
CREATE TABLE `reviews` ( `id` int(11) NOT NULL AUTO_INCREMENT, `product_id` int(11) NOT NULL COMMENT '商品ID', `user_id` int(11) NOT NULL, `rating` tinyint(1) NOT NULL COMMENT '评分 1-5', `content` text COMMENT '评价内容', `images` varchar(255) DEFAULT NULL COMMENT '图片,逗号分隔', `created_at` datetime NOT NULL, `updated_at` datetime DEFAULT NULL COMMENT '追评时间', `helpful_count` int(11) DEFAULT 0 COMMENT '点赞数', PRIMARY KEY (`id`), KEY `product_id` (`product_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
后端 PHP 实现 (动态SQL拼装)
这是最关键的代码逻辑,假设你使用的是 PDO(推荐)或 MySQLi。
文件: api/get_reviews.php
<?php
// 假设已经建立了数据库连接 $db_connection (PDO)
$productId = intval($_GET['product_id'] ?? 0);
$rating = intval($_GET['rating'] ?? 0); // 0 表示全部
$hasImage = intval($_GET['has_image'] ?? 0); // 0:全部 1:有图 -1:无图
$hasAppend = intval($_GET['has_append'] ?? 0); // 0:全部 1:有追评
$orderBy = $_GET['order_by'] ?? 'latest'; // latest, highest, lowest, helpful
$keyword = trim($_GET['keyword'] ?? ''); // 内容搜索 (可选)
$page = max(1, intval($_GET['page'] ?? 1));
$pageSize = 10;
// ----- 1. 基础 WHERE 条件 -----
$where = "WHERE product_id = :product_id"; // 必须条件
$params = [':product_id' => $productId];
// 评分筛选
if ($rating >= 1 && $rating <= 5) {
$where .= " AND rating = :rating";
$params[':rating'] = $rating;
}
// 有图/无图筛选
if ($hasImage === 1) {
$where .= " AND images IS NOT NULL AND images != ''";
} elseif ($hasImage === -1) { // 注意:-1 表示无图
$where .= " AND (images IS NULL OR images = '')";
}
// 追评筛选
if ($hasAppend === 1) {
$where .= " AND updated_at IS NOT NULL"; // 假设追评会更新更新时间
}
// 关键词搜索 (防止SQL注入,使用LIKE)
if (!empty($keyword)) {
$where .= " AND content LIKE :keyword";
$params[':keyword'] = '%' . $keyword . '%';
}
// ----- 2. 排序 -----
switch ($orderBy) {
case 'highest':
$orderSQL = "ORDER BY rating DESC, created_at DESC";
break;
case 'lowest':
$orderSQL = "ORDER BY rating ASC, created_at DESC";
break;
case 'helpful':
$orderSQL = "ORDER BY helpful_count DESC, created_at DESC";
break;
case 'latest':
default:
$orderSQL = "ORDER BY created_at DESC";
break;
}
// ----- 3. 分页 -----
$offset = ($page - 1) * $pageSize;
$limitSQL = "LIMIT :limit OFFSET :offset";
$params[':limit'] = $pageSize;
$params[':offset'] = $offset;
// ----- 4. 执行查询 -----
$sql = "SELECT * FROM reviews {$where} {$orderSQL} {$limitSQL}";
$stmt = $db_connection->prepare($sql);
// 绑定参数 (注意分页参数类型)
foreach ($params as $key => $value) {
if ($key === ':limit' || $key === ':offset') {
$stmt->bindValue($key, $value, PDO::PARAM_INT);
} else {
$stmt->bindValue($key, $value);
}
}
$stmt->execute();
$reviews = $stmt->fetchAll(PDO::FETCH_ASSOC);
// ----- 5. 获取总数量 (用于分页) -----
$countSql = "SELECT COUNT(*) as total FROM reviews {$where}";
$countStmt = $db_connection->prepare($countSql);
foreach ($params as $key => $value) {
// 注意:count查询的SQL不包含limit参数,需要过滤掉
if ($key === ':limit' || $key === ':offset') {
continue;
}
$countStmt->bindValue($key, $value);
}
$countStmt->execute();
$total = $countStmt->fetch(PDO::FETCH_ASSOC)['total'];
// ----- 6. 返回JSON数据 -----
header('Content-Type: application/json');
echo json_encode([
'code' => 200,
'data' => $reviews,
'total' => $total,
'page' => $page,
'page_size' => $pageSize
]);
前端实现(HTML + JavaScript + AJAX)
前端负责捕获用户操作,发起异步请求并渲染结果。
文件: product_detail.php (简化版)
<!DOCTYPE html>
<html>
<head>商品评价</title>
</head>
<body>
<h1>商品详情</h1>
<!-- 筛选区 -->
<div id="filter-area">
<select id="rating-filter">
<option value="0">全部评分</option>
<option value="5">5星</option>
<option value="4">4星</option>
<option value="3">3星</option>
<option value="2">2星</option>
<option value="1">1星</option>
</select>
<select id="image-filter">
<option value="0">全部评价</option>
<option value="1">有图评价</option>
<option value="-1">无图评价</option>
</select>
<select id="order-filter">
<option value="latest">最新</option>
<option value="helpful">最有帮助</option>
</select>
<button id="apply-filter">筛选</button>
</div>
<!-- 评价列表 -->
<div id="review-list"></div>
<!-- 分页 -->
<div id="pagination"></div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
let currentPage = 1;
const productId = 123; // 从页面获取实际商品ID
function loadReviews(page) {
currentPage = page || 1;
// 收集筛选条件
const params = {
product_id: productId,
rating: $('#rating-filter').val(),
has_image: $('#image-filter').val(),
order_by: $('#order-filter').val(),
page: currentPage
};
$.get('/api/get_reviews.php', params, function(response) {
if (response.code === 200) {
renderReviews(response.data);
renderPagination(response.total, response.page, response.page_size);
}
}, 'json');
}
function renderReviews(reviews) {
let html = '';
reviews.forEach(function(review) {
html += `<div class="review-item">
<div>评分: ${'★'.repeat(review.rating)}${'☆'.repeat(5-review.rating)}</div>
<p>${review.content}</p>
<small>${review.created_at}</small>
</div>`;
});
$('#review-list').html(html);
}
// 绑定筛选按钮点击
$('#apply-filter').click(function() {
loadReviews(1); // 重新筛选时回到第一页
});
// 初始加载
loadReviews(1);
});
</script>
</body>
</html>
进阶优化与注意事项
- 避免 SQL 注入:
- 永远不要直接拼接用户输入到SQL中。
- 使用预处理语句(Prepared Statement)是标准做法。
- 使用全文索引(性能优化):
- 如果关键词搜索非常频繁,可以考虑对
content字段建立 MySQL 的 FULLTEXT 索引,然后使用MATCH...AGAINST替代LIKE(性能更好)。
- 如果关键词搜索非常频繁,可以考虑对
- 筛选统计(用户体验):
- 很多电商网站会显示“全部 500条”、“有图 30条”,可以用一个 SQL 在加载时一次性统计。
SELECT COUNT(*) as total, SUM(CASE WHEN images != '' THEN 1 ELSE 0 END) as has_images, SUM(CASE WHEN rating = 5 THEN 1 ELSE 0 END) as five_star FROM reviews WHERE product_id = ?
- 很多电商网站会显示“全部 500条”、“有图 30条”,可以用一个 SQL 在加载时一次性统计。
- 分页安全:
- 防止
page参数过大导致数据库压力或返回大量无效页码。
- 防止
- Redis 缓存(高并发场景):
对于同一个商品的评价筛选结果(尤其是评分、有图这类变化不频繁的筛选),可以缓存到 Redis 中,设置5分钟过期,可以有效降低数据库压力。
总结流程图
用户操作 (点击筛选/排序)
↓
前端JS(收集参数)
↓
AJAX请求 (GET /api/reviews)
↓
PHP接收参数
↓
安全验证 并 动态拼接SQL WHERE/ORDER BY/LIMIT
↓
PDO预处理 + 参数绑定
↓
执行SQL → 获取数据
↓
返回JSON (包含评价列表 + 总数)
↓
前端渲染HTML
这样,一个完整的、安全的、可扩展的商品评价筛选功能就实现了。