You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:24:15