ThinkPHP项目事件与监听器绑定

wen PHP项目 3

本文目录导读:

ThinkPHP项目事件与监听器绑定

  1. 事件定义文件方式(推荐)
  2. 创建事件类
  3. 创建监听器
  4. 监听器优先级配置
  5. 创建订阅器
  6. 动态绑定事件监听器
  7. 在控制器中使用事件
  8. 命令行模式绑定
  9. 事件监听器的完整示例
  10. 基于注解的自动绑定(ThinkPHP 8)
  11. 最佳实践建议

在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' => '登录成功']);
    }
}

最佳实践建议

  1. 事件命名规范:使用语义化的名称,如 UserLoginOrderCreate
  2. 业务解耦:事件主要用于业务解耦,避免类与类之间的直接依赖
  3. 异步处理:对于耗时操作(如发邮件、通知等),考虑使用异步事件或队列
  4. 错误处理:在监听器中做好异常处理,避免影响主业务流程
  5. 性能考虑:合理使用事件,避免过度设计导致性能问题

通过以上方式,你可以灵活地在ThinkPHP项目中绑定事件和监听器,实现业务逻辑的解耦和扩展。

抱歉,评论功能暂时关闭!