Laravel5.4+MySQL5.7:订单与司机技能及标签匹配查询问题
解决Laravel中司机技能与客户订单的匹配查询问题
看起来你在实现司机技能和订单匹配的逻辑时踩了几个常见的坑,我来帮你梳理并修复这些问题:
原代码的核心问题
- 错误引用固定技能项:循环里一直用
$skills[0],导致永远只处理司机的第一个技能,完全忽略了其他技能 - 标签匹配逻辑错误:
whereIn('tags', $tags_values)无法正确匹配多值的tags字段(不管是JSON数组还是逗号分隔字符串) - 查询条件结构混乱:循环中的
where会不断覆盖之前的条件,没有用分组逻辑实现"或"关系(司机匹配任意一个技能+标签组合即可) - 冗余写法:
where('type','=',NULL)可以简化为更规范的whereNull('type')
修复后的完整代码
// 先整理司机的技能及对应标签,方便后续查询使用 $driverSkills = UsersSkills::with('tags') ->where('user_id', $biker_id) ->get() ->map(function ($skill) { return [ 'skill_id' => $skill->skill_id, 'tags' => $skill->tags->pluck('tag')->toArray() ]; }); // 查询符合条件的订单 $orders = Order::whereHas('order_items', function ($query) use ($driverSkills) { // 所有技能匹配条件是"或"关系:司机只要匹配任意一个技能+标签组合即可 $query->where(function ($subQuery) use ($driverSkills) { foreach ($driverSkills as $skill) { $subQuery->orWhere(function ($skillQuery) use ($skill) { // 匹配技能层级:订单项item_id以司机的skill_id开头(支持多层级匹配) $skillQuery->where('item_id', 'LIKE', $skill['skill_id'] . '%'); // 匹配标签:如果该技能有标签,需要订单项包含所有标签(可根据需求调整为"任意匹配") if (!empty($skill['tags'])) { foreach ($skill['tags'] as $tag) { // 假设tags字段是MySQL JSON数组类型,用whereJsonContains匹配 $skillQuery->whereJsonContains('tags', $tag); // 如果tags是逗号分隔字符串,替换为下面的写法: // $skillQuery->whereRaw('FIND_IN_SET(?, tags)', [$tag]); } } }); } }); })->where(function($query) use($maxLat,$minLat,$maxLon,$minLon,$biker_search_time,$estimation_id) { $query->where('done_by_expert', 1) ->whereBetween('destination_longitude', [$minLon, $maxLon]) ->whereBetween('destination_latitude', [$minLat, $maxLat]) ->where('status', 'pending') ->whereNull('type') ->where('vehicle_type_id', $estimation_id) ->whereNull('favorites') ->where('created_at', '>=', Carbon::now()->subMinute($biker_search_time)); })->get();
关键调整说明
- 技能数据预处理:用
map把司机的技能和标签整理成结构化数组,避免在查询闭包里重复调用关系方法 - 分组查询逻辑:用嵌套的
where+orWhere实现"多技能任意匹配"的逻辑,每个技能的匹配条件是独立的分组 - 标签匹配适配:
- 如果你的
tags字段是MySQL JSON数组类型:用whereJsonContains精准匹配标签 - 如果是逗号分隔的字符串:用
FIND_IN_SET函数进行匹配
- 如果你的
- 灵活的标签逻辑:如果需求是"订单项只要有一个标签匹配即可",可以把标签循环改成:
if (!empty($skill['tags'])) { $skillQuery->where(function ($tagQuery) use ($skill) { foreach ($skill['tags'] as $tag) { $tagQuery->orWhereJsonContains('tags', $tag); } }); }
内容的提问来源于stack exchange,提问作者bitcodr
相关产品推荐
相关产品推荐

