Laravel中whereJsonDoesntContain查询未读消息失败排查求助
Laravel JSON字段键值对筛选未生效问题排查
你的问题出在whereJsonDoesntContain的用法上——这个方法主要用于JSON数组的元素匹配,而你的seen字段存储的是JSON对象(键为成员ID、值为类型的关联数组),原写法无法正确匹配键值对的存在性。
问题根源
当seen存储的是类似{"1":"admin"}的JSON对象时,whereJsonDoesntContain('seen', [$member->id => 'admin'])不符合Laravel JSON查询的语法逻辑,该方法无法识别你要检查的是「对象中是否存在指定键值对」,因此筛选不生效。
解决方案
以下两种方法可以实现需求:
方案一:使用原生SQL查询(适配MySQL)
直接通过数据库原生JSON函数精准判断,兼容性更强:
$chats = ChatsMembers::with([ 'unread' => function ($query) use ($member) { $jsonPath = "$." . $member->id; // 筛选逻辑:要么不存在该成员ID的键,要么键存在但值不是admin $query->whereRaw('NOT JSON_CONTAINS_PATH(seen, \'one\', ?) OR JSON_UNQUOTE(JSON_EXTRACT(seen, ?)) != ?', [$jsonPath, $jsonPath, 'admin']) ->select(['id', 'content', 'created_at', 'member_id']); } ]) ->where([['user_id', $userID], ['user_type', '0']]) ->join('chats', 'chats_members.chat_id', '=', 'chats.id') ->select('chats.id', 'chats.name', 'chats.bio', 'chats.image', 'chats.status') ->get();
方案二:使用Laravel内置JSON查询方法组合
如果想避免原生SQL,可以通过组合条件实现:
$chats = ChatsMembers::with([ 'unread' => function ($query) use ($member) { $query->where(function ($q) use ($member) { // 情况1:seen中没有该成员ID的键 $q->whereJsonDoesntHave('seen', $member->id) // 情况2:有该键,但对应的值不是admin ->orWhere(function ($subQ) use ($member) { $subQ->whereJsonHas('seen', $member->id) ->whereJsonDoesntContain('seen', [$member->id => 'admin']); }); }) ->select(['id', 'content', 'created_at', 'member_id']); } ]) ->where([['user_id', $userID], ['user_type', '0']]) ->join('chats', 'chats_members.chat_id', '=', 'chats.id') ->select('chats.id', 'chats.name', 'chats.bio', 'chats.image', 'chats.status') ->get();
额外注意事项
- 确保
seen字段在数据库中的类型是JSON或TEXT(Laravel能自动解析为JSON) - 检查
$member->id的类型与seen中存储的键类型一致(比如都是数字或字符串,避免类型不匹配导致查询失效)
内容的提问来源于stack exchange,提问作者Abdullah A. Khalil
相关产品推荐
相关产品推荐

