多条件筛选需“跳过空值+显式分组+安全拼接”三步闭环:先trim()+!empty()过滤空参,数值用filter_var校验,字符串交由Query Builder自动转义;OR条件须group_start()/group_end()显式括起;分页需启用page_query_strings并动态构造base_url,避免丢参。

多条件筛选在 CodeIgniter 中不是靠堆 where() 实现的,而是靠「跳过空条件 + 显式分组 + 安全拼接」三步闭环。直接链式调用 where() 且不校验参数,90% 的线上 SQL 错误都出在这一步。
怎么安全跳过空值或无效参数
用户没填的字段(比如价格区间留空、关键词为空字符串),不能让 where('price >=', '') 进入查询——MySQL 会把它当 0 或隐式转成空字符串,导致全表扫描甚至逻辑错乱。
- 所有输入必须先走
trim()+!empty()判断,$kw = trim($this->input->get('kw'));后立刻if (!empty($kw)) { $this->db->like('title', $kw); } - 数值型字段(如
cat_id、status)必须用filter_var($val, FILTER_VALIDATE_INT)或is_numeric()校验,避免传入'1 OR 1=1'这类注入片段 - 字符串字段别手动拼 SQL,交给 Query Builder:用
$this->db->escape_str($val)或直接传给where()(它默认开启自动转义)
如何写对 OR 条件,尤其是跨字段搜索
or_like() 看似方便,但它的默认行为是把所有后续 or_* 都塞进同一个 OR 组,容易和已有 AND 条件冲突。比如你本想查「(标题含关键词 OR 描述含关键词) AND 分类=2」,结果生成的是「标题含关键词 OR 描述含关键词 AND 分类=2」,优先级错乱。
- 必须用
group_start()/group_end()显式括起 OR 部分:$this->db->group_start()->like('title', $kw)->or_like('description', $kw)->group_end() - 多个关键词(如“手机 5G”)要拆开处理:先
explode(' ', $kw),再对每个词循环执行group_start()→like→or_like→group_end() - 前缀匹配(如品牌名“Apple”开头)显式传第三个参数:
like('brand', 'Apple', 'before'),否则默认是前后模糊%Apple%
分页时怎么保留所有筛选参数
CodeIgniter 默认分页只认 URI 段(如 /search/2),但筛选参数基本都在 query string(?cat=2&kw=phone)里。不改配置,一翻页就丢条件。
- 必须启用
$config['page_query_strings'] = TRUE;,让分页类识别per_page这类参数 -
$config['base_url']不能写死路径,得动态拼:base_url('search') . '?' . http_build_query(array_diff($_GET, ['per_page'])) - 控制器开头加
$_GET = array_map('trim', $_GET);,否则空格会导致http_build_query()生成带%20的 URL,前端可能解析失败 - 前端表单
method必须为get,post提交后分页链接无法继承参数
性能敏感点:count_all_results() 和 CONCAT 模糊查询
带多条件的分页必须查总数,但 count_all_results() 在复杂 WHERE 下会重跑一遍完整查询,比 get()->num_rows() 更慢;而用 CONCAT(title, ' ', description) LIKE '%xxx%' 这种写法,MySQL 基本无法走索引。
- 高频搜索字段(如 title、content)建议建全文索引,改用
match(title, description) against('xxx' in natural language mode) - 避免在
where()里写原生 CONCAT 表达式,除非数据量小且能接受全表扫描 - 如果总条数允许近似,可考虑缓存 count 结果,或用
SQL_CALC_FOUND_ROWS(需关闭count_all_results()并手动SELECT FOUND_ROWS())
最常被忽略的是空值校验顺序和分页 URL 构造逻辑——这两个点一旦漏掉,要么查出错数据,要么用户点第二页就回到全部结果。别图省事跳过 !empty() 或硬编码 base_url。


















