
在 laravel 8 中,可通过 `wherehas()` 结合闭包查询,在保持原有 eloquent 关系链的同时,对关联模型(如 `stock`、`category` 等)的字段进行 like 模糊搜索,无需破坏原有查询结构。
你当前的查询仅在主表 terms 的 title 字段中搜索关键词,但实际业务中常需支持“跨表搜索”——例如同时匹配商品标题(term.title)、库存编码(stock.code)、分类名称(category.name)或属性值(attributes.value)。Laravel 提供了优雅的解决方案:whereHas(),它允许你在已定义的 Eloquent 关系基础上,对关联模型执行条件查询。
以下是以你代码为基础的完整优化示例(假设关系已正确定义):
$posts = Term::where('user_id', $user_id)
->where('status', 1)
->where('type', 'product')
->with(['preview', 'attributes', 'category', 'price', 'options', 'stock', 'affiliate'])
->withCount('reviews');
// 若存在搜索关键词,则启用跨表模糊搜索
if (!empty($request->term)) {
$term = '%' . $request->term . '%';
$posts = $posts->where(function ($query) use ($term) {
// 主表字段搜索
$query->where('title', 'LIKE', $term)
->orWhereHas('stock', function ($q) use ($term) {
$q->where('code', 'LIKE', $term);
})
->orWhereHas('category', function ($q) use ($term) {
$q->where('name', 'LIKE', $term);
})
->orWhereHas('attributes', function ($q) use ($term) {
$q->where('value', 'LIKE', $term); // 假设 attributes 是一对多,且含 value 字段
});
});
}
$data = $posts->get(); // 执行最终查询✅ 关键说明:
- whereHas('stock', ...) 要求模型 Term 必须已定义 stock() 关系方法(如 return $this->hasOne(Stock::class, 'term_id'););
- 使用外层 where(function () {...}) 包裹所有 OR 条件,避免逻辑优先级错误(否则 WHERE status=1 AND type='product' OR ... 会破坏原有筛选);
- 若需搜索多个字段(如 stock.code 和 stock.barcode),可在 whereHas 的闭包内叠加 orWhere;
- 对于一对多关联(如 attributes),whereHas 默认按「至少一条匹配」生效;若需「全部匹配」,应使用 whereDoesntHave() 或子查询配合 havingRaw,但属进阶场景。
⚠️ 注意事项:
- 数据库索引至关重要:为高频搜索字段(如 term.title、stock.code、category.name)添加 INDEX,否则模糊查询(LIKE '%xxx%')将导致全表扫描,性能急剧下降;
- 避免在 whereHas 中嵌套过深或多层 orWhereHas,可能引发 N+1 或笛卡尔积风险,建议结合 with() 预加载并用 PHP 层二次过滤(适用于数据量小的场景);
- 如需全文检索能力(如中文分词、相关性排序),应考虑 MySQL FULLTEXT 索引或集成 Algolia / Meilisearch。
通过 whereHas(),你无需拆分查询、无需原生 SQL,即可在 Laravel 的 Eloquent 生态中安全、可维护地实现多表联合搜索——这才是符合 Laravel 哲学的优雅解法。










