数字化技能提升教程,数字化时代生存宝典

Laravel通知系统

浏览:90

用户注册、订单状态变化、密码重置和系统告警等场景,都需要向用户发送提醒。Laravel Notifications 支持邮件、数据库、广播和短信等通知渠道,可以将通知内容统一管理,再根据业务选择发送方式。

下面以订单创建后的通知为例,介绍邮件通知和数据库通知的基本用法。

一、创建通知类

在项目根目录执行:

php artisan make:notification OrderCreatedNotification

Laravel 会生成:

app/Notifications/OrderCreatedNotification.php

通知类可以同时定义多个发送渠道,例如邮件和数据库。

二、定义通知渠道

编辑通知文件:

<?php

namespace App\Notifications;

use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class OrderCreatedNotification extends Notification implements ShouldQueue
{
use Queueable;

public function __construct(
public Order $order
) {
}

public function via(object $notifiable): array
{
return ['mail', 'database'];
}

public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject('订单创建成功')
->greeting('您好')
->line("您的订单 {$this->order->id} 已创建成功。")
->line("订单金额:{$this->order->amount}")
->action('查看订单', url("/orders/{$this->order->id}"))
->line('感谢您的使用。');
}

public function toDatabase(object $notifiable): array
{
return [
'order_id' => $this->order->id,
'message' => "订单 {$this->order->id} 已创建成功",
'status' => $this->order->status,
];
}
}

via() 决定通知使用哪些渠道:

  • mail:发送邮件。
  • database:保存到数据库。
  • broadcast:通过广播推送。
  • vonage:使用 Vonage 短信服务。

本示例实现了邮件和数据库两种渠道。

三、让用户支持通知

Laravel 默认会在 User 模型中引入 Notifiable Trait:

use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
use Notifiable;
}

如果项目中没有该 Trait,需要手动添加。

邮件通知默认使用用户模型中的 email 字段。也可以通过 routeNotificationForMail() 自定义邮件地址:

public function routeNotificationForMail(
    $notification
): string {
    return $this->notification_email;
}

自定义字段必须存在于用户模型或相关业务数据中。

四、创建通知数据表

数据库通知需要使用 Laravel 的通知表。执行:

php artisan notifications:table
php artisan migrate

迁移完成后,通知会保存到 notifications 表。

表中通常包含:

  • 通知 ID。
  • 接收用户类型和 ID。
  • 通知类型。
  • 通知数据。
  • 已读时间。
  • 创建和更新时间。

执行迁移前,应确认项目数据库连接配置正确。

五、发送通知

假设当前用户已经登录,订单创建成功后可以发送通知:

use App\Notifications\OrderCreatedNotification;

$user->notify(
new OrderCreatedNotification($order)
);

也可以通过通知门面发送:

use Illuminate\Support\Facades\Notification;
use App\Notifications\OrderCreatedNotification;

Notification::send(
$users,
new OrderCreatedNotification($order)
);

其中,$users 可以是用户集合,也可以是实现了通知接收能力的对象集合。

六、配置邮件服务

邮件通知需要先配置邮件服务。.env 示例:

MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your-account@example.com
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=no-reply@example.com
MAIL_FROM_NAME="${APP_NAME}"

具体参数应以邮件服务商提供的配置为准。

修改配置后,如果项目启用了配置缓存,需要执行:

php artisan config:clear
php artisan config:cache

开发环境也可以使用日志邮件驱动:

MAIL_MAILER=log

使用 log 驱动时,邮件内容会写入应用日志,适合检查模板和发送流程,不会真正发送到收件箱。

七、使用队列异步发送通知

邮件发送可能增加接口响应时间,因此示例中的通知实现了 ShouldQueue:

class OrderCreatedNotification extends Notification implements ShouldQueue
{
    use Queueable;
}

项目需要配置可用的异步队列,例如:

QUEUE_CONNECTION=database

如果使用数据库队列,需要创建任务表:

php artisan make:queue-table
php artisan migrate

然后启动队列工作进程:

php artisan queue:work

订单创建后,通知会进入队列,由工作进程负责发送。

生产环境应使用 Supervisor、Horizon 或其他进程管理工具保持队列工作进程运行。邮件发送失败时,还应根据业务设置重试次数和失败任务处理方式。

八、读取未读站内通知

在控制器中读取当前用户的未读通知:

public function index()
{
    $notifications = request()
        ->user()
        ->unreadNotifications()
        ->latest()
        ->paginate(20);

return response()->json($notifications);
}

在 Blade 模板中显示:

@foreach (auth()->user()->unreadNotifications as $notification)
    <div>
        {{ $notification->data['message'] ?? '你有一条新通知' }}
    </div>
@endforeach

通知数据来自 toDatabase() 返回的数组。读取不存在的键时,可以使用 ?? 设置默认内容。

九、标记通知为已读

标记一条通知为已读:

$notification = auth()
    ->user()
    ->notifications()
    ->findOrFail($notificationId);

$notification->markAsRead();

全部标记为已读:

auth()
    ->user()
    ->unreadNotifications
    ->markAsRead();

也可以直接更新未读通知:

auth()
    ->user()
    ->unreadNotifications()
    ->update([
        'read_at' => now(),
    ]);

操作通知时,应确保查询范围属于当前登录用户,避免通过修改通知 ID 访问其他用户的通知。

十、删除通知

删除单条通知:

auth()
    ->user()
    ->notifications()
    ->whereKey($notificationId)
    ->delete();

删除当前用户的全部通知:

auth()
    ->user()
    ->notifications()
    ->delete();

通知删除属于不可逆操作,后台系统可以先提供“标记为已读”功能,再根据保留策略定期清理旧通知。

十一、使用数据库通知模板

如果不希望直接在 Blade 中读取数组,可以在通知中提供更明确的字段:

public function toDatabase(object $notifiable): array
{
    return [
        'type' => 'order_created',
        'title' => '订单创建成功',
        'message' => "订单 {$this->order->id} 已创建成功",
        'url' => url("/orders/{$this->order->id}"),
    ];
}

模板中调用:

@foreach (auth()->user()->unreadNotifications as $notification)
    <a href="{{ $notification->data['url'] }}">
        <strong>{{ $notification->data['title'] }}</strong>
        <span>{{ $notification->data['message'] }}</span>
    </a>
@endforeach

通知数据结构确定后,应避免随意修改字段名称,否则前端模板可能无法正常展示。

十二、测试通知发送

Laravel 提供 Notification::fake() 模拟通知:

use Illuminate\Support\Facades\Notification;

Notification::fake();

$user->notify(
new OrderCreatedNotification($order)
);

Notification::assertSentTo(
$user,
OrderCreatedNotification::class
);

如果需要确认通知使用了正确渠道:

Notification::assertSentTo(
    $user,
    OrderCreatedNotification::class,
    function ($notification, $channels) {
        return in_array('mail', $channels, true)
            && in_array('database', $channels, true);
    }
);

测试通知时,使用模拟方法可以避免实际发送邮件或写入真实通知数据。

十三、常见问题

通知没有发送邮件怎么办?

先检查 .env 中的邮件配置,再执行:

php artisan config:clear

如果通知实现了 ShouldQueue,还要确认队列工作进程正在运行:

php artisan queue:work

同时检查失败任务:

php artisan queue:failed

为什么数据库中没有通知记录?

确认通知的 via() 方法包含 database,并且已经执行:

php artisan notifications:table
php artisan migrate

如果通知使用队列,还需要等待队列工作进程处理任务。

邮件和站内通知可以使用不同内容吗?

可以。分别在 toMail() 和 toDatabase() 中定义内容即可。邮件适合较完整的说明,站内通知可以只保留标题、摘要和跳转地址。

通知中的订单信息被重复处理怎么办?

队列通知可能因为失败重试而执行多次。涉及积分、库存或外部扣款时,应为业务操作设计幂等机制,避免重复产生结果。

广告合作

广告合作

QQ群号:4114653

QQ群号:4114653

温馨提示:
  1. 本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。邮箱:2942802716#qq.com(#改为@)。
  2. 本站原创内容未经允许不得转载,转载请注明出处“站长百科”和原文地址。