本文目录导读:

在ThinkPHP 6/8中,事件与监听器的绑定主要有以下几种方式,我会详细讲解并给出代码示例:
事件定义文件方式(推荐)
创建事件定义文件
在 app 目录下的 event.php 文件中定义:
<?php
// app/event.php
return [
// 事件绑定
'bind' => [
'UserLogin' => 'app\event\UserLogin',
'OrderCreate' => 'app\event\OrderCreate',
],
// 事件监听
'listen' => [
'app\event\UserLogin' => [
'app\listener\UserLoginListener',
'app\listener\UserLoginLog',
],
'app\event\OrderCreate' => [
'app\listener\OrderCreateListener',
'app\listener\SendSms',
'app\listener\SendEmail',
],
],
// 事件订阅
'subscribe' => [
'app\subscribe\UserSubscribe',
'app\subscribe\OrderSubscribe',
],
];
创建事件类
<?php
// app/event/UserLogin.php
namespace app\event;
class UserLogin
{
// 用户信息
public $user;
// 登录时间
public $loginTime;
// 登录IP
public $ip;
public function __construct($user, $loginTime, $ip)
{
$this->user = $user;
$this->loginTime = $loginTime;
$this->ip = $ip;
}
}
<?php
// app/event/OrderCreate.php
namespace app\event;
class OrderCreate
{
public $orderId;
public $orderData;
public function __construct($orderId, $orderData = [])
{
$this->orderId = $orderId;
$this->orderData = $orderData;
}
}
创建监听器
<?php
// app/listener/UserLoginListener.php
namespace app\listener;
use app\event\UserLogin;
class UserLoginListener
{
/**
* 事件监听处理
* @param UserLogin $event 事件对象
*/
public function handle(UserLogin $event)
{
// 更新用户最后登录时间
$user = $event->user;
$user->last_login_time = $event->loginTime;
$user->last_login_ip = $event->ip;
$user->save();
// 记录日志
trace("用户 {$user->username} 在 {$event->loginTime} 登录成功");
}
}
<?php
// app/listener/UserLoginLog.php
namespace app\listener;
use app\event\UserLogin;
class UserLoginLog
{
public function handle(UserLogin $event)
{
$data = [
'user_id' => $event->user->id,
'login_time' => $event->loginTime,
'ip' => $event->ip,
'agent' => request()->header('user-agent')
];
// 写入登录日志表
Db::name('login_log')->insert($data);
}
}
监听器优先级配置
<?php
// app/listener/UserLoginListener.php
namespace app\listener;
use app\event\UserLogin;
class UserLoginListener
{
/**
* 监听器优先级(数值越大优先级越高)
*/
public $priority = 10;
public function handle(UserLogin $event)
{
// 处理逻辑
}
}
创建订阅器
<?php
// app/subscribe/UserSubscribe.php
namespace app\subscribe;
use think\Event;
class UserSubscribe
{
public function onUserLogin($event)
{
// 处理用户登录事件
echo '用户登录了';
}
public function onUserLogout($event)
{
// 处理用户退出事件
echo '用户退出了';
}
/**
* 注册订阅者
*/
public function subscribe(Event $event)
{
$event->listen('UserLogin', [$this, 'onUserLogin']);
$event->listen('UserLogout', [$this, 'onUserLogout']);
}
}
动态绑定事件监听器
<?php
namespace app\controller;
use think\facade\Event;
class User
{
public function login()
{
// ...登录逻辑...
// 方式1:动态绑定事件监听器
Event::listen('UserLogin', function ($event) {
// 即时监听
echo $event->user->username . ' 登录了';
});
// 方式2:动态绑定多个监听器
Event::listen('UserLogin', [
'app\listener\SendWelcomeEmail',
function ($event) {
// 匿名函数监听
}
]);
// 方式3:绑定事件类
Event::listen('app\event\UserLogin', 'app\listener\UserLoginListener');
// 触发事件
$event = new UserLogin($user, time(), request()->ip());
Event::trigger('UserLogin', $event);
}
}
在控制器中使用事件
<?php
namespace app\controller;
use think\facade\Event;
use app\event\UserLogin;
use app\event\OrderCreate;
use app\model\User;
use app\model\Order;
class Test
{
// 触发用户登录事件
public function login()
{
$user = User::find(1);
// 创建事件并触发
$event = new UserLogin($user, date('Y-m-d H:i:s'), request()->ip());
Event::trigger($event); // 多态形式触发
return '登录成功';
}
// 触发订单创建事件
public function createOrder()
{
$orderId = 10086;
$orderData = [
'user_id' => 1,
'amount' => 999.00,
'status' => 'pending'
];
// 触发事件
Event::trigger('OrderCreate', $orderData); // 简单形式
// 或
Event::trigger(new OrderCreate($orderId, $orderData)); // 多态形式
return '订单创建成功';
}
// 异步事件处理
public function asyncEvent()
{
// 使用异步事件
Event::async('UserLogin', ['user_id' => 1], 3600); // 延迟1小时执行
}
// 批量触发事件
public function batchEvent()
{
$events = [
['UserLogin', ['user_id' => 1]],
['OrderCreate', ['order_id' => 10086]],
];
Event::triggerBatch($events); // 批量触发
}
}
命令行模式绑定
<?php
// config/event.php
return [
// 命令行模式定义
'command' => [
'UserLogin' => ['app\listener\UserLoginListener'],
'OrderCreate' => ['app\listener\OrderCreateListener'],
],
];
事件监听器的完整示例
<?php
// app/listener/OrderCreateListener.php
namespace app\listener;
use app\event\OrderCreate;
use think\facade\Log;
use think\facade\Cache;
class OrderCreateListener
{
/**
* 监听器优先级
*/
public $priority = 5;
/**
* 处理事件
*/
public function handle(OrderCreate $event)
{
// 缓存订单数据
Cache::set("order_{$event->orderId}", $event->orderData, 3600);
// 清空用户订单缓存
Cache::delete("user_orders_{$event->orderData['user_id']}");
// 记录日志
Log::info("订单创建:{$event->orderId}");
// 其他业务逻辑,如发送邮件、通知等
$this->sendNotification($event);
}
private function sendNotification($event)
{
// 发送通知逻辑
}
/**
* 事件错误处理
*/
public function failed(OrderCreate $event, \Throwable $e)
{
Log::error("订单事件处理失败:{$e->getMessage()}");
}
}
基于注解的自动绑定(ThinkPHP 8)
<?php
// app/controller/User.php
namespace app\controller;
use think\annotation\route\Group;
use think\annotation\route\Route;
use think\facade\Event;
#[Group('user')]
class User
{
#[Route('POST', 'login')]
public function login()
{
// 登录逻辑
$user = User::find(1);
// 触发事件
Event::trigger('UserLogin', $user);
return json(['code' => 0, 'msg' => '登录成功']);
}
}
最佳实践建议
- 事件命名规范:使用语义化的名称,如
UserLogin、OrderCreate等 - 业务解耦:事件主要用于业务解耦,避免类与类之间的直接依赖
- 异步处理:对于耗时操作(如发邮件、通知等),考虑使用异步事件或队列
- 错误处理:在监听器中做好异常处理,避免影响主业务流程
- 性能考虑:合理使用事件,避免过度设计导致性能问题
通过以上方式,你可以灵活地在ThinkPHP项目中绑定事件和监听器,实现业务逻辑的解耦和扩展。