需先运行 php artisan notifications:table 生成迁移文件,再执行 php artisan migrate 创建 notifications 表;表中应手动添加 [notifiable_type, notifiable_id] 组合索引以提升查询性能。

notifications 表怎么建才不会报错
必须先跑 php artisan notifications:table,再执行 php artisan migrate。漏掉前者会导致迁移文件缺失,后者会报 Class 'CreateNotificationsTable' not found;漏掉后者则表根本不存在,发通知时抛出 SQLSTATE[42S02]: Base table or view not found: 1146 Table 'xxx.notifications' doesn't exist。
注意:该迁移文件默认只含基础字段(id、type、notifiable_type、notifiable_id、data、read_at 等),不带索引。线上环境建议手动在迁移里加组合索引:$table->index(['notifiable_type', 'notifiable_id']),否则查用户未读数会慢。
via() 返回 ['database'] 但没存进去?检查这三点
数据库通知看似简单,但卡住基本都出在这几个地方:
- 目标模型没 use
Notifiabletrait —— 比如自定义的App\Models\Admin忘了加,notify()方法根本不存在 -
toDatabase()方法返回的不是数组,比如误写成return $this->reply->toArray();而没包装成键值对,结果存进data字段的是空字符串或 null - 通知类构造函数里传入的模型被 eager loading 或 soft delete 影响,导致
$this->reply->user为 null,toDatabase()中取$this->reply->user->name报Trying to get property 'name' of non-object,整个通知静默失败(Laravel 默认不抛异常)
toDatabase() 返回的数据结构怎么设计才好查好展
data 字段是 JSON,但别把它当黑盒乱塞。前端要渲染「XX 在帖子《YYY》中回复了你」,后端就该明确提供可读字段:
推荐这样返回:
return [
'message' => "在《{$topic->title}》中回复了你",
'link' => $topic->link(['#reply'.$this->reply->id]),
'topic_id' => $topic->id,
'reply_id' => $this->reply->id,
'actor_name' => $this->reply->user->name ?? '已注销用户',
];
不推荐这样:
– 只返回原始 $this->reply->toArray(),字段多且嵌套深,前端解析成本高
– 把 HTML 片段拼好塞进去,违反前后端分离原则,也难做国际化
– 缺少 link 或 topic_id,导致点击通知跳转逻辑得额外查库
unreadNotifications 性能差?别直接遍历
$user->unreadNotifications 是 Eloquent 关系,底层是 where read_at is null 查询。如果用户有几千条未读,这个集合加载太重。
更实用的做法是:
- 只取最新 20 条:
$user->unreadNotifications()->latest()->limit(20)->get() - 需要总数时用 count() 而非 load 后 count:
$user->unreadNotifications()->count() - 标记已读别用循环:
$user->unreadNotifications->markAsRead()是 N+1,改用$user->unreadNotifications()->update(['read_at' => now()])
真正容易被忽略的是:notification 的 data 字段一旦存了大对象(比如带 avatar URL 的完整用户数据),单条记录可能超 10KB,批量读时 IO 和内存压力会陡增——宁可前端按需拉取关联数据,也别在 data 里冗余存。











