Laravel HasMany关联模型筛选:获取含全部指定物品类型的公园
解决Laravel中筛选包含全部指定类型物品的公园问题
嗨,我来帮你优化这个筛选逻辑~首先咱们先分析下你原来的代码为什么没效果:
你一开始用的where('type', json_decode($request->type_list)),其实Laravel会把数组参数转成whereIn条件,但whereHas的逻辑是只要关联模型中有一个符合条件就会返回该Park,所以这样写只能筛选出包含任意一种指定类型的公园,而不是你需要的全部包含。
你后来用循环取交集的方法虽然可行,但确实不够优雅,而且会执行多次数据库查询,数据量大的时候性能会打折扣。下面给你两个更高效简洁的方案:
方案一:链式调用多个whereHas
这是最直观的方式,遍历你的类型数组,给每个类型添加一个whereHas条件,这样每个条件都必须满足,也就是公园必须同时拥有所有指定类型的物品:
$typesArray = json_decode($request->type_list, true); // 初始化查询构造器 $parksQuery = Park::query(); // 给每个类型添加whereHas条件 foreach ($typesArray as $type) { $parksQuery->whereHas('items', function ($query) use ($type) { $query->where('type', $type); }); } // 执行查询 $parks = $parksQuery->get();
这个方法的优势是逻辑清晰,一次数据库查询就能完成,性能比循环取交集好很多。
方案二:用groupBy + having统计匹配类型数量
如果你的类型数组可能有重复(或者想更灵活地统计),可以用分组统计的方式,确保公园关联的物品类型覆盖了所有指定类型:
$typesArray = json_decode($request->type_list, true); $requiredTypeCount = count($typesArray); $parks = Park::whereHas('items', function ($query) use ($typesArray) { // 先筛选出包含任意指定类型的物品 $query->whereIn('type', $typesArray); }) ->groupBy('parks.id') // 按公园分组 // 统计该公园关联的不重复指定类型数量,必须等于需要的类型总数 ->havingRaw('COUNT(DISTINCT items.type) = ?', [$requiredTypeCount]) ->get();
这里用DISTINCT是为了避免同一个类型有多个物品时重复计数(比如一个公园有3个喷泉,只算1种类型)。
对比你的原方案
你原来的循环取交集方法,会执行N次数据库查询(N是类型数量),当类型多或者公园数据量大时,数据库压力会比较大。上面两个方案都是一次查询,代码更简洁,性能也更优。
另外别忘了处理边界情况:如果传入的type_list是空数组,你可以根据业务需求返回所有公园,或者直接返回空集合哦~
内容的提问来源于stack exchange,提问作者Oscar
相关产品推荐
相关产品推荐

