如何在Laravel中正确查询并拼接当前用户的全部往来会话消息
问题分析
你现有两种SQL写法都无法实现按会话分组的需求,问题如下:
- 不带GROUP BY的查询可以拉取所有属于当前用户的消息,但数据是平铺状态,没有会话归属标识,自然无法按会话整合
- 带GROUP BY的写法逻辑完全错误,仅按sender_id分组只会随机返回每个发送者的一条消息,拿不到完整会话内容,同时如果数据库开启
ONLY_FULL_GROUP_BY模式会直接抛出语法错误
该需求完全可以通过Laravel框架实现,具体方案如下:
1. 核心实现逻辑
按会话分组的核心是给每条消息生成统一的会话标识:同一对用户的往来消息,不管谁是发送方谁是接收方,生成的会话标识完全一致。
通用规则为:将sender_id和receiver_id按数值大小排序后拼接,比如当前用户ID为1,和ID为2的用户往来时,不管是1发2还是2发1,会话标识都为1_2,即可将所有同会话的消息归为一组。
2. 原生SQL实现参考
如果需要直接用SQL查询,可以用以下写法拿到带会话标识的所有消息:
SELECT sender_id, receiver_id, message, created_at, CONCAT(LEAST(sender_id, receiver_id), '_', GREATEST(sender_id, receiver_id)) as conversation_key FROM messages WHERE sender_id = 1 OR receiver_id = 1 ORDER BY created_at ASC;
查询结果拿到后按conversation_key字段分组即可得到所有会话内容。
3. Laravel框架实现方案
3.1 先定义Message模型
// app/Models/Message.php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Message extends Model { protected $fillable = ['sender_id', 'receiver_id', 'message']; }
3.2 获取所有消息并按会话分组
借助Laravel集合的分组方法可以非常便捷的完成需求:
// 取当前登录用户ID $currentUserId = auth()->id(); $messageGroups = Message::where('sender_id', $currentUserId) ->orWhere('receiver_id', $currentUserId) ->orderBy('created_at', 'asc') ->get() // 按会话标识分组 ->groupBy(function ($item) use ($currentUserId) { // 取当前会话的对方用户ID $oppositeId = $item->sender_id === $currentUserId ? $item->receiver_id : $item->sender_id; // 生成统一的会话标识 return min($currentUserId, $oppositeId) . '_' . max($currentUserId, $oppositeId); });
最终返回的$messageGroups为键值对结构:键是会话标识,值是对应会话下按时间正序排列的所有消息集合,可直接用于前端会话列表、会话详情的渲染。
3.3 扩展:获取会话列表(每个会话仅显示最新一条消息)
如果是做消息首页的会话列表,不需要拉取所有历史消息,只需每个会话返回最新一条,可以用以下写法:
$currentUserId = auth()->id(); $conversationList = Message::selectRaw("*, CONCAT(LEAST(sender_id, receiver_id), '_', GREATEST(sender_id, receiver_id)) as conversation_key") ->where('sender_id', $currentUserId) ->orWhere('receiver_id', $currentUserId) ->orderBy('created_at', 'desc') ->get() ->unique('conversation_key') // 每个会话仅保留最新的一条消息 ->values();
内容的提问来源于stack exchange,提问作者Նորայր Եղիազարյան
相关产品推荐
相关产品推荐

