PHP项目列表筛选查询如何组合条件

wen PHP项目 30

本文目录导读:

PHP项目列表筛选查询如何组合条件

  1. 基础思路(手动拼接)
  2. 使用数组构建(更清晰)
  3. 封装成函数/类(可复用)
  4. 处理常见特殊条件
  5. 安全建议
  6. 使用框架(推荐)

在PHP项目中实现组合条件筛选查询,常见的方式是根据用户选择的条件动态构建SQL的WHERE子句,下面给出几种典型的实现思路和代码示例。


基础思路(手动拼接)

核心步骤

  1. 接收前端传来的筛选参数(如 $_GET$_POST)。
  2. 遍历参数,判断哪个条件被选中。
  3. 动态构建 WHERE 1=1 + AND ... 形式。
  4. 使用预处理语句(防止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 子句并用预处理绑定参数

抱歉,评论功能暂时关闭!