PHP

使用Laravel事件系统

是滑稽啊
2024-05-17 / 0 评论 / 10 阅读 / 正在检测是否收录...

比如在用户注册时候需要一些额外的操作。

使用laravel中的事件系统去创建事件和监控器去实现便于开发和维护。

文档介绍 https://learnku.com/docs/laravel/8.x/events/9391#01e998

创建事件和监控器

#指令创建事件
php artisan make:event UserRegistered
#指令创建监控器
php artisan make:listener CreateUserDatabase --event=UserRegistered

事件案例代码:

<?php
namespace App\Events;

class UserRegistered
{
    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(public int $userId)
    {
        //Log::debug('UserRegistered注册事件');
    }
}

监控案例代码:

<?php
namespace App\Listeners;

use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;

class CreateUserDatabase implements ShouldQueue
{

    /**
     * 任务将被发送到的队列的名称
     *
     * @var string|null
     */
    public $queue = 'listeners';

    /**
     * Create the event listener.
     *
     * @return void
     */
    // public function __construct(){}

    /**
     * Handle the event.
     *
     * @param  \App\Events\UserRegistered  $event
     * @return void
     */
    public function handle(UserRegistered $event)
    {

        Log::debug('触发了用户注册事件 USER_ID: ' . $event->userId);

        DB::table('users')->where('id', $event->userId)->update(['deleted_at' => time()]);

        try {
            DB::beginTransaction();

            #创建新用户专属数据库
            // DB::select('select * from users where active = ?', [1]); #执行sql原生操作
            #创建上报字段信息表
            DB::commit();

        } catch (\Exception $e) {
            DB::rollBack();
        }

    }

    /**
     * 获取监听器队列的名称
     *
     * @return string
     */
    // public function viaQueue()
    // {
    //     return $this->queue;
    // }
}

配置事件与监听器的绑定

打开 app/Providers/EventServiceProvider.php 文件。

protected $listen = [
    UserRegistered::class => [
        CreateUserDatabase::class,
    ],
];

使用事件

在想要触发的位置去进行触发事件的监听

use App\Events\UserRegistered;

//注册
public function register(Request $request)
{
    $input = $request->only('account_name', 'password');
    event(new UserRegistered(2)); #2为传递的用户ID
    return $this->services->register($input);
}

事件执行方式

事件执行有两种方式

方式一,同步触发(同步执行)

这是最直接的触发方式,事件在触发后,相应的监听器会立即执行,执行过程与触发事件的代码在同一请求生命周期内完成。
同步触发适用于那些执行速度较快、不影响用户体验的操作。
同步触发事件的典型代码示例如下:

use App\Events\SomeEvent;

// 触发事件
event(new SomeEvent($data));

方式二,异步触发(队列方式)

对于耗时较长的事件处理逻辑,如发送邮件、短信通知、复杂的数据处理等,
推荐使用异步触发方式,以避免阻塞用户的请求响应。
Laravel 提供了队列(Queue)系统来实现异步处理。
异步触发事件通常有两种模式:

  1. 使用 Queueable 特性

在事件类中使用 Illuminate\Queue\InteractsWithQueue 特性,并实现
shouldQueue 方法,使事件变为可队列化的。然后,当触发该事件时,Laravel 会自动将其推送到队列中异步处理。

use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class SomeEvent implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue;

    // ...构造函数和属性...

    public function handle()
    {
        // 事件处理逻辑
    }
}
  1. 在监听器中使用队列

即使事件本身没有实现 ShouldQueue 接口,也可以在监听器的 handle 方法中将耗时操作放入队列。
这要求监听器实现 ShouldQueue 接口,并配置监听器为异步处理。

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;

class SomeEventListener implements ShouldQueue
{
    use InteractsWithQueue;

    public function handle(SomeEvent $event)
    {
        // 将耗时操作放入队列处理
        SomeLongRunningJob::dispatch($event->data);
    }
}

触发队列任务

在上述异步处理方式中,提到的 SomeLongRunningJob 是一个队列任务的例子。
你可以使用 Laravel 的任务调度器(Jobs)配合队列来进一步处理复杂的异步逻辑。
创建队列任务并将其加入队列的代码示例如下:

use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\Jobs\Job;
use Illuminate\Contracts\Queue\ShouldQueue;

class SomeLongRunningJob implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, SerializesModels;

    public $data;

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

    public function handle()
    {
        // 执行耗时任务
    }
}

// 触发队列任务
SomeLongRunningJob::dispatch($data);

总结

Laravel 提供了灵活的事件触发机制,无论是同步还是异步处理,都能通过简洁的API实现复杂逻辑的解耦和高效执行。

0

评论

博主关闭了所有页面的评论