Laravel多对多关联下,如何获取会话关联用户及两用户间的会话
原有错误写法的核心原因
$user->conversation不带括号时返回的是当前用户关联的所有会话组成的Eloquent集合,并非单个会话实例,直接链式调用->user是在集合对象上取不存在的属性,自然无法运行。- 第二种尝试直接在内存集合上筛选,既没有正确关联到会话下的用户表,还会存在因为提前加载全量数据导致的性能问题,逻辑上也无法覆盖“两个用户同时参与同一会话”的判断。
需求1实现:获取指定用户所有有会话往来的其他用户
优先推荐数据库层面的关联过滤写法,无N+1问题,性能最优:
$targetUserId = 1; $talkedUsers = User::query() ->where('id', '!=', $targetUserId) ->whereHas('conversation', function ($query) use ($targetUserId) { // 筛出同时关联了当前目标用户的会话 $query->whereHas('users', function ($queryBuilder) use ($targetUserId) { $queryBuilder->where('id', $targetUserId); }); }) ->get();
如果需要在已加载的用户会话集合上做处理,也可以用集合方法遍历:
$user = User::find(1); // 预加载关联避免N+1查询 $user->loadMissing('conversation.users'); $talkedUsers = $user->conversation // 展开所有会话下的用户列表 ->flatMap(fn($conversation) => $conversation->users) // 排除用户本身 ->reject(fn($item) => $item->id === $user->id) // 按用户ID去重 ->unique('id') ->values();
需求2实现:查询两个指定用户共同参与的所有会话
同样可以直接通过Eloquent关联过滤实现,无需原生SQL:
$userXId = 1; $userYId = 2; $sharedConversations = Conversation::query() // 会话必须包含用户X ->whereHas('users', fn($query) => $query->where('id', $userXId)) // 会话必须包含用户Y ->whereHas('users', fn($query) => $query->where('id', $userYId)) ->get();
如果已经拿到用户X的模型实例,也可以直接在其关联查询构建器上做过滤,注意这里关联方法要带括号才是查询构建器,不带括号会先把用户X的所有会话加载到内存:
$userX = User::find(1); $userYId = 2; $sharedConversations = $userX->conversation() ->whereHas('users', fn($query) => $query->where('id', $userYId)) ->get();
内容的提问来源于stack exchange,提问作者Victoracho
相关产品推荐
相关产品推荐

