Laravel Eloquent如何正确实现可选筛选与分页结合
核心问题说明
你当前代码的核心错误在于调用Customer::all()的时机:
all()会直接执行全表查询,把数据库中所有客户记录全部加载到PHP内存的Eloquent集合中,后续的where筛选、分页都是在内存集合上做处理,不是数据库层面的查询- 当表数据量超过千条级别时,会直接出现内存溢出、接口响应超时的问题,数据量越大性能越差
- 没有对入参做合法性校验,非法的
per_page参数会直接导致分页报错
优化实现方案
所有筛选、分页逻辑必须在数据库查询构造器层面完成,绝对不要先查询全量数据再在内存中处理。
基础可用版本
public function index(Request $request) { // 入参校验,给per_page设置默认值和上限,避免恶意请求拉取全量数据 $validated = $request->validate([ 'per_page' => 'nullable|integer|min:1|max:100', 'name' => 'nullable|string|max:255', // 后续新增筛选字段直接在这里追加校验规则即可,比如手机号、客户状态、注册时间 ]); $itemPerPage = $validated['per_page'] ?? 15; // 初始化查询构造器,此时不会执行任何SQL $customerQuery = Customer::query(); // 仅当筛选参数存在时,才拼接对应的where条件到SQL中 if (!empty($validated['name'])) { // 精确匹配用where('name', '=', $validated['name']),模糊匹配用下面的like写法 $customerQuery->where('name', 'like', "%{$validated['name']}%"); } // 数据库层面执行分页,自动生成count统计、limit分页SQL,性能最优 return new CustomerCollection($customerQuery->paginate($itemPerPage)); }
这个方案天然支持可选筛选:参数传了就追加筛选条件,不传就返回全量分页数据,完全符合需求。
更简洁的链式写法
可以用Laravel查询构造器自带的when()方法省略重复的if判断,代码可读性更高:
public function index(Request $request) { $validated = $request->validate([ 'per_page' => 'nullable|integer|min:1|max:100', 'name' => 'nullable|string|max:255', ]); $itemPerPage = $validated['per_page'] ?? 15; $customerList = Customer::query() ->when(!empty($validated['name']), function ($query) use ($validated) { $searchName = str_replace(['%', '_'], ['\%', '\_'], $validated['name']); $query->where('name', 'like', "%{$searchName}%"); }) // 新增筛选条件直接链式追加when块即可 ->paginate($itemPerPage); return new CustomerCollection($customerList); }
注意:使用like模糊查询时,要手动转义用户输入中的
%、_通配符,避免特殊字符导致查询结果不符合预期。
方案优势
- 全程没有全表查询,所有筛选、分页逻辑都在数据库执行,即使表数据量达到百万级也能保持稳定性能
- 入参校验拦截非法请求,单页条数上限避免数据库被大查询打挂
- 结构清晰,后续新增筛选字段只需要加校验规则、追加对应的查询条件即可,维护成本极低
内容的提问来源于stack exchange,提问作者like_code
相关产品推荐
相关产品推荐

