Laravel中Contact与Company关联搜索逻辑问题排查及优化求助
问题排查与优化方案
我来帮你理顺这个查询逻辑的问题,你的代码确实在同时传入client和search参数时出现了逻辑偏差,而且整体的分支结构可以大幅简化,降低出错概率。
问题根源分析
你代码里的第一个when分支(同时存在client和search时)逻辑有问题:
$q->whereHas('companies', function ($q) use ($request) { $q->where('is_client', true)->where(function ($q) use ($request) { $q->orWhere('name', 'like', '%'. $request->search . '%'); }); })->where(function ($q) use ($request) { $q->orWhere('first_name', 'like', '%'.$request->search.'%') ->orWhere('last_name', 'like', '%'.$request->search.'%'); });
这里的whereHas子查询里,where('is_client', true)之后跟着的orWhere会让子查询变成**“公司是客户”或“公司名匹配搜索词”,但你实际需要的是“联系人关联客户公司,且(联系人姓名匹配或该客户公司名匹配)”**。另外,外层where和whereHas的组合逻辑也没把所有搜索条件统一到“与客户关联”的约束下,导致结果不符合预期。
除此之外,你用了三个独立的when分支,代码冗余度高,很容易在修改时遗漏逻辑。
优化后的代码方案
我们可以把逻辑合并,用更简洁的方式实现需求:
$contacts = Contact::query() // 筛选客户联系人:仅当client参数存在时,限制关联的公司为客户 ->when($request->client, function ($query) { $query->whereHas('companies', fn($subQ) => $subQ->where('is_client', true)); }) // 处理搜索逻辑:统一匹配联系人姓名或关联公司名(客户模式下仅匹配客户公司) ->when($request->search, function ($query) use ($request) { $searchTerm = '%' . $request->search . '%'; $query->where(function ($innerQuery) use ($searchTerm, $request) { // 匹配联系人自身姓名 $innerQuery->where('first_name', 'like', $searchTerm) ->orWhere('last_name', 'like', $searchTerm) // 匹配关联公司名称,客户模式下额外限制为客户公司 ->orWhereHas('companies', function ($subQuery) use ($searchTerm, $request) { $subQuery->where('name', 'like', $searchTerm); if ($request->client) { $subQuery->where('is_client', true); } }); }); }) ->paginate(30);
优化逻辑说明
- 客户筛选逻辑:单独用一个
when处理client参数,确保只要传入该参数,联系人必须关联至少一个标记为客户的公司,对应你“客户联系人初始列表”的需求。 - 统一搜索逻辑:把所有搜索条件包裹在同一个
where闭包里,确保:- 联系人的
first_name或last_name匹配搜索词 - 或者关联公司的名称匹配搜索词(如果是客户模式,仅匹配客户公司的名称)
- 联系人的
- 代码精简:提前拼接
$searchTerm避免重复代码,用短闭包(fn())简化子查询写法,整体逻辑更清晰,维护成本更低。
这个方案完美解决了你之前的问题:非客户联系人搜索、客户联系人初始列表显示正常,同时客户联系人的搜索结果也会严格符合“关联客户公司+匹配搜索词”的预期。
内容的提问来源于stack exchange,提问作者Nitish Kumar
相关产品推荐
相关产品推荐

