必须显式声明参数白名单,优先用$request->only()过滤;数字参数强制类型转换;where()需配合filled()判断空值;路由参数须加正则约束;数据库过滤优先于集合过滤。

只允许特定 GET 参数进入查询
直接用 $request->all() 或 $request->query() 拿全部参数拼 where,等于把攻击面全打开。Laravel 本身不自动过滤请求参数,必须显式声明白名单。
- 用
$request->only(['status', 'category_id', 'q'])明确收哪些字段,其余自动丢弃 - 别用
$request->except()—— 新增参数时容易漏加,维护成本高 - 如果参数名带下划线(如
min_price),确保模型字段也匹配,否则where()会静默失败 - 前端传
status=(空字符串)或status=null时,$request->only()仍会保留该键,后续需配合filled()判断
where() 链中跳过空值和无效值
where() 不会自动忽略 null 或空字符串,比如 where('status', $request->input('status')) 在 status 为空时会查出所有 status = '' 或 status = 0 的记录——这不是过滤,是误查。
- 优先用
$request->filled('status')替代$request->has('status')或$request->input('status') !== null - 数字类参数务必强制类型转换:
where('price', '>=', (int) $request->input('price_min')),否则字符串比较可能出错 - 模糊搜索统一走
where('title', 'like', "%{$request->q}%"),别混用=和like导致逻辑混乱 - 避免写
when($request->has('xxx'), ...)后没返回$builder,链式调用会中断,get()报错
路由参数约束防非法输入穿透到控制器
GET 查询参数能被用户随意改,但路径参数(如 /user/{id})更危险——它直接进路由匹配,绕过中间件和控制器校验。必须在路由层就卡死格式。
- 单个路由加约束:
->where('id', '[0-9]+'),非数字直接 404 - 全局约束更省事:
Route::pattern('id', '[0-9]+')写在RouteServiceProvider中,所有{id}自动生效 - UUID 类型用标准正则:
[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12},别简写成.* - slug 类型推荐
[a-z0-9\-]+,拒绝大写字母和特殊符号,避免后续 URL 重写或缓存问题
集合层过滤仅作兜底,不能替代数据库查询
有人图省事,在 User::all()->where(...) 里做筛选,这是严重性能陷阱。集合过滤发生在 PHP 内存里,数据量一过万就 OOM。
- 数据库过滤永远优先:
User::where(...)->get(),让 MySQL 承担筛选压力 - 集合过滤只用于已加载的少量数据,比如前端传了 5 条 ID,你查出来后按业务规则再筛一遍
- 用
where()时注意第三个参数是运算符,where('type', 'user')是全等匹配,where('score', '>=', 80)才是范围判断 - 复合逻辑(如“状态启用且创建时间在本周内”)必须用
filter()闭包,where()不支持多条件布尔表达式
where(),而是判断哪个环节该由数据库承担、哪个该由 PHP 处理、哪个根本不该放进来——参数白名单、路由约束、Eloquent 链式构建,这三层缺一不可。











