定义事件
php artisan make:event TestEvent
生成的文件在 app\Events\TestEvent.php
内容如下
<?php
namespace App\Events;
use App\Models\Test;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class TestEvent {
use Dispatchable, InteractsWithSockets, SerializesModels;
public $test;
/**
* Create a new event instance.
*
* @return void
*/
public function __construct(Test $test) {
$this->test = $test;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn() {
return new PrivateChannel('channel-name');
}
}
app\Listeners\TestEvent.php
文件内容
<?php
/**
* Created by PhpStorm.
* User: 舒孝元
* Date: 2021/3/24 14:53
* Email: sxy@shuxiaoyuan.com
*/
namespace App\Listeners;
use App\Events\TestEvent as TestEvents;
// 事件监听器队列,如果是耗时较长的任务,可以仍在队列里面执行,只需要 implements ShouldQueue,事件调度器会自动使用 Laravel 的队列系统自动排队
use Illuminate\Contracts\Queue\ShouldQueue;
// 如果你需要手动访问监听器下面队列任务的 delete 和 release 方法,你可以通过使用 Illuminate\Queue\InteractsWithQueue trait 来实现
use Illuminate\Queue\InteractsWithQueue;
class TestEvent implements ShouldQueue {
use InteractsWithQueue;
/**
* 任务连接
* 在config/queue.php配置中
*
* @var string|null
*/
public $connection = 'redis';
/**
* 任务发送到的队列的名称
* php artisan queue:work --queue=listeners
*
* @var string|null
*/
public $queue = 'listeners';
/**
* 处理任务的延迟时间,单位:秒
*
* @var int
*/
public $delay = 10;
/**
* 创建事件监听器
*
* TestEvent constructor.
* @return void
*/
public function __construct() {
}
/**
* 处理事件逻辑
*
* @param TestEvents $event
* @return void
*/
public function handle(TestEvents $event) {
$test = $event->test;
$test->reserve_field1 = time() . ':这个是事件处理的,App\Events\TestEvent、App\Listeners\TestEvent';
$test->reserve_field3 = time();
$test->sex = time(); // 设置一个肯定失败的
$test->save();
}
/**
* 确定监听器是否应加入队列
*
* @param TestEvents $event
* @return bool
*/
public function shouldQueue(TestEvents $event) {
// 只有 ID 小于等于 10 的才加入队列
return $test = $event->test->id <= 10;
}
/**
* 处理失败任务
*
* @param TestEvents $event
* @param \Exception $exception
* @return void
*/
public function failed(TestEvents $event, $exception) {
// 处理你的失败逻辑
dd(123321);
}
}
分发事件
event()
辅助函数可以全局使用,你可以在应用中的任何位置进行调用
private function test(Request $request) {
$id = $request->input('id', 1);
$test = Test::find($id);
$test->openid = str_random(23);
$test->reserve_field2 = time();
$test->save();
event(new TestEvent($test));
return $test;
}
这是测试处理队列失败的情况
模型触发事件
允许你在模型生命周期中的多个时间点调用如下这些方法: 事件允许你在一个指定模型类每次保存或更新的时候执行代码
事件 | 说明 |
---|---|
retrieved | 获取到模型示例后触发 |
creating | 插入到数据库前触发 |
created | 插入到数据库后触发 |
updating | 更新到数据库前触发 |
updated | 更新到数据库后触发 |
saving | 保存到数据库前触发(插入、更新之前,无论插入还是更新都会触发) |
saved | 保存到数据库后触发(插入、更新之后,无论插入还是更新都会触发) |
deleting | 从数据库删除记录前触发 |
deleted | 从数据库删除记录后触发 |
restoring | 恢复软删除记录前触发 |
restored | 恢复软删除记录后触发 |
注:批量更新时不会触发相应事件,因为是直接走查询构建器完成的,绕过了模型方法
一、通过静态方法监听模型事件(不推荐)
直接在 app\Providers\EventServiceProvider.php
文件的 boot
方法中加入你需要监听的事件
//插入到数据库后的操作
public function boot() {
parent::boot();
//闭包函数传入参数是模型实例
User::created(function ($user){
Log::info('插入到数据库后实现的方法',$user->toArray());
});
}
//插入到数据库前的操作,可以更改插入的值
public function boot() {
parent::boot();
User::creating(function ($user) {
$user->password = 'shuxiaoyuan';
Log::info('插入到数据库前实现的方法', $user->toArray());
});
}
二、通过订阅者监听模型事件
创建相应事件类
创建一个 Test
表获取到模型示例后触发的事件:php artisan make:event TestRetrieved
文件路径:app\Events\TestRetrieved.php
代码如下:
<?php
/**
* 事件订阅者,这里以 Test 模型事件为例
* 需要在 Test 模型中建立模型事件与自定义事件类的映射
*
* User: 舒孝元
* Date: 2021/3/24 20:43
* Email: sxy@shuxiaoyuan.com
*/
namespace App\Events;
use App\Models\Test;
class TestRetrieved {
public $test;
public function __construct(Test $test) {
$this->test = $test;
}
}
在模型中建立映射关系
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Events\TestRetrieved;
class Test extends Model {
protected $table = 'test1';
protected $guarded = [];
// 建立模型事件与自定义事件类的映射
protected $dispatchesEvents = [
'retrieved' => TestRetrieved::class,
];
}
创建订阅者监听事件类
可以直接在 app\Providers\EventServiceProvider.php
文件的 listen
属性中为每个事件绑定对应的监听器类,不过一般不建议这样做,我们在 App
目录下创建文件夹 Listeners
在文件夹中新建文件 TestEventSubscribe.php
代码如下:
<?php
/**
* 事件订阅者,这里以 Test 模型事件为例
*
* User: 舒孝元
* Date: 2021/3/24 20:16
* Email: sxy@shuxiaoyuan.com
*/
namespace App\Listeners;
use Illuminate\Support\Facades\Log;
use App\Events\TestRetrieved;
class TestEventSubscribe {
/**
* retrieved:获取到模型实例后触发
*/
public function onTestRetrieved($event) {
// 处理你的逻辑,这里我们就简单写入一下日志
Log::info('事件订阅者,获取到模型实例后触发:', ['info' => $event]);
}
/**
* creating:插入到数据库前触发
*/
public function onTestCreating($event) {
}
/**
* 为订阅者注册监听器
*/
public function subscribe($events) {
$events->listen(TestRetrieved::class, TestEventSubscribe::class . '@onTestRetrieved');
// $events->listen(TestCreating::class, TestEventSubscribe::class . '@onTestCreating');
}
}
注册订阅者
在 app\Providers\EventServiceProvider.php
中注册订阅者
代码如下:
<?php
namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
class EventServiceProvider extends ServiceProvider {
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot() {
parent::boot();
}
/**
* 确定是否应自动发现事件和侦听器。
* 默认是禁止的,若开启,可以在部署时使用 event:cache 缓存所有事件
* event:clear 清除缓存的事件
*
* @return bool
*/
public function shouldDiscoverEvents() {
return true;
}
/**
* 获取应该用于发现事件的监听器目录
* 默认是 Listeners 文件夹
* @return array
*/
protected function discoverEventsWithin() {
return [
$this->app->path('Listeners'),
];
}
/**
* 注册订阅者
*
* @var array
*/
protected $subscribe = [
'App\Listeners\TestEventSubscribe',
];
}
到此设置完毕,我们去验证一下,随便搞个控制器方法去触发获取模型事件
/**
* 测试模型事件订阅者
*
* @param Request $request
*/
private function subscribe(Request $request) {
$id = $request->input('id', 1);
$test = Test::find($id);
return $test;
}
日志记录如下:
三、通过观察者监听模型事件
创建观察者
在 App
下新建目录 Observers
,在该目录下新建观察者类 TestObserver
代码如下:
<?php
/**
* 观察者监听模型事件
*
* User: 舒孝元
* Date: 2021/3/25 11:54
* Email: sxy@shuxiaoyuan.com
*/
namespace App\Observers;
use App\Models\Test;
use Illuminate\Support\Facades\Log;
class TestObserver {
/**
* 插入到数据库后触发
*
* @param Test $test
*/
public function created(Test $test) {
// 返回一个数据库不存在的字段
$test->reserve_field4 = '这个字段是插入到数据库后给更新掉了';
$test->save();
$test->observe = '这是数据插入到数据库后触发的观察者模型事件';
}
/**
* 插入到数据库前触发
*
* @param Test $test
*/
public function creating(Test $test) {
$test->reserve_field3 = '123';
}
}
注册相应的观察者
在 app\Providers\EventServiceProvider.php
中注册订阅者
public function boot() {
parent::boot();
// 注册 Test 模型的观察者
Test::observe(TestObserver::class);
}
到此设置完毕,我们去验证一下,随便搞个控制器方法去触发模型事件
/**
* 观察者监听模型事件
*
* @param Request $request
* @return mixed
*/
public function observe(Request $request) {
$data = [
'openid' => str_random(23),
'unionid' => str_random(23),
'nickname' => '舒孝元',
'mobile' => '13311939117',
'sex' => 1,
'reserve_field1' => '测试观察者监听模型事件',
'reserve_field2' => '在入库前,将 123 写入 reserve_field3 表',
];
$test_info = Test::create($data);
return $test_info;
}
数据库中记录如下:
总结 : 如果只是监听一两个模型事件,通过静态方法监听方式比较合适;如果仅仅监听系统支持的模型事件,并且要监听多个模型的多个事件,观察者是最佳选择;如果还要在模型类上监听更多系统模型事件之外的自定义事件,则使用订阅者来监听比较合适。