小项目用whereLike最快但易漏匹配,whereFullText支持自然语言搜索需建FULLTEXT索引且MySQL 5.6+;中文需ngram分词;参数须用占位符防SQL注入;联合搜索用闭包包裹;分页总数不准时优先simplePaginate;搜索接口应禁用appends和事件降低开销。

用 whereLike 还是 whereFullText?看数据量和搜索精度
小项目、关键词简单、字段少,直接用 whereLike 最快;但模糊匹配容易漏结果,比如搜 “admin” 匹配不到 “administrator”。whereFullText(配合 MySQL 的 FULLTEXT 索引)能支持自然语言搜索、词干匹配、相关性排序,但要求字段类型是 TEXT 或 VARCHAR,且必须提前建好索引,否则报错 Can't find FULLTEXT index。
- MySQL 5.6+ 才支持 InnoDB 的全文索引,老版本只能用 MyISAM(不推荐)
- Laravel 9+ 的
whereFullText默认走布尔模式,搜 “laravel api” 会拆成两个词,中间有空格就可能不命中——加引号强制短语匹配:whereFullText('title', '"laravel api"') - 中文需额外配置 ngram 分词器,否则全文索引基本无效;没条件的话,老实用
whereRaw+CONCAT('%', ?, '%')
搜索参数怎么安全传进 Query Builder?别拼 SQL 字符串
用户输入直接进 whereRaw 是典型 SQL 注入口。Laravel 提供的占位符绑定机制必须用上,哪怕多写两行。
- 错误写法:
whereRaw("title LIKE '%{$request->q}%'")—— $request->q 是用户可控输入,危险 - 正确写法:
where('title', 'like', '%' . $request->q . '%')或whereRaw('title LIKE ?', ['%' . $request->q . '%']) - 多个字段联合搜索时,别堆一堆
orWhere:它会破坏主查询的 AND 逻辑,要用where(function ($q) { ... })包一层
分页 + 搜索结果总数不准?withCount 和 paginate 别混用
用 paginate() 时 Laravel 默认执行两条 SQL:一条查数据,一条查总数(COUNT(*))。但如果用了 groupBy、distinct 或复杂 JOIN,COUNT 可能出错,返回 0 或远大于实际页数。
- 确认是否真需要精确总数:很多 API 其实只显示“下一页”,用
simplePaginate()更轻量,只查是否有下一页 - 如果必须总数,且用了
selectRaw或聚合函数,手动传total参数给LengthAwarePaginator,别依赖自动 COUNT -
withCount()是查关联数量的,跟搜索分页总数无关,混用会导致 N+1 或逻辑错乱
API 搜索响应慢?先关掉 Eloquent 的 appends 和模型事件
搜索接口常被高频调用,但很多人忘了模型里定义的 $appends 属性或 booted 里的监听器,会在每条结果上触发额外计算或查询。
- 检查模型中是否有
getXXXAttribute方法或$appends = ['full_name'],搜索时这些字段大概率用不上,临时用makeHidden屏蔽 - 全局事件监听(如
creating)对只读搜索无意义,可在搜索上下文里用Model::unsetEventDispatcher()临时关闭 - 数据库层面加
EXPLAIN看执行计划:如果 type 是ALL,说明没走索引;key列为空也意味着索引失效
搜索不是加个 like 就完事,真正卡住的往往是索引缺失、字符集不匹配、或模型层的隐式开销。上线前至少跑一次真实数据量的 EXPLAIN,别只在本地测 10 条记录。










