
本文详解如何在 Laravel 中正确实现带条件筛选的数据库搜索,解决原始代码中因混用原生查询与集合方法导致的 Call to a member function where() on Array 和 explode() expects parameter 2 to be string, object given 等典型错误。
本文详解如何在 laravel 中正确实现带条件筛选的数据库搜索,解决原始代码中因混用原生查询与集合方法导致的 `call to a member function where() on array` 和 `explode() expects parameter 2 to be string, object given` 等典型错误。
在 Laravel 开发中,实现搜索功能时常见的误区是:在原生 SQL 查询(如 DB::select())后,试图对返回的数组直接调用 Eloquent 集合方法(如 where()、filter())——这必然失败,因为数组不具备这些方法;而若转为 collect() 后误用闭包式 where()(即 where(Closure)),又会触发底层 Str::contains() 等字符串处理函数对对象的非法调用,引发 explode() 错误。
✅ 正确做法是:将搜索逻辑下推至数据库层,优先使用 Eloquent 或 Query Builder 构建可链式调用的查询构造器(QueryBuilder),而非先取全量数据再内存过滤。这样既保障性能,又避免类型错误。
以下为推荐重构方案(基于您原有结构优化):
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
public function index($mid, Request $request)
{
// ✅ 使用 Request 对象自动验证和获取参数(更安全)
$search = $request->input('search', '');
// ✅ 使用 Query Builder 替代原生 SQL —— 支持链式 + 自动参数绑定,防 SQL 注入
$query = DB::table('pengguna as a')
->leftJoin('penggunarank as b', 'a.effective_rank', '=', 'b.id')
->leftJoin('penggunarank as c', 'a.appointed_rank', '=', 'c.id')
->leftJoin('hirarki as d', 'a.userrank', '=', 'd.id')
->leftJoin('pengguna as e', 'a.placement', '=', 'e.id')
->leftJoin('hirarkimid as f', function ($join) use ($mid) {
$join->on('a.userrank', '=', 'f.hirarki')
->on('a.mid', '=', DB::raw('?'), [DB::raw($mid)]);
})
->select(
'a.id', 'a.alternate_id', 'a.setujuterma', 'a.mykad', 'a.nama', 'a.email',
'a.notel', 'a.etunai', 'b.ranktitle', 'c.ranktitle as appointed_rank',
'd.nama as hirarki', 'e.alternate_id as placement', 'e.nama as leadername',
'a.akses', 'a.suspendreason', 'a.regstamp', 'a.matagajet',
'f.display as hirarkidisplay',
DB::raw('IF(a.mykadverify = "3", "1", "0") as mykadverifydecode')
)
->where('a.mid', $mid)
->where('a.akses', '!=', -1);
// ✅ 搜索条件动态添加(仅当有输入时)
if (!empty($search)) {
$query->where(function ($q) use ($search) {
$q->where('a.alternate_id', 'LIKE', "%{$search}%")
->orWhere('a.nama', 'LIKE', "%{$search}%")
->orWhere('a.mykad', 'LIKE', "%{$search}%")
->orWhere('a.notel', 'LIKE', "%{$search}%")
->orWhere('a.email', 'LIKE', "%{$search}%");
});
}
// ✅ 排序 + 分页(推荐用 paginate() 替代 toArray() + sortByDesc)
$users = $query->orderBy('a.alternate_id', 'DESC')->get();
return [
'data' => $users,
'count' => $users->count(),
];
}
? 关键改进说明:
- 杜绝 SQL 注入风险:所有 $mid 和 $search 均通过参数绑定传递,不再拼接字符串;
- 搜索前置执行:where(...) 在 get() 之前完成,由数据库完成过滤,避免 PHP 层遍历大数组;
- 移除冗余 collect() / sortByDesc():数据库 ORDER BY 更高效;如需前端排序,应交由 JS 处理;
- 返回结构清晰化:明确 data 和 count 字段,便于前端消费;
- 兼容性增强:使用 Request 类型提示替代原始 $payload 数组,启用 Laravel 的自动验证、过滤与 CSRF 保护能力。
⚠️ 注意事项:
- 若必须使用原生 SQL(如复杂视图或存储过程),请改用 DB::select() + array_filter() 手动筛选,但务必对 $search 进行 htmlspecialchars() 或正则清洗,防止 XSS;
- collect()->where(Closure) 仅适用于 Eloquent Collection,且 Closure 参数是单个模型实例(非 QueryBuilder),切勿混淆;
- 生产环境建议为高频搜索字段(如 nama, mykad)添加数据库索引,显著提升响应速度。
遵循以上实践,即可写出健壮、可维护、高性能的 Laravel 搜索逻辑。











