Laravel Eloquent:能否用单查询实现指定顺序关联视频获取?
单查询实现按关联类型优先级获取视频
要实现按「关联Cars的视频优先,其次是Bikes,最后是Trucks」的顺序返回最多6条视频,且仅用单次查询,可以通过给视频标记优先级权重的方式实现,核心思路是用CASE语句结合关联存在判断排序,同时确保去重。
实现代码
protected function related(Video $video): Collection { $nVideos = 6; // 获取当前视频关联的各模型ID集合 $carIds = $video->cars->pluck('id')->toArray(); $bikeIds = $video->bikes->pluck('id')->toArray(); $truckIds = $video->trucks->pluck('id')->toArray(); // 获取多对多关联的中间表名(适配自定义表名场景) $carPivotTable = (new Video)->cars()->getTable(); $bikePivotTable = (new Video)->bikes()->getTable(); $truckPivotTable = (new Video)->trucks()->getTable(); return Video::whereNot('id', $video->id) // 筛选关联至少一种目标类型的视频 ->where(function ($query) use ($carIds, $bikeIds, $truckIds) { $query->whereHas('cars', fn($q) => $q->whereIn('id', $carIds)) ->orWhereHas('bikes', fn($q) => $q->whereIn('id', $bikeIds)) ->orWhereHas('trucks', fn($q) => $q->whereIn('id', $truckIds)); }) // 按关联类型优先级排序:Cars(1) > Bikes(2) > Trucks(3) ->orderByRaw(" CASE WHEN EXISTS (SELECT 1 FROM {$carPivotTable} WHERE video_id = videos.id AND car_id IN (" . implode(',', $carIds) . ")) THEN 1 WHEN EXISTS (SELECT 1 FROM {$bikePivotTable} WHERE video_id = videos.id AND bike_id IN (" . implode(',', $bikeIds) . ")) THEN 2 WHEN EXISTS (SELECT 1 FROM {$truckPivotTable} WHERE video_id = videos.id AND truck_id IN (" . implode(',', $truckIds) . ")) THEN 3 ELSE 4 END ") // 避免同一视频因关联多类型重复出现 ->distinct() ->take($nVideos) ->get(); }
关键说明
- 优先级排序逻辑:通过
CASE语句和EXISTS子查询,直接在数据库层面判断视频是否关联对应类型,给不同类型分配排序权重,确保关联Cars的视频排在最前。 - 去重处理:使用
distinct()保证同一视频即使关联多个类型,也只会按最高优先级返回一次。 - 适配自定义中间表:通过模型关联方法获取中间表名,避免硬编码表名导致的兼容性问题。
内容的提问来源于stack exchange,提问作者Jefferson Moreira
相关产品推荐
相关产品推荐

