必须用独立 messages 表实现私信功能,而非 Notification 类;需包含 sender_id、recipient_id、subject、body、is_read、deleted_by_sender、deleted_by_recipient 7 字段,建复合索引 (recipient_id, is_read, deleted_by_recipient),并封装 send() 静态方法。

别用 Notification 类发私信
它根本不是为用户间通信设计的——Notification 没有 receiver_id 字段,强行往 notifications 表里塞私信,会导致查询时无法按接收人索引,未读数统计错、列表漏数据、分页乱序。更麻烦的是,它的通道契约(比如 toMail()、toDatabase())和私信生命周期(如双向删除)完全不兼容。
- 查未读数时,你得写
Notification::where('notifiable_id', $user->id)->where('read_at', null)->count(),但这条语句没法区分“这是系统通知还是别人发来的私信” -
notifications表默认没建(notifiable_id, type)复合索引,10 万条记录后收件箱加载直接卡住 - 用户删了消息,你得调
$user->notifications()->delete(),结果把所有通知(含邮件回执、密码重置记录)全清了
messages 表字段不能省、顺序不能乱
必须包含 sender_id、recipient_id、subject、body、is_read、deleted_by_sender、deleted_by_recipient 这 7 个字段。少一个,后面逻辑就断档;顺序不对,索引失效。
-
deleted_by_sender和deleted_by_recipient是布尔值,不是软删除的deleted_at—— 否则一方删除后另一方再也查不到原始记录 - 复合索引必须建在
(recipient_id, is_read, deleted_by_recipient)上,缺一不可:只建(recipient_id),查未读列表会全表扫描;漏掉deleted_by_recipient,已删除的消息还会出现在分页结果里 -
subject设为nullable是合理的(比如纯文本提醒),但别设成TEXT,浪费索引空间;VARCHAR(255)足够
Message::send() 要封装成静态方法,别在控制器里 new Message()
直接 new 模型看似简单,但后续加事务、限流、事件触发、或改成队列异步发,就得翻控制器重写。封装进模型,逻辑收口,测试也方便。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 示例写法:
public static function send($sender, $recipient, $subject, $body) { return static::create([ 'sender_id' => $sender->id, 'recipient_id' => $recipient->id, 'subject' => $subject, 'body' => $body, 'is_read' => false, 'deleted_by_sender' => false, 'deleted_by_recipient' => false, ]); } - 如果要支持异步,就在这个方法里加
dispatch(new SendPrivateMessage(...)),而不是让控制器感知队列细节 - 千万别在队列任务里用
request()或session()—— 它们在队列上下文中不存在,会抛RuntimeException
查未读数和收件箱列表,条件永远以当前用户身份为基准
同一个消息对发送者和接收者,可见性完全不同。查的时候不严格绑定角色,就会出现“显示 3 条未读,点进去只有 1 条”的经典问题。
- 查未读数只能这样写:
Message::where('recipient_id', $user->id)->where('is_read', false)->where('deleted_by_recipient', false)->count() - 收件箱分页必须把
orderBy('created_at', 'desc')放在paginate()前面,否则 Laravel 会先分页再排序,结果顺序错乱 - 绝对不要用
withTrashed()查站内信 —— 逻辑删除就是“已删除”,不该出现在任何用户界面上,哪怕只是灰显
双向删除那套逻辑看着绕,但真上线后你会发现:用户删了消息又反悔想找回,或者客服要查历史沟通链路,全靠这两个布尔字段撑着。漏掉一个,后期补成本远高于一开始多写两行。










