安全拼接多条件需先校验参数:用trim()+!empty()过滤空值,数值型字段用is_numeric()或filter_var()校验,字符串交由query builder自动转义,避免手动拼接sql。

CodeIgniter 实现多条件组合筛选,关键不在“堆条件”,而在于让 where() 调用可累积、可跳过、不报错——尤其当某些筛选项为空或未提交时。
如何安全拼接多个 where() 条件
直接链式调用 where() 很容易因空值导致 SQL 错误(比如 WHERE name = '' 匹配全表),或因类型不一致引发隐式转换问题。必须对每个参数做显式校验和过滤。
- 用
$this->input->get()或$this->input->post()获取参数后,立即用trim()+!empty()判断是否参与查询 - 数值型字段(如价格区间、分类 ID)务必用
is_numeric()或filter_var($val, FILTER_VALIDATE_INT)校验,避免注入或意外匹配 - 字符串字段建议统一用
escape_str()或交给 Query Builder 自动处理(启用escape()参数) - 不要写
$this->db->where('price >=', $min_price)->where('price 这类裸比较,改用 <code>where_between()更安全
like() 和 or_like() 在多关键词搜索中的坑
电商搜索常需支持“标题或描述含关键词”,但 or_like() 默认会把所有条件塞进一个 OR 组,容易和其它 AND 条件逻辑冲突,生成意外的 SQL。
- 正确做法是手动分组:
$this->db->group_start()->like('title', $kw)->or_like('description', $kw)->group_end() - 如果同时有多个关键词(如“手机 5G”),别直接
like('title', '手机 5G'),应拆成两个like()并用and_group_start()套住 -
like()默认是前后模糊(%kw%),若只需前缀匹配(如品牌名“Apple”开头),显式传第三个参数'before'或'after' - 注意 MySQL 的
LIKE不走索引的问题,高频字段建议加全文索引 +match() against()替代
分页与多条件筛选共存时的 URL 构造
CodeIgniter 默认分页类(pagination)只认 URI 段,但多条件筛选参数基本都走 query string(?cat=2&price_min=100&kw=phone),不改造就无法翻页保留条件。
- 禁用
uri_segment,改用base_url('search').'?'.http_build_query($_GET)生成分页链接 - 分页初始化时设置
suffix为'?'.http_build_query(array_diff($_GET, ['per_page'])),排除分页参数本身 - 务必在控制器开头用
$_GET = array_map('trim', $_GET)清理空格,否则http_build_query()可能生成带空格的 URL - 前端表单 method 必须为
get,否则分页链接无法继承筛选状态
性能敏感点:count_all_results() 不能直接套在复杂 where 后
分页需要总数,但直接在带多个 like()、join()、group_by() 的查询后调 count_all_results(),会强制执行完整 SELECT,极慢。
- 先用
$this->db->select('COUNT(*) as cnt')->from(...)->where(...)->get()->row()->cnt手动 COUNT,避免字段膨胀 - 如果有 JOIN,确认是否真需要关联表数据再 COUNT;否则先查主表 ID 列表,再用
where_in()查详情 - 对高并发搜索,考虑加缓存层(如 Redis)缓存常用组合的总数,过期时间设为 30–60 秒即可
- 别忽略 MySQL 的
SQL_CALC_FOUND_ROWS,CodeIgniter 不原生支持,需手写query()配合found_rows(),但要注意并发下不准
多条件筛选最麻烦的不是写法,而是每个字段的语义边界——比如“库存 > 0”要不要默认加上、“是否上架”字段要不要强制过滤、“搜索词长度小于 2 时是否拒绝”——这些业务规则必须在控制器入口就收口,不能甩给模型或数据库兜底。











