Laravel查询聊天记录如何去重 仅获取各发送者最新消息
Laravel聊天接口按发送者取最新消息实现方案
场景说明
基于ReactJS + Laravel栈开发聊天应用,消息列表接口需要实现:针对指定消息接收方(对应字段userId),每个消息发送方(对应字段senderId)仅返回最新1条消息,不返回全量历史消息,返回结构匹配MVP版本约定格式。
原有问题代码
$types= chat::select('id','senderId')->where('userId', $id)->get(); foreach ($types as $chats) { $chats->chat = chat::select('id','unseenMsgs', 'senderId')->where('senderId', $chats->senderId)->get(); foreach ($chats->chat as $child){ $child->lastMessage = chat::select('userId','message', 'time', 'senderId') ->where('id', $child->id) ->orderBy('id', 'asc')->skip(0)->take(1) ->get(); } } return ['chatsContacts' => $types];
原有逻辑问题
- 第一层查询未对
senderId做去重,会重复拉取同一发送者的多条历史消息,产生冗余遍历 - 取消息时使用
orderBy('id', 'asc')正序排列,取到的是会话最早的消息,和「取最新消息」的需求完全相反 - 多层循环嵌套执行SQL查询,属于典型的N+1查询问题,数据量上升后接口响应速度会严重下降
可直接落地的实现代码
基于模型关联的写法(推荐,性能更好)
// 第一步:一次性查出当前用户作为接收方时,每个发送者对应的最新消息ID $latestMessageIds = Chat::where('userId', $id) ->groupBy('senderId') ->selectRaw('MAX(id) as latest_id') ->pluck('latest_id'); // 第二步:批量查询最新消息详情,同时统计每个会话的未读消息数 $chatsContacts = Chat::whereIn('id', $latestMessageIds) ->withCount(['unseenMsgs' => function($query) use ($id) { // 未读统计规则:当前接收方视角下,该发送者发来的未读消息,可根据自身表结构调整判断条件 $query->where('userId', $id)->where('is_seen', 0); }]) ->select('id', 'senderId', 'userId', 'message', 'time') ->orderBy('time', 'desc') ->get() ->map(function ($chat) { // 按接口要求结构组装返回数据 return [ 'id' => $chat->id, 'senderId' => $chat->senderId, 'unseenMsgs' => $chat->unseen_msgs_count, 'lastMessage' => [ 'userId' => $chat->userId, 'message' => $chat->message, 'time' => $chat->time, 'senderId' => $chat->senderId ] ]; }); return ['chatsContacts' => $chatsContacts];
无模型关联的兼容写法
如果没有定义未读消息的模型关联,直接用原生SQL聚合统计即可,不需要额外改模型:
// 一次性查出每个发送者的最新消息ID、对应未读数 $senderLatestData = Chat::where('userId', $id) ->groupBy('senderId') ->selectRaw('MAX(id) as latest_id, SUM(CASE WHEN is_seen = 0 THEN 1 ELSE 0 END) as unseen_count, senderId') ->get() ->keyBy('latest_id'); $latestMessageIds = $senderLatestData->pluck('latest_id'); // 批量查最新消息详情 $chatsContacts = Chat::whereIn('id', $latestMessageIds) ->select('id', 'senderId', 'userId', 'message', 'time') ->orderBy('time', 'desc') ->get() ->map(function ($chat) use ($senderLatestData) { $latestInfo = $senderLatestData->get($chat->id); return [ 'id' => $chat->id, 'senderId' => $chat->senderId, 'unseenMsgs' => $latestInfo->unseen_count, 'lastMessage' => [ 'userId' => $chat->userId, 'message' => $chat->message, 'time' => $chat->time, 'senderId' => $chat->senderId ] ]; }); return ['chatsContacts' => $chatsContacts];
注意:如果你的
unseenMsgs字段是直接存在会话表的固定值而非消息维度的统计字段,直接在分组查询时取该字段对应值即可,不需要做sum聚合。
内容的提问来源于stack exchange,提问作者syrine bh
相关产品推荐
相关产品推荐

