本文目录导读:

在PHP项目中实现组合条件筛选查询,常见的方式是根据用户选择的条件动态构建SQL的WHERE子句,下面给出几种典型的实现思路和代码示例。
基础思路(手动拼接)
核心步骤
- 接收前端传来的筛选参数(如
$_GET或$_POST)。 - 遍历参数,判断哪个条件被选中。
- 动态构建
WHERE 1=1+AND ...形式。 - 使用预处理语句(防止SQL注入)。
示例代码(MySQLi + 预处理)
<?php
// 假设筛选条件:价格区间、分类、关键词、状态
$price_min = $_GET['price_min'] ?? '';
$price_max = $_GET['price_max'] ?? '';
$category = $_GET['category'] ?? '';
$keyword = $_GET['keyword'] ?? '';
$status = $_GET['status'] ?? '';
// 基础SQL
$sql = "SELECT * FROM products WHERE 1=1";
$params = [];
$types = '';
// 价格区间
if ($price_min !== '' && $price_max !== '') {
$sql .= " AND price BETWEEN ? AND ?";
$params[] = (float)$price_min;
$params[] = (float)$price_max;
$types .= 'dd';
} elseif ($price_min !== '') {
$sql .= " AND price >= ?";
$params[] = (float)$price_min;
$types .= 'd';
} elseif ($price_max !== '') {
$sql .= " AND price <= ?";
$params[] = (float)$price_max;
$types .= 'd';
}
// 分类(精确匹配)
if (!empty($category)) {
$sql .= " AND category_id = ?";
$params[] = (int)$category;
$types .= 'i';
}
// 关键词(模糊搜索)
if (!empty($keyword)) {
$sql .= " AND (name LIKE ? OR description LIKE ?)";
$likeKeyword = '%' . $keyword . '%';
$params[] = $likeKeyword;
$params[] = $likeKeyword;
$types .= 'ss';
}
// 状态(枚举值)
if (!empty($status)) {
$sql .= " AND status = ?";
$params[] = $status;
$types .= 's';
}
// 预处理执行
$stmt = $conn->prepare($sql);
if (!empty($params)) {
$stmt->bind_param($types, ...$params);
}
$stmt->execute();
$result = $stmt->get_result();
$products = $result->fetch_all(MYSQLI_ASSOC);
注意:
WHERE 1=1的作用是保证后续的AND语法正确,也可以先判断是否有条件再决定是否加WHERE。
使用数组构建(更清晰)
适合条件较多时,用数组存储条件片段,implode。
$conditions = [];
$params = [];
$types = '';
if (!empty($category)) {
$conditions[] = "category_id = ?";
$params[] = (int)$category;
$types .= 'i';
}
if (!empty($keyword)) {
$conditions[] = "(name LIKE ? OR description LIKE ?)";
$like = '%' . $keyword . '%';
$params[] = $like;
$params[] = $like;
$types .= 'ss';
}
$where = '';
if (!empty($conditions)) {
$where = ' WHERE ' . implode(' AND ', $conditions);
}
$sql = "SELECT * FROM products" . $where;
封装成函数/类(可复用)
class ProductFilter
{
private array $conditions = [];
private array $params = [];
private string $paramTypes = '';
public function addCondition(string $sqlFragment, string $types, ...$values): void
{
$this->conditions[] = $sqlFragment;
foreach ($values as $val) {
$this->params[] = $val;
}
$this->paramTypes .= $types;
}
public function getSQL(string $baseSQL): string
{
if (empty($this->conditions)) {
return $baseSQL;
}
return $baseSQL . ' WHERE ' . implode(' AND ', $this->conditions);
}
public function getParams(): array
{
return [$this->paramTypes, ...$this->params];
}
// 用法示例
public function buildFromRequest(array $request): void
{
if (!empty($request['category'])) {
$this->addCondition('category_id = ?', 'i', (int)$request['category']);
}
if (!empty($request['keyword'])) {
$like = '%' . $request['keyword'] . '%';
$this->addCondition('(name LIKE ? OR description LIKE ?)', 'ss', $like, $like);
}
// 其他条件...
}
}
// 使用
$filter = new ProductFilter();
$filter->buildFromRequest($_GET);
$sql = $filter->getSQL("SELECT * FROM products");
list($types, ...$params) = $filter->getParams();
$stmt = $conn->prepare($sql);
$stmt->bind_param($types, ...$params);
处理常见特殊条件
多选(如多个分类ID)
$category_ids = $_GET['category_ids'] ?? []; // 数组
if (!empty($category_ids)) {
// 生成占位符 ? count($category_ids) 个
$placeholders = implode(',', array_fill(0, count($category_ids), '?'));
$conditions[] = "category_id IN ($placeholders)";
foreach ($category_ids as $id) {
$params[] = (int)$id;
$types .= 'i';
}
}
日期范围
if (!empty($date_from) && !empty($date_to)) {
$conditions[] = "created_at BETWEEN ? AND ?";
$params[] = $date_from;
$params[] = $date_to;
$types .= 'ss';
}
排序(非筛选,但常结合使用)
$order = $_GET['order'] ?? 'id';
$sort = strtoupper($_GET['sort'] ?? 'DESC');
// 白名单校验
$allowedOrders = ['id', 'price', 'created_at'];
$allowedSorts = ['ASC', 'DESC'];
if (in_array($order, $allowedOrders) && in_array($sort, $allowedSorts)) {
$sql .= " ORDER BY $order $sort";
} else {
$sql .= " ORDER BY id DESC"; // 默认
}
安全建议
| 风险点 | 措施 |
|---|---|
| SQL注入 | 必须使用预处理语句(如 bind_param 或 PDO 的 prepare + execute) |
| 参数未过滤 | 类型转换(intval, floatval)、htmlspecialchars(输出时) |
| 排序字段注入 | 白名单校验,绝不直接拼接用户输入的字段名 |
| 模糊搜索性能 | 考虑 全文索引 或限制关键词长度(如 min(100, strlen($keyword))) |
使用框架(推荐)
如果项目使用 Laravel / ThinkPHP / Symfony 等,框架自带查询构建器更加安全简洁:
Laravel 示例
$products = Product::query()
->when($request->category, fn($q, $v) => $q->where('category_id', $v))
->when($request->keyword, fn($q, $v) => $q->where(function($q2) use ($v) {
$q2->where('name', 'like', "%$v%")
->orWhere('description', 'like', "%$v%");
}))
->when($request->price_min, fn($q, $v) => $q->where('price', '>=', $v))
->when($request->price_max, fn($q, $v) => $q->where('price', '<=', $v))
->orderBy($request->order ?? 'id', $request->sort ?? 'desc')
->paginate(15);
- 原生写法:手动构建SQL + 预处理参数。
- 封装写法:条件数组、函数/类封装。
- 框架写法:利用链式调用,最安全简洁。
无论哪种方式,核心都是根据用户输入动态拼接 WHERE 子句并用预处理绑定参数。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播