本文目录导读:

在 Symfony 中,Event(事件) 和 Event Dispatcher(事件调度器) 是一个核心组件,用于实现 观察者模式,让系统中的不同部分能够解耦通信,当你提到“命名”时,通常涉及三个方面:事件名称的命名约定、事件类的命名 以及 自定义事件的命名规范。
下面我将从这几个角度详细说明,并重点给出在 PHP/Symfony 项目中实际可用的经验规范。
事件名称(Event Name)的命名约定
事件名称是字符串,用于标识具体事件,Symfony 官方和社区有明确的命名规则:
- 格式: 通常使用 全小写,并用 点号() 作为层级分隔符。
- 构成:
命名空间.动词或命名空间.名词.动词
常见例子:
| 事件名称 | 说明 |
|---|---|
kernel.request |
内核收到请求 |
kernel.response |
内核发送响应 |
kernel.exception |
内核捕获异常 |
security.authentication.success |
认证成功 |
security.authentication.failure |
认证失败 |
doctrine.post_persist |
Doctrine 持久化后 |
user.registered |
用户注册事件(自定义) |
✅ 推荐规则:
{包/模块名}.{主体}.{动作}
order.createdorder.paiduser.password_changedblog.post_published
注意: 事件名称不涉及命名空间解析,只是纯字符串,因此避免使用 或 。
事件类(Event Class)的命名规范
Symfony 建议每个事件使用一个独立的 PHP 类来表示事件数据。
类名规则:
- 以
Event- 放在
Event目录下- 类名通常与事件名称的“名词部分”呼应
- 放在
示例:
// 事件名称: user.registered
// 事件类: App\Event\UserRegisteredEvent
namespace App\Event;
use Symfony\Contracts\EventDispatcher\Event;
class UserRegisteredEvent extends Event
{
public const NAME = 'user.registered'; // 推荐将事件名称定义为类常量
public function __construct(
private User $user,
) {}
public function getUser(): User
{
return $this->user;
}
}
使用常量
NAME的好处:其他地方引用时不用硬编码字符串,更安全、易重构。
事件命名的最佳实践
在 Symfony 项目中,除了要知道“叫什么”,还要知道“怎么用”,以下是几条实用的命名实践:
✅ 3.1 使用常量引用事件名称
不推荐:
$dispatcher->dispatch('user.registered', $event);
推荐:
$dispatcher->dispatch(new UserRegisteredEvent($user), UserRegisteredEvent::NAME); // 或(可以省略 NAME,由事件类自己提供) $dispatcher->dispatch($event);
✅ 3.2 事件类应当只携带数据,不包含业务逻辑
class OrderPaidEvent extends Event
{
public function __construct(
public readonly Order $order,
public readonly \DateTimeImmutable $paidAt,
) {}
}
✅ 3.3 区分“前/后”事件
当有 before/after 或 pre/post 语义时,可以用 order.pre_pay 和 order.post_pay,或用两个不同的事件类。
更常见做法:
class OrderBeforePayEvent extends Event { /* ... */ }
class OrderAfterPayEvent extends Event { /* ... */ }
事件名称:
order.before_payorder.after_pay
✅ 3.4 避免事件名过于通用或模糊
❌ event.user(不明确是什么)
✅ user.password_reset_requested
✅ user.email_confirmed
完整示例:自定义事件在项目中的使用
假设你可能在做一个电商项目,用户注册后要发邮件、记录日志。
第一步:定义事件类
// src/Event/UserRegisteredEvent.php
namespace App\Event;
use App\Entity\User;
use Symfony\Contracts\EventDispatcher\Event;
class UserRegisteredEvent extends Event
{
public const NAME = 'user.registered';
public function __construct(
private User $user,
) {}
public function getUser(): User
{
return $this->user;
}
}
第二步:触发事件(通常在服务中)
// src/Service/UserRegistrationService.php
use App\Event\UserRegisteredEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class UserRegistrationService
{
public function __construct(
private EventDispatcherInterface $eventDispatcher,
) {}
public function register(User $user): void
{
// ... 保存 user 到数据库
$event = new UserRegisteredEvent($user);
$this->eventDispatcher->dispatch($event, UserRegisteredEvent::NAME);
}
}
第三步:监听事件
# config/services.yaml
services:
App\EventListener\SendWelcomeEmailListener:
tags:
- { name: kernel.event_listener, event: user.registered, method: onUserRegistered }
或使用 Attribute(Symfony 6+ 推荐):
// src/EventListener/SendWelcomeEmailListener.php
namespace App\EventListener;
use App\Event\UserRegisteredEvent;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener(event: UserRegisteredEvent::NAME)]
class SendWelcomeEmailListener
{
public function __invoke(UserRegisteredEvent $event): void
{
$user = $event->getUser();
// 发送欢迎邮件...
}
}
总结建议
| 方面 | 推荐做法 |
|---|---|
| 事件名称 | 小写.点号分隔,如 order.paid |
| 事件类名 | 名词 + Event 后缀,实现 Event |
| 类常量 | 定义 public const NAME = 'event.name' |
| 目录位置 | src/Event/ |
| 事件数据 | 只存数据,不写逻辑,使用 readonly 属性 |
| 事件调度 | 使用 $dispatcher->dispatch($event, EventClass::NAME) |
| 监听器注册 | 推荐使用 #[AsEventListener] Attribute |
如果你是在写一个 可复用 Bundle,事件名最好加上你的 vendor 前缀,如 acme_user.registered,避免与其他 Bundle 冲突。
希望这些解答对你有帮助!如果还有具体场景,欢迎继续提问。