Yii框架不内置智能搜索,需用CASE WHEN手动构造权重排序或引入Sphinx/Elasticsearch扩展;LIKE仅支持精确字符匹配,无法实现关键词靠前权重高、词干模糊匹配等需求。

Yii 框架本身不内置“智能搜索”或“模糊关键词匹配排序”,必须手动构造 SQL 表达式(如 CASE WHEN)或借助扩展(如 yii2-sphinx、yii2-elasticsearch)。直接用 like 查只能做前缀/包含匹配,无法实现“关键词越靠前、匹配权重越高”的业务排序需求。
用 CASE WHEN 实现关键词优先级排序
这是最轻量、无需额外服务的方案,适合中小项目。核心是把搜索词数组转为 SQL 中的条件分支,并按顺序赋予权重值。
- 每个关键词对应一个
WHEN `name` LIKE '%关键词%' THEN 1分支,数字越小权重越高 -
ORDER BY必须传入字符串表达式(如"case when ... end"),不能用数组语法;Yii 2.0+ 不支持直接在orderBy()里写原始 SQL 字段别名 - 注意 SQL 注入风险:关键词必须经
addslashes()或使用bindValue()绑定参数,示例中直接拼接仅适用于可信内部词表(如固定配置的$search_arr) - 若关键词含单引号(如
O'Reilly),不转义会导致 SQL 报错SQLSTATE[42000]: Syntax error or access violation
foreach ($search_arr as $k2 => $v2) {
$kk = $k2 + 1;
$escaped = addslashes($v2); // 关键防御点
if ($k2 === 0) {
$order_by = "CASE WHEN `g`.`name` LIKE '%{$escaped}%' THEN {$kk}";
} else {
$order_by .= " WHEN `g`.`name` LIKE '%{$escaped}%' THEN {$kk}";
}
}
$order_by .= " ELSE 999 END"; // 未匹配项垫底
$query->orderBy([$order_by => SORT_ASC]);
解决“输入 tools 却匹配不到 tool”的模糊匹配问题
单纯 LIKE '%tools%' 不会命中 tool,因为数据库是精确字符匹配。要支持词干或子串泛化,得主动降维:
- 用 MySQL 内置函数
INSTR()或LOCATE()替代LIKE,例如INSTR(`name`, 'tool') > 0,它对tools和tool都返回非零 - 更稳妥的是预处理关键词:对用户输入做切词(如用空格、标点分割),再对每个词干截取前缀(如
substr($word, 0, 4)),生成多个LIKE 'tool%'条件 - 避免全表扫描:给
name字段加前缀索引(如INDEX(name(50))),否则LIKE '%xxx%'必走全表 - 不要依赖 PHP 层做字符串遍历匹配——数据量一过万,响应时间就不可控
为什么不用 find()->where(['like', 'name', $keyword]) 直接查?
这个写法默认等价于 LIKE '%keyword%',看似简单,但有三个硬伤:
- 无法表达多关键词权重:所有匹配结果都一样排序,做不到“沙发”排在“床”前面
- 不支持部分匹配逻辑:搜
sofa查不出sofas,除非你手动加通配符('%sofa%'),但又会误召awesome - 性能差且难优化:MySQL 对
%xxx%索引失效,即使加了全文索引,MyISAM 的MATCH AGAINST也不支持中文分词,InnoDB 全文索引要求 MySQL ≥ 5.6 且需配置ngram_token_size
真正麻烦的地方不在写法,而在权衡:要不要引入 Sphinx/Elasticsearch?如果只是后台管理搜商品名,CASE WHEN 足够;如果面向终端用户且需拼音、错别字、同义词,就得换引擎——而 Yii 本身只管怎么发请求,不管你怎么建倒排索引。


















