27、Laravel 邮件、通知、队列及作业的深度解析

Laravel 邮件、通知、队列及作业的深度解析

1. 邮件配置与通知系统

在使用邮件功能时,若要为每条消息设置 “通用收件人” 配置,可在 config/mail.php 文件中添加 to 键,示例如下:

'to' => [
    'address' => 'matt@mattstauffer.co',
    'name' => 'Matt Testing My Application'
],

需注意,要使用此功能,需实际设置一个真实的邮件驱动,如 Mailgun 或 Sendmail。

大多数 Web 应用发送邮件的目的是通知用户特定操作已发生或需要发生。随着用户通信偏好日益多样化,我们会使用更多不同的工具通过 Slack、SMS 等方式进行通信。

Laravel 5.3 引入了通知的概念。通知是一个 PHP 类,代表你可能想发送给用户的单个通信。以健身应用为例,当有新的锻炼计划可用时,可通知用户。

创建通知的命令为:

php artisan make:notification WorkoutAvailable

生成的通知类示例如下:

<?php
namespace App\Notifications;

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

class WorkoutAvailable extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        return (new MailMessage)
                    ->line('The introduction to the notification.')
                    ->action('Notification Action', 'https://laravel.com')
                    ->line('Thank you for using our application!');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

从这个示例中可以学到以下几点:
- 要将相关数据传递给构造函数。
- via() 方法可定义针对给定用户使用的通知渠道( $notifiable 代表系统中要通知的实体,多数情况下是用户,但并非总是如此)。
- 每个通知渠道都有单独的方法,可具体定义如何通过该渠道发送通知。

$notifiable 不一定是用户,可能是 “教练”、“学员”、“组”、“公司” 或 “服务器” 等。

修改后的 WorkoutAvailable 通知类示例如下:

class WorkoutAvailable extends Notification
{
    use Queueable;
    public $workout;

    public function __construct($workout)
    {
        $this->workout = $workout;
    }

    public function via($notifiable)
    {
        // This method doesn't exist on the User... we're going to make it up
        return $notifiable->preferredNotificationChannels();
    }

    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->line('You have a new workout available!')
            ->action('Check it out now', route('workout', [$this->workout]))
            ->line('Thank you for training with us!');
    }

    public function toArray($notifiable)
    {
        return [];
    }
}
2. 定义 via() 方法

定义 via() 方法时,有以下几种方式:
- 简单地将所有通知都通过邮件或 SMS 发送,示例如下:

public function via($notifiable)
{
    return 'nexmo';
}
  • 让每个用户选择其首选的通知方式,并将其保存在用户模型中,示例如下:
public function via($notifiable)
{
    return $notifiable->preferred_notification_channel;
}
  • 为每个可通知实体创建一个方法,实现复杂的通知逻辑,如在工作时间通过某些渠道通知用户,晚上通过其他渠道通知。
3. 发送通知

发送通知有两种方式:
- 使用 Notifiable 特性:任何导入 Laravel\Notifications\Notifiable 特性( App\User 类默认导入)的模型都有 notify() 方法,示例如下:

use App\Notifications\WorkoutAvailable;
...
$user->notify(new WorkoutAvailable($workout));
  • 使用 Notification 门面:虽然这种方法较繁琐,因为需要同时传递可通知实体和通知,但可以同时传递多个可通知实体,示例如下:
use App\Notifications\WorkoutAvailable;
...
Notification::send($users, new WorkoutAvailable($workout));
4. 排队通知

大多数通知驱动在发送通知时需要发送 HTTP 请求,这可能会减慢用户体验,因此建议对通知进行排队。所有通知默认导入 Queueable 特性,只需在通知类中添加 implements ShouldQueue ,Laravel 会立即将其移动到队列中。

若要延迟通知的发送,可使用 delay() 方法,示例如下:

$delayUntil = Carbon::now()->addMinutes(15);
$user->notify((new WorkoutAvailable($workout))->delay($delayUntil));
5. 开箱即用的通知类型

Laravel 自带了用于电子邮件、数据库、广播、Nexmo SMS 和 Slack 的通知驱动。
- 电子邮件通知 :示例邮件构建代码如下:

public function toMail($notifiable)
{
    return (new MailMessage)
        ->line('You have a new workout available!')
        ->action('Check it out now', route('workout', [$this->workout]))
        ->line('Thank you for training with us!');
}

邮件通知系统会将应用程序的名称放在邮件标题中,可在 config/app.php name 键中自定义该名称。邮件默认发送到可通知实体的 email 属性,可通过在可通知类中添加 routeNotificationForMail() 方法来自定义。邮件主题通过解析通知类名并转换为单词来设置,也可通过 subject() 方法自定义。若要修改模板,可使用以下命令发布并编辑:

php artisan vendor:publish --tag=laravel-notifications

还可通过在 toMail() 方法的 MailMessage 调用链中添加 error() 方法将默认模板样式更改为 “错误” 消息。

  • 数据库通知 :使用数据库通知渠道将通知发送到数据库表。步骤如下:
    1. 使用 php artisan notifications:table 创建表。
    2. 在通知类中创建 toDatabase() 方法并返回一个数据数组,该数据将以 JSON 格式编码并存储在数据库表的 data 列中。

Notifiable 特性为导入它的任何模型添加了 notifications 关系,可轻松访问通知表中的记录。可筛选未读通知,也可将一个或所有通知标记为已读。

  • 广播通知 :广播渠道使用 Laravel 的事件广播功能(Echo)发送通知。在通知类中创建 toBroadcast() 方法并返回一个数据数组,若应用程序正确配置了事件广播,该数据将在名为 {notifiable}.{id} 的私有频道上广播。

  • SMS 通知 :SMS 通知通过 Nexmo 发送,若要发送 SMS 通知,需注册 Nexmo 账户并按照文档说明操作,在通知类中设置 toNexmo() 方法并自定义 SMS 消息。

  • Slack 通知 :Slack 通知渠道允许自定义通知的外观,甚至可以附加文件,在通知类中设置 toSlack() 方法并自定义消息。

6. 测试
  • 邮件测试 :有两种测试邮件的方法。若使用传统邮件语法,推荐使用 MailThief 工具,通过 Composer 引入后,在测试中使用 MailThief::hijack() 捕获对 Mail 门面或任何邮件类的调用,可对发件人、收件人、CC 和 BCC 值以及邮件内容和附件进行断言。若使用邮件类,可使用简单的语法对发送的邮件进行断言,示例如下:
public function test_signup_triggers_welcome_email()
{
    ...
    Mail::assertSent(WelcomeEmail::class, function ($e) {
        return $e->subject == 'Welcome!';
    });
    // You can also use assertSentTo() to explicitly test the recipients
}
  • 通知测试 :Laravel 提供了一组内置的断言来测试通知,示例如下:
public function test_new_signups_triggers_admin_notification()
{
    ...
    Notification::assertSentTo($user, NewUsersSignedup::class,
        function ($n, $channels) {
            return $n->user->email == 'user-who-signed-up@gmail.com'
            && $channels == ['mail'];
    });
    // You can also use assertNotSentTo()
}
7. 队列

队列就像在银行排队,即使有多个队列,每个队列一次也只服务一个人,每个人最终都会到达队列前端并得到服务。在编程中,应用程序将 “作业” 添加到队列中,由 “队列工作器” 负责一次从队列中取出一个作业并执行相应的操作。

Laravel 支持使用 Redis、beanstalkd、Amazon 的 SQS(简单队列服务)或数据库表来处理队列。也可选择 sync 驱动使作业直接在应用程序中运行而不排队,或选择 null 驱动丢弃作业,这两种驱动通常用于本地开发或测试环境。

使用队列的原因如下:
- 可将耗时或缓慢的过程从任何同步调用中移除,如发送邮件。
- 可将一些复杂的任务(如 cron 作业或 webhook)的各个部分排队,让队列工作器逐个处理。
- 当服务器无法处理大量处理任务时,可启动多个队列工作器更快地处理队列。

8. 基本队列配置

队列有自己的专用配置文件 config/queue.php ,可设置多个驱动并定义默认驱动,还可在此存储 SQS、Redis 或 beanstalkd 的认证信息。

在 Laravel Forge 上使用 beanstalkd 队列很简单,每个创建的服务器都自动配置了 beanstalkd,只需访问任何站点的 Forge 控制台,转到 “Queue Workers” 选项卡并点击 “Start Worker” 即可使用 beanstalkd 作为队列驱动。

9. 排队作业

在 Laravel 中,作业是一个包含作业名称、数据有效负载、已尝试处理该作业的次数和其他简单元数据的信息集合。

创建作业的命令为:

php artisan make:job CrunchReports

生成的作业类示例如下:

<?php
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class CrunchReports implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        //
    }
}

该模板导入了 Queueable InteractsWithQueue SerializesModels 特性,并实现了 ShouldQueue 接口。提供了构造函数和 handle() 方法,构造函数用于附加数据到作业, handle() 方法是作业逻辑的所在位置。

SerializesModels 特性使作业能够序列化注入的模型,但只序列化 Eloquent 对象的主键,在作业处理时从数据库中根据主键获取新鲜的 Eloquent 模型。

填充后的示例作业类如下:

use App\ReportGenerator;
use Illuminate\Log\Writer as Logger;

class CrunchReports implements ShouldQueue
{
    use InteractsWithQueue, SerializesModels;
    protected $user;

    public function __construct($user)
    {
        $this->user = $user;
    }

    public function handle(ReportGenerator $generator, Logger $logger)
    {
        $generator->generateReportsForUser($this->user);
        $logger->info('Generated reports.');
    }
}

将作业推送到队列有两种主要方式:
- 使用全局 dispatch() 辅助函数。
- 使用 DispatchesJobs 特性提供的方法,该特性默认在每个控制器中导入。

可通过 onConnection() 方法自定义作业的连接,还可控制队列和延迟设置。

总结

Laravel 的邮件和通知功能为各种消息传递系统提供了简单、一致的接口。邮件系统使用 “邮件类” 为不同的邮件驱动提供一致的语法,通知系统使构建可通过多种不同媒体传递的单个通知变得容易。队列和作业功能则帮助处理耗时或复杂的任务,提高应用程序的性能和响应能力。

Laravel 邮件、通知、队列及作业的深度解析

10. 作业调度

Laravel 的调度器让传统的 cron 成为过去式。通过调度器,你可以在 Laravel 应用中轻松地定义和管理定时任务,而无需直接操作服务器的 cron 表。

以下是一个简单的调度示例,假设你需要每天凌晨 2 点执行一次清理任务:

// 在 app/Console/Kernel.php 文件中
protected function schedule(Schedule $schedule)
{
    $schedule->call(function () {
        // 执行清理任务的代码
        DB::table('old_records')->delete();
    })->dailyAt('02:00');
}

在上述代码中, $schedule->call() 方法用于指定要执行的任务, dailyAt('02:00') 则指定了任务执行的时间。

除了 dailyAt 方法,Laravel 调度器还提供了许多其他的调度频率方法,如下表所示:
| 方法 | 描述 |
| ---- | ---- |
| everyMinute() | 每分钟执行一次 |
| everyFiveMinutes() | 每五分钟执行一次 |
| everyTenMinutes() | 每十分钟执行一次 |
| everyFifteenMinutes() | 每十五分钟执行一次 |
| everyThirtyMinutes() | 每三十分钟执行一次 |
| hourly() | 每小时执行一次 |
| hourlyAt(17) | 每小时的第 17 分钟执行一次 |
| daily() | 每天凌晨 0 点执行一次 |
| dailyAt('13:00') | 每天下午 1 点执行一次 |
| twiceDaily(1, 13) | 每天凌晨 1 点和下午 1 点执行一次 |
| weekly() | 每周周日凌晨 0 点执行一次 |
| monthly() | 每月 1 号凌晨 0 点执行一次 |
| monthlyOn(4, '15:00') | 每月 4 号下午 3 点执行一次 |
| quarterly() | 每季度第一天凌晨 0 点执行一次 |
| yearly() | 每年 1 月 1 号凌晨 0 点执行一次 |

同时,调度器还支持链式调用,例如可以结合 when() 方法根据条件来决定是否执行任务:

$schedule->call(function () {
    // 执行任务的代码
})->daily()->when(function () {
    return DB::table('conditions')->where('status', 'active')->exists();
});

在上述代码中,只有当 conditions 表中存在 status active 的记录时,任务才会执行。

11. 事件与监听器

Laravel 的事件和监听器系统提供了一种强大的方式来解耦应用程序的各个部分。事件是应用程序中发生的特定动作或状态变化的抽象表示,而监听器则是对这些事件做出响应的代码块。

以下是创建事件和监听器的步骤:
1. 创建事件 :使用 Artisan 命令创建一个事件类:

php artisan make:event UserRegistered

生成的事件类示例如下:

<?php
namespace App\Events;

use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class UserRegistered
{
    use Dispatchable, SerializesModels;

    public $user;

    public function __construct($user)
    {
        $this->user = $user;
    }
}

在上述代码中, __construct 方法接收一个 $user 参数,并将其赋值给类的属性,以便在监听器中使用。

  1. 创建监听器 :使用 Artisan 命令创建一个监听器类:
php artisan make:listener SendWelcomeEmail --event=UserRegistered

生成的监听器类示例如下:

<?php
namespace App\Listeners;

use App\Events\UserRegistered;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendWelcomeEmail
{
    use InteractsWithQueue;

    public function handle(UserRegistered $event)
    {
        // 发送欢迎邮件的代码
        Mail::to($event->user->email)->send(new WelcomeEmail($event->user));
    }
}

在上述代码中, handle 方法接收一个 UserRegistered 事件实例,并在方法内部执行发送欢迎邮件的操作。

  1. 注册事件和监听器 :在 app/Providers/EventServiceProvider.php 文件中注册事件和监听器的映射关系:
protected $listen = [
    UserRegistered::class => [
        SendWelcomeEmail::class,
    ],
];

在上述代码中, $listen 数组定义了事件和监听器的映射关系,当 UserRegistered 事件触发时, SendWelcomeEmail 监听器将被执行。

12. 事件广播

事件广播允许你将服务器端的事件实时推送到客户端,通常用于实现实时更新、聊天系统等功能。Laravel 提供了内置的事件广播支持,结合前端的 Laravel Echo 库,可以轻松实现这一功能。

以下是一个简单的事件广播示例:
1. 配置广播驱动 :在 config/broadcasting.php 文件中配置广播驱动,例如使用 Pusher:

'connections' => [
    'pusher' => [
        'driver' => 'pusher',
        'key' => env('PUSHER_APP_KEY'),
        'secret' => env('PUSHER_APP_SECRET'),
        'app_id' => env('PUSHER_APP_ID'),
        'options' => [
            'cluster' => env('PUSHER_APP_CLUSTER'),
            'useTLS' => true,
        ],
    ],
],
  1. 创建可广播事件 :使用 Artisan 命令创建一个可广播事件类:
php artisan make:event NewMessage --broadcast

生成的可广播事件类示例如下:

<?php
namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class NewMessage implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message;

    public function __construct($message)
    {
        $this->message = $message;
    }

    public function broadcastOn()
    {
        return new PrivateChannel('chat.' . $this->message->chat_id);
    }
}

在上述代码中, broadcastOn 方法指定了事件将在哪个频道上广播。

  1. 触发事件 :在控制器或其他地方触发事件:
event(new NewMessage($message));
  1. 前端监听事件 :在前端使用 Laravel Echo 监听事件:
import Echo from 'laravel-echo';

window.Pusher = require('pusher-js');

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    forceTLS: true
});

window.Echo.private('chat.1')
    .listen('NewMessage', (e) => {
        console.log(e.message);
    });

在上述代码中,前端监听 chat.1 频道上的 NewMessage 事件,并在事件触发时打印消息。

总结

Laravel 提供了丰富的工具和功能来处理邮件、通知、队列、作业调度、事件和广播等任务。通过合理使用这些功能,你可以构建出高效、可扩展的 Web 应用程序。邮件和通知系统让你能够方便地与用户进行沟通,队列和作业调度则帮助你处理耗时或复杂的任务,事件和广播系统则提供了强大的解耦和实时通信能力。掌握这些技术,将大大提升你的 Laravel 开发能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值