Eloquent 模糊搜索应使用 where('field', 'like', "%{$keyword}%") 安全绑定,禁用 whereRaw 拼接;多条件搜索推荐 when() 链式调用避免嵌套。
where() 和 like 做基础模糊搜索直接在 Eloquent 查询中拼接 LIKE 是最常见写法,但要注意 SQL 注入风险。Laravel 提供了安全的参数绑定方式,别手写 "%{$keyword}%" 拼字符串。
比如搜索用户昵称包含 “admin”:
use App\Models\User;
$users = User::where('nickname', 'like', '%admin%')->get();
如果关键词来自请求,必须用变量绑定:
$keyword = $request->input('q', '');
$users = User::where('nickname', 'like', "%{$keyword}%")->get();
whereRaw("nickname LIKE '%{$keyword}%'") —— 无过滤时极易被注入like 操作符 + 双引号包裹带百分号的变量,Laravel 自动转义ilike(PostgreSQL)或 LOWER() 函数when() 避免 if 判断嵌套当搜索表单有「用户名」「邮箱」「状态」「创建时间范围」等多个可选字段时,硬写一堆 if 会让查询构建逻辑混乱且难维护。when() 是 Laravel 专为这种场景设计的链式条件方法。
$users = User::when($request->filled('name'), function ($query) use ($request) {
return $query->where('name', 'like', "%{$request->name}%");
})
->when($request->filled('email'), function ($query) use ($request) {
return $query->where('email', 'like', "%{$request->email}%");
})
->when($request->filled('status'), function ($query) use ($request) {
return $query->where('status', $request->status);
})
->when($request->filled('start_date'), function ($query) use ($request) {
return $query->whereDate('created_at', '>=', $request->start_date);
})
->when($request->filled('end_date'), function ($query) use ($request) {
return $query->whereDate('created_at', '<=', $request->end_date);
})
->paginate(15);
filled() 比 has() 更稳妥:它排除空字符串、null、0 等“假值”,避免意外匹配whereDate() 而非 where(),否则可能因时间部分不匹配漏数据where 和 where(..., 'like', ...) 完全没问题,when() 只是控制是否执行那段逻辑FULLTEXT 或 PostgreSQL tsvector
当数据量上万、模糊查询变慢,或需要支持“相关度排序”“同义词”“中文分词”时,纯 LIKE 就力不从心了。Laravel 本身不封装全文索引,但可调用底层能力。
MySQL 示例(需提前建 FULLTEXT 索引):
ALTER TABLE users ADD FULLTEXT(name, email);
然后在查询中用:
$keyword = $request->q;
$users = User::whereRaw("MATCH(name, email) AGAINST(? IN NATURAL LANGUAGE MODE)", [$keyword])
->orderByRaw("MATCH(name, email) AGAINST(?) DESC", [$keyword])
->get();
中文按字切分效果差,需配合 ngram 插件或改用 Elasticsearchto_tsvector + to_tsquery 对中文更友好(配合 zhparser 扩展)用 paginate() 后点击第2页,URL 里搜索关键词丢了——这是新手最常踩的坑。Laravel 的 appends() 就是干这个的。
$users = User::when(...)->paginate(15);
// 在 Blade 中渲染分页时:
{{ $users->appends(request()->query())->links() }}
更稳妥的做法是只追加关键搜索字段,避免把无关参数(如 _token)也带上:
{{ $users->appends(request()->only(['q', 'status', 'start_date', 'end_date']))->links() }}
withQueryString()(Laravel 9+ 新增),它会无差别携带所有 query,可能暴露敏感参数only() 显式声明要保留的键,清晰可控request()->only(...) 获取的是最终实际用于搜索的值