final class TicketNotificationListener implements EventSubscriber { /** * @var TicketMailer */ private $mailer; /** * @var TicketSlackSender */ private $slackSender; /** * @var NotificationManager */ private $notificationManager; /** * We must wait the flush to send closing notification in order to * be sure to have the latest message of the ticket. * * @var Ticket[]|ArrayCollection */ private $closedTickets; /** * @param TicketMailer $mailer * @param TicketSlackSender $slackSender * @param NotificationManager $notificationManager */ public function __construct(TicketMailer $mailer,TicketSlackSender $slackSender,NotificationManager $notificationManager) { $this->mailer = $mailer; $this->slackSender = $slackSender; $this->notificationManager = $notificationManager; $this->closedTickets = new ArrayCollection(); } // Stuff... }
目标是在使用Doctrine sql通过邮件,Slack和内部通知创建或更新Ticket或TicketMessage实体时分派通知.
我已经与Doctrine有一个循环依赖问题,所以我从事件args中注入了实体管理器:
class NotificationManager { /** * Must be set instead of extending the EntityManagerDecorator class to avoid circular dependency. * * @var EntityManagerInterface */ private $entityManager; /** * @var NotificationRepository */ private $notificationRepository; /** * @var RouterInterface */ private $router; /** * @param RouterInterface $router */ public function __construct(RouterInterface $router) { $this->router = $router; } /** * @param EntityManagerInterface $entityManager */ public function setEntityManager(EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; $this->notificationRepository = $this->entityManager->getRepository('AppBundle:Notification'); } // Stuff... }
经理从TicketNotificationListener注入
public function postPersist(LifecycleEventArgs $args) { // Must be lazy set from here to avoid circular dependency. $this->notificationManager->setEntityManager($args->getEntityManager()); $entity = $args->getEntity(); }
Web应用程序正在运行,但是当我尝试运行像doctrine:database:drop这样的命令时,我得到了这个:
[Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException] Circular reference detected for service "doctrine.dbal.default_connection",path: "doctrine.dbal.default_connection -> mailer.ticket -> twig -> security.authorization_checker -> security.authentication.manager -> fos_user.user_provider.username_email -> fos_user.user_manager".
但这与供应商服务有关.
谢谢.
>域事件(例如TicketWasClosed)
> Doctrine的生命周期事件(例如PostPersist)
Doctrine的事件系统旨在挂钩持久化流程,处理与保存到数据库和从数据库加载直接相关的内容.它不应该用于其他任何事情.
对我来说,你想要发生的事情是:
When a ticket was closed,send a notification.
这与Doctrine或一般的持久性无关.您需要的是另一个专门用于域事件的事件系统.
您仍然可以使用Doctrine中的EventManager,但请确保创建用于域事件的第二个实例.
你也可以用别的东西.例如Symfony的EventDispatcher.如果您正在使用Symfony框架,那么同样适用于此:不要使用Symfony的实例,为Domain Events创建自己的实例.
我个人喜欢SimpleBus,它使用对象作为事件而不是字符串(对象作为“参数”).它还遵循消息总线和中间件模式,它们提供了更多的自定义选项.
PS:关于域事件有很多非常好的文章.谷歌是你的朋友:)
例
通常,域事件在对它们执行操作时会记录在实体本身中.因此Ticket实体将具有如下方法:
public function close() { // insert logic to close ticket here $this->record(new TicketWasClosed($this->id)); }
这可以确保实体对其状态和行为负全部责任,保护其不变量.
当然,我们需要一种方法将记录的域事件从实体中取出:
/** @return object[] */ public function recordedEvents() { // return recorded events }
从这里我们可能想要两件事:
>将这些事件收集到一个调度程序/发布者中.
>仅在成功交易后发送/发布这些事件.
使用Doctrine ORM,您可以订阅Doctrine的OnFlush事件的监听器,该事件将在所有刷新的实体(用于收集域事件)上调用recordedEvents(),而PostFlush可以将这些事件传递给调度程序/发布者(仅在成功时) .
SimpleBus提供了一个提供此功能的DoctrineORMBridge.