Laravel筛选值非空时动态添加where/whereIn条件的最优方案
Laravel多筛选项动态查询最优实现
不用多层if-else拆查询拼接,直接用Laravel查询构造器原生提供的when()方法即可,逻辑内聚在查询链上,可读性高,没有额外性能开销。
第一步:提前预处理所有请求参数
不要把参数格式化逻辑混在查询链里,统一先处理完所有入参默认值、格式转换:
$perPage = 20; // 批量提取需要的筛选项,不用逐个调用$request取参 $filters = $request->only([ 'input1', 'input2', 'start_date', 'end_date', 'process', 'status', 'user_id' ]); // 格式化日期参数 if (!empty($filters['start_date'])) { $filters['start_date'] .= ' 00:00:00'; } if (!empty($filters['end_date'])) { // 注意:原逻辑拼00:00:00会漏掉结束日期当天的所有数据,这里修正为23:59:59 $filters['end_date'] .= ' 23:59:59'; } // 流程参数默认值 if (empty($filters['process'])) { $filters['process'] = [1,2,3,4]; }
第二步:链式调用when追加动态条件
when()方法接收三个参数:第一个是判断条件,条件为真时执行第二个闭包追加查询,条件为假时执行第三个可选闭包,完全匹配“非空才加条件、不同值走不同查询逻辑”的需求:
$items = DB::table('transactions_tbl') ->select([/* 你需要查询的字段数组 */]) ->leftJoin('tbl_1 as table1', 'table1.id', '=', 'transactions_tbl.tbl1_f_key') ->leftJoin('tbl_2 as table2', 'table2.id', '=', 'transactions_tbl.tbl2_f_key') // input1非空时追加等值查询 ->when(!empty($filters['input1']), function ($query) use ($filters) { $query->where('transactions_tbl.input1', $filters['input1']); }) // input2非空时追加等值查询 ->when(!empty($filters['input2']), function ($query) use ($filters) { $query->where('transactions_tbl.input2', $filters['input2']); }) // process逻辑:传了有效值走where单值匹配,没传走默认数组的whereIn ->when(!empty($request->input('process')), function ($query) use ($filters) { $query->where('transactions_tbl.process', $filters['process']); }, function ($query) use ($filters) { $query->whereIn('transactions_tbl.process', $filters['process']); }) // 开始时间条件 ->when(!empty($filters['start_date']), function ($query) use ($filters) { $query->where('transactions_tbl.created_at', '>=', $filters['start_date']); }) // 结束时间条件 ->when(!empty($filters['end_date']), function ($query) use ($filters) { $query->where('transactions_tbl.created_at', '<=', $filters['end_date']); }) // 状态条件 ->when(!empty($filters['status']), function ($query) use ($filters) { $query->where('transactions_tbl.status', $filters['status']); }) // 指派用户条件 ->when(!empty($filters['user_id']), function ($query) use ($filters) { $query->where('transactions_tbl.assigned_user', $filters['user_id']); }) ->paginate($perPage);
筛选项更多时的简化写法
如果后续筛选项增加到10个以上,可以把通用的等值查询抽成映射数组批量处理,减少重复代码:
// 配置等值查询的字段和参数映射:[数据库字段名 => 对应请求参数键名] $equalFilterMap = [ 'transactions_tbl.input1' => 'input1', 'transactions_tbl.input2' => 'input2', 'transactions_tbl.status' => 'status', 'transactions_tbl.assigned_user' => 'user_id' ]; // 初始化基础查询 $query = DB::table('transactions_tbl') ->select([/* 查询字段数组 */]) ->leftJoin('tbl_1 as table1', 'table1.id', '=', 'transactions_tbl.tbl1_f_key') ->leftJoin('tbl_2 as table2', 'table2.id', '=', 'transactions_tbl.tbl2_f_key'); // 批量追加通用等值条件 foreach ($equalFilterMap as $column => $paramKey) { $query->when(!empty($filters[$paramKey]), function ($q) use ($column, $filters, $paramKey) { $q->where($column, $filters[$paramKey]); }); } // 单独追加特殊逻辑的条件(非等值、区间、默认值类条件) $query->when(!empty($request->input('process')), function ($q) use ($filters) { $q->where('transactions_tbl.process', $filters['process']); }, function ($q) use ($filters) { $q->whereIn('transactions_tbl.process', $filters['process']); }) ->when(!empty($filters['start_date']), function ($q) use ($filters) { $q->where('transactions_tbl.created_at', '>=', $filters['start_date']); }) ->when(!empty($filters['end_date']), function ($q) use ($filters) { $q->where('transactions_tbl.created_at', '<=', $filters['end_date']); }); // 最终分页取数 $items = $query->paginate($perPage);
注意事项
- 判断参数非空不要用
isset(),如果参数有效值是0或字符串"0"会被误判为空,根据业务场景选择!empty()或者更严谨的判断规则 - 日期条件如果不想手动拼接字符串,可以直接用
whereDate/whereBetween方法,省去时间字符串拼接的出错可能 - 该写法和多层if拼接的SQL生成逻辑完全一致,没有额外性能损耗,不会追加无效的where条件
内容的提问来源于stack exchange,提问作者newCode
相关产品推荐
相关产品推荐

