Symfony 通知程序将自定义元数据附加到信封
我正在使用 Symfony 通知程序和信使组件异步发送 SMS 消息(以及将来的推送和电子邮件通知)。
一切工作正常,但是一旦发送消息,我想记录有关它的信息。
我可以通过订阅 WorkerMessageHandledEvent
来捕获成功的消息,它为我提供了 Message
对象,以及包含的 Envelope
及其所有 Stamp
里面的对象。根据所有可用信息,我将使用名为 MessageLog
的实体将其记录在我的数据库中。
class MessengerSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
WorkerMessageHandledEvent::class => ['onHandled']
];
}
public function onHandled(WorkerMessageHandledEvent $event) {
$log = new MessageLog();
$log->setSentAt(new DateTime());
if($event->getEnvelope()->getMessage() instanceof SmsMessage) {
$log->setSubject($event->getEnvelope()->getMessage()->getSubject());
$log->setRecipient($event->getEnvelope()->getMessage()->getPhone());
}
// Do more tracking
}
}
我想做的是跟踪“调用”消息的对象。例如,如果我有一个新闻提要,并且发布帖子会发出通知,我想将每条记录的消息归因于该帖子(以显示每个帖子的受众覆盖率/投放统计数据 - 以及来自管理员 POV 审核和报告) )。
我尝试添加 Stamp
或其他尝试将自定义元数据附加到消息的方法,但在使用 symfony/notifier
时它似乎被抽象了捆。
下面是我用来发送通知的内容(或多或少是 WIP):
class PostService {
protected NotifierInterface $notifier;
public function ___construct(NotifierInterface $notifier) {
$this->notifier = $notifier;
}
public function sendNotifications(Post $post) {
$notification = new PostNotification($post);
$recipients = [];
foreach($post->getNewsFeed()->getSubscribers() as $user) {
$recipients[] = new Recipient($user->getEmail(), $user->getMobilePhone());
}
$this->notifier->send($notification, ...$recipients);
}
}
class PostNotification extends Notification implements SmsNotificationInterface {
protected Post $post;
public function __construct(Post $post) {
parent::__construct();
$this->post = $post;
}
public function getChannels(RecipientInterface $recipient): array {
return ['sms'];
}
public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage {
if($transport === 'sms') {
return new SmsMessage($recipient->getPhone(), $this->getPostContentAsSms());
}
return null;
}
private function getPostContentAsSms() {
return $post->getTitle()."\n\n".$post->getContent();
}
}
当这一切完成时,这就是我在 WorkerMessageHandledEvent
中所拥有的全部内容
^ Symfony\Component\Messenger\Event\WorkerMessageHandledEvent^ {#5590
-envelope: Symfony\Component\Messenger\Envelope^ {#8022
-stamps: array:7 [
"Symfony\Component\Messenger\Stamp\BusNameStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\BusNameStamp^ {#10417
-busName: "messenger.bus.default"
}
]
"Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineReceivedStamp" => array:1 [
0 => Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineReceivedStamp^ {#10419
-id: "2031"
}
]
"Symfony\Component\Messenger\Stamp\TransportMessageIdStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\TransportMessageIdStamp^ {#10339
-id: "2031"
}
]
"Symfony\Component\Messenger\Stamp\ReceivedStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\ReceivedStamp^ {#5628
-transportName: "async"
}
]
"Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp^ {#7306}
]
"Symfony\Component\Messenger\Stamp\AckStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\AckStamp^ {#7159
-ack: Closure(Envelope $envelope, Throwable $e = null)^ {#6205
class: "Symfony\Component\Messenger\Worker"
this: Symfony\Component\Messenger\Worker {#5108 …}
use: {
$transportName: "async"
$acked: & false
}
}
}
]
"Symfony\Component\Messenger\Stamp\HandledStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\HandledStamp^ {#11445
-result: Symfony\Component\Notifier\Message\SentMessage^ {#2288
-original: Symfony\Component\Notifier\Message\NullMessage^ {#6625
-decoratedMessage: Symfony\Component\Notifier\Message\SmsMessage^ {#10348
-transport: null
-subject: ".................................................."
-phone: "0412345678"
}
}
-transport: "null"
-messageId: null
}
-handlerName: "Symfony\Component\Notifier\Messenger\MessageHandler::__invoke"
}
]
]
-message: Symfony\Component\Notifier\Message\SmsMessage^ {#10348}
}
-receiverName: "async"
}
doco 向我展示了添加我自己的通知的方法邮票到信封,我猜我可以用它来附加元数据,例如我的 Post
对象,但这意味着我需要使用 MessageBusInterface
来发送通知。我不想这样做,因为我想通过 NotifierInterface
路由消息,以获得渠道策略、短信传输等的所有好处。
tl;dr:我如何获取一些元数据如果我使用 NotifierInterface
发送消息,则为 WorkerMessageHandledEvent
I'm using the Symfony notifier and messenger components to asynchronously send SMS messages (and in the future push and email notifications).
Everything works just fine, however once a message is sent, I'd like to log information about it.
I can catch a successful message by subscribing to WorkerMessageHandledEvent
which provides me the Message
object, along with the containing Envelope
and all its Stamp
objects inside. From all the available information, I'll be logging this in my database using an entity named MessageLog
.
class MessengerSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
WorkerMessageHandledEvent::class => ['onHandled']
];
}
public function onHandled(WorkerMessageHandledEvent $event) {
$log = new MessageLog();
$log->setSentAt(new DateTime());
if($event->getEnvelope()->getMessage() instanceof SmsMessage) {
$log->setSubject($event->getEnvelope()->getMessage()->getSubject());
$log->setRecipient($event->getEnvelope()->getMessage()->getPhone());
}
// Do more tracking
}
}
What I'd like to do, is track the object that "invoked" the message. For example, if I have a news feed, and posting a post sends out a notification, I'd like to attribute each logged message to that post (to display audience reach/delivery stats per post - and from an admin POV auditing and reporting).
I've tried to go about adding a Stamp
, or other means of trying to attach custom metadata to the message, but it seems to be abstracted when using the symfony/notifier
bundle.
The below is what I'm using to send notifications (more or less WIP):
class PostService {
protected NotifierInterface $notifier;
public function ___construct(NotifierInterface $notifier) {
$this->notifier = $notifier;
}
public function sendNotifications(Post $post) {
$notification = new PostNotification($post);
$recipients = [];
foreach($post->getNewsFeed()->getSubscribers() as $user) {
$recipients[] = new Recipient($user->getEmail(), $user->getMobilePhone());
}
$this->notifier->send($notification, ...$recipients);
}
}
class PostNotification extends Notification implements SmsNotificationInterface {
protected Post $post;
public function __construct(Post $post) {
parent::__construct();
$this->post = $post;
}
public function getChannels(RecipientInterface $recipient): array {
return ['sms'];
}
public function asSmsMessage(SmsRecipientInterface $recipient, string $transport = null): ?SmsMessage {
if($transport === 'sms') {
return new SmsMessage($recipient->getPhone(), $this->getPostContentAsSms());
}
return null;
}
private function getPostContentAsSms() {
return $post->getTitle()."\n\n".$post->getContent();
}
}
By the time this is all done, this is all I have in the WorkerMessageHandledEvent
^ Symfony\Component\Messenger\Event\WorkerMessageHandledEvent^ {#5590
-envelope: Symfony\Component\Messenger\Envelope^ {#8022
-stamps: array:7 [
"Symfony\Component\Messenger\Stamp\BusNameStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\BusNameStamp^ {#10417
-busName: "messenger.bus.default"
}
]
"Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineReceivedStamp" => array:1 [
0 => Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineReceivedStamp^ {#10419
-id: "2031"
}
]
"Symfony\Component\Messenger\Stamp\TransportMessageIdStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\TransportMessageIdStamp^ {#10339
-id: "2031"
}
]
"Symfony\Component\Messenger\Stamp\ReceivedStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\ReceivedStamp^ {#5628
-transportName: "async"
}
]
"Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\ConsumedByWorkerStamp^ {#7306}
]
"Symfony\Component\Messenger\Stamp\AckStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\AckStamp^ {#7159
-ack: Closure(Envelope $envelope, Throwable $e = null)^ {#6205
class: "Symfony\Component\Messenger\Worker"
this: Symfony\Component\Messenger\Worker {#5108 …}
use: {
$transportName: "async"
$acked: & false
}
}
}
]
"Symfony\Component\Messenger\Stamp\HandledStamp" => array:1 [
0 => Symfony\Component\Messenger\Stamp\HandledStamp^ {#11445
-result: Symfony\Component\Notifier\Message\SentMessage^ {#2288
-original: Symfony\Component\Notifier\Message\NullMessage^ {#6625
-decoratedMessage: Symfony\Component\Notifier\Message\SmsMessage^ {#10348
-transport: null
-subject: ".................................................."
-phone: "0412345678"
}
}
-transport: "null"
-messageId: null
}
-handlerName: "Symfony\Component\Notifier\Messenger\MessageHandler::__invoke"
}
]
]
-message: Symfony\Component\Notifier\Message\SmsMessage^ {#10348}
}
-receiverName: "async"
}
The doco shows me ways to add my own stamps to the envelope, which I'm guessing I can use to attach metadata such as my Post
object, but this means I need to use the MessageBusInterface
to send notifications. I don't want to do because I would like to route messages through the NotifierInterface
to gain all the benefits of channel policies, texter transports, etc.
tl;dr: how do I get some metadata through to a WorkerMessageHandledEvent
if I send a message using the NotifierInterface
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我找到了让它发挥作用的方法!
本质上,我们这里有两个组件,Symfony notifier 和 Symfony messenger。当一起使用时,它们创建了一种向任意数量的端点发送消息的强大方法。
首先,我所做的是创建一个名为
NotificationStampsInterface
的接口和一个名为NotificationStamps
的特征,该特征满足该接口(通过使用接口方法存储受保护的数组来读取/写入它) 。然后可以将此接口添加到您的自定义通知对象(在本例中是
PostNotification
),以及NotificationStamps
特征以满足接口方法的要求。这里的技巧是,当通过通知程序发送通知时,它最终会调用消息组件来发送消息。处理这个问题的位是
Symfony\Component\Notifier\Channel\SmsChannel
。本质上,如果 MessageBus 可用,它将通过该总线推送消息,而不是直接通过通知程序。我们可以扩展
SmsChannel
类,在notify()
方法中添加我们自己的逻辑。最后,我们需要通过在
services.yaml
中添加以下内容来覆盖服务,就是这样!我们现在有一种方法可以将标记附加到我们的Notification对象中,该对象将一直传递到
WorkerMessageHandledEvent
。一个示例用途是(至少对于我的情况)
一旦发送消息,转储结果表明我们确实在事件触发时注册了我们的标记。
I've found a way to make it work!
Essentially what happens is that we have two components here, the Symfony notifier and the Symfony messenger. When used together, they create a powerful way to send messages to any number of endpoints.
Firstly what I did was create an interface called
NotificationStampsInterface
and a trait calledNotificationStamps
that satisfies the interface (by storing a protected array using the interface methods to read/write to it).This interface can then be added onto your custom notification object, in this instance
PostNotification
, alongside with theNotificationStamps
trait to satisfy the interface methods.The trick here is that when sending a notification via the notifier, it ultimately calls on the messenger component to send the message. The bit that handles this is
Symfony\Component\Notifier\Channel\SmsChannel
. Essentially, if a MessageBus is available, it will push messages through that rather than going straight though the notifier.We can extend the
SmsChannel
class to add our own logic insidenotify()
method.Lastly we need to override the service by adding the following in
services.yaml
And that's it! We now have a way to append stamps to our Notification object that will carry all the way through to the
WorkerMessageHandledEvent
.An example use would be (for my situation at least)
Once the message is sent, dumping the result shows that we do indeed have our stamp registered at the point where our event fires.