事件系统
DuxLite 提供了简洁的事件系统,用于实现模块间的松耦合通信。事件系统基于 Symfony EventDispatcher,支持事件触发和监听。
什么是事件系统
事件系统允许在应用的特定时刻触发事件,其他模块可以监听这些事件并执行相应的处理逻辑。
核心概念
- 事件触发:在业务逻辑中触发事件
- 事件监听:使用注解监听特定事件
- 解耦通信:模块间通过事件进行松耦合通信
事件定义
1. 创建事件类
事件类需要继承 Symfony\Contracts\EventDispatcher\Event
:
php
<?php
namespace App\System\Events;
use App\System\Models\SystemUser;
use Symfony\Contracts\EventDispatcher\Event;
class UserRegistered extends Event
{
public function __construct(
public SystemUser $user,
public array $metadata = []
) {}
public function getUser(): SystemUser
{
return $this->user;
}
public function getMetadata(): array
{
return $this->metadata;
}
}
2. 触发事件
触发字符串事件(简单方式)
php
use Core\App;
// 在服务中触发事件
class UserService
{
public static function register(array $userData): User
{
$user = User::create($userData);
// 触发字符串事件
App::event()->dispatch('user.registered', $user);
return $user;
}
}
触发事件对象(推荐方式)
php
use Core\App;
use App\System\Events\UserRegistered;
class UserService
{
public static function register(array $userData): User
{
$user = User::create($userData);
// 创建事件对象
$event = new UserRegistered($user, ['source' => 'web']);
// 触发事件对象
App::event()->dispatch($event, 'user.registered');
return $user;
}
}
事件监听
1. 创建监听器
使用 #[Listener]
注解创建事件监听器:
php
<?php
namespace App\System\Listeners;
use App\System\Events\UserRegistered;
use Core\Event\Attribute\Listener;
use Core\App;
class UserListener
{
// 监听字符串事件
#[Listener('user.registered')]
public function handleUserRegistered($user): void
{
// 发送欢迎邮件
App::queue()->push(new SendWelcomeEmailJob($user->email));
// 记录日志
App::log()->info('新用户注册', ['user_id' => $user->id]);
}
// 监听事件对象
#[Listener('user.registered')]
public function handleUserRegisteredEvent(UserRegistered $event): void
{
$user = $event->getUser();
$metadata = $event->getMetadata();
// 根据来源处理不同逻辑
if ($metadata['source'] === 'admin') {
// 管理员创建的用户处理逻辑
} else {
// 用户自注册处理逻辑
}
}
}
2. 监听器优先级
可以设置监听器的执行优先级:
php
class NotificationListener
{
// 高优先级监听器(先执行)
#[Listener('article.published', priority: 100)]
public function sendUrgentNotification($article): void
{
// 发送紧急通知
}
// 普通优先级监听器(后执行)
#[Listener('article.published')]
public function updateCache($article): void
{
// 更新缓存
App::cache()->forget("article.{$article->id}");
}
}
实际应用示例
1. 用户管理模块
php
// 定义用户注册事件
namespace App\System\Events;
use App\System\Models\SystemUser;
use Symfony\Contracts\EventDispatcher\Event;
class UserRegistered extends Event
{
public function __construct(
public SystemUser $user,
public string $source = 'web'
) {}
}
// 用户服务
class UserService
{
public static function createUser(array $data): User
{
$user = User::create($data);
// 触发事件对象
$event = new UserRegistered($user, 'admin');
App::event()->dispatch($event, 'user.registered');
return $user;
}
}
// 邮件监听器
class EmailListener
{
#[Listener('user.registered')]
public function sendWelcomeEmail(UserRegistered $event): void
{
$user = $event->user;
// 根据注册来源发送不同邮件
if ($event->source === 'admin') {
App::queue()->push(new SendAdminCreatedUserEmailJob($user));
} else {
App::queue()->push(new SendWelcomeEmailJob($user));
}
}
}
2. 文章发布系统
php
// 定义文章发布事件
namespace App\Blog\Events;
use App\Blog\Models\BlogArticle;
use Symfony\Contracts\EventDispatcher\Event;
class ArticlePublished extends Event
{
public function __construct(
public BlogArticle $article,
public array $changes = []
) {}
}
// 文章服务
class ArticleService
{
public static function publish(int $articleId): void
{
$article = Article::find($articleId);
$oldStatus = $article->status;
$article->update(['status' => 'published', 'published_at' => now()]);
// 触发事件对象
$event = new ArticlePublished($article, [
'old_status' => $oldStatus,
'published_by' => auth()->id()
]);
App::event()->dispatch($event, 'article.published');
}
}
// 缓存监听器
class CacheListener
{
#[Listener('article.published')]
public function clearArticleCache(ArticlePublished $event): void
{
$article = $event->article;
// 清除文章缓存
App::cache()->forget("article.{$article->id}");
App::cache()->forget('articles.list');
}
}
最佳实践
1. 事件命名规范
使用清晰的事件命名:
php
// ✅ 推荐的命名方式
App::event()->dispatch('user.registered', $user);
App::event()->dispatch('article.published', $article);
App::event()->dispatch('order.completed', $order);
// ❌ 避免的命名方式
App::event()->dispatch('userEvent', $user);
App::event()->dispatch('doSomething', $data);
2. 监听器职责单一
每个监听器只处理一个特定的任务:
php
// ✅ 推荐:职责单一
class EmailNotificationListener
{
#[Listener('user.registered')]
public function sendWelcomeEmail($user): void
{
// 只负责发送邮件
}
}
class UserStatisticsListener
{
#[Listener('user.registered')]
public function updateStats($user): void
{
// 只负责更新统计
}
}
3. 异常处理
监听器中应该妥善处理异常:
php
class EmailListener
{
#[Listener('user.registered')]
public function sendWelcomeEmail($user): void
{
try {
// 发送邮件逻辑
$this->mailService->send($user->email, 'welcome');
} catch (\Exception $e) {
// 记录错误但不影响其他监听器
App::log()->error('邮件发送失败', [
'user_id' => $user->id,
'error' => $e->getMessage()
]);
}
}
}
总结
DuxLite 事件系统的核心要点:
核心特性
- 事件继承:事件类继承
Symfony\Contracts\EventDispatcher\Event
- 注解驱动:使用
#[Listener]
注解定义监听器 - 优先级控制:支持监听器执行优先级设置
- 松耦合通信:模块间通过事件进行解耦通信
使用场景
- 用户注册:发送欢迎邮件、更新统计、分配角色
- 内容发布:清除缓存、发送通知、更新索引
- 订单处理:库存更新、支付处理、物流通知
- 系统监控:日志记录、性能统计、异常报告
最佳实践
- 事件命名:使用清晰的点号分隔命名(如
user.registered
) - 职责单一:每个监听器只处理一个特定任务
- 异常处理:监听器中妥善处理异常,不影响其他监听器
- 事件对象:优先使用事件对象而不是字符串事件
开发建议
- 事件类继承
Symfony\Contracts\EventDispatcher\Event
- 监听器按业务模块组织,便于维护
- 合理使用优先级控制执行顺序
- 结合队列系统处理复杂业务逻辑
通过事件系统,你可以构建出高度解耦、易于扩展的应用架构。接下来我们将学习队列系统的使用。