damianociarla/notification-bundle

DCSNotificationBundle 是一个 Symfony2 扩展包,允许您向任何接收者发送任何类型的通知

dev-master 2015-10-30 10:54 UTC

This package is not auto-updated.

Last update: 2024-09-14 16:07:05 UTC


README

DCSNotificationBundle 是一个允许您发送通知的 Symfony2 扩展包。

使用示例

为了说明此扩展包的工作原理,我们将通过一个示例进行说明。我们有一个博客应用程序,允许已登录用户对已发布的帖子添加评论。

当读者在帖子下留言时,帖子的作者和所有之前的评论者都会收到关于新评论的电子邮件。作为负责任的开发者,我们给所有评论者提供了禁用通知的选项。

流程

DCSNotificationBundle 提供了三个重要的服务

  • dcs_notification.service.notification 通过 notify() 方法处理通知
  • dcs_notification.manager.component 负责创建通知的 主题
  • dcs_notification.service.notifier 通过 process() 方法处理通知

对于每种类型的通知,我们负责创建一个 通知器 和一个 传输器,它们知道如何处理通知。

配置

要使用 DSCNotificationBunde,我们需要配置该扩展包和我们的通知器。

以下是一个扩展包配置示例

dcs_notification:
    db_driver: orm
    class:
        model:
            component: Blog\NotificationBundle\Entity\Component
            component_setting: Blog\NotificationBundle\Entity\ComponentSetting
            notification: Blog\NotificationBundle\Entity\Notification
            notification_component: Blog\NotificationBundle\Entity\NotificationComponent
            recipient: Blog\NotificationBundle\Entity\Recipient
    transporters:
        mail:
            id: blog_notification.transporter.mail
    actions:
        new_comment:
            transporters:
                mail:
                    id: mail
                    config:
                        template: BlogNotificationBundle:Email:new_comment.html.twig

dcs_notification 是 DCSNotificationBundle 配置的键。要使用扩展包,John 首先配置了必需的键

  • db_driver 设置为 orm 告诉 Symfony 2 使用 Doctrine ORM
  • class 包含模型类列表。如果我们不需要复杂的类,我们可以直接扩展扩展包提供的基类
    • Blog\NotificationBundle\Entity\Component extends DCS\NotificationBundle\Entity\Component
    • Blog\NotificationBundle\Entity\ComponentSetting extends DCS\NotificationBundle\Entity\ComponentSetting
    • Blog\NotificationBundle\Entity\Notification extends DCS\NotificationBundle\Entity\Notification
    • Blog\NotificationBundle\Entity\NotificationComponent extends DCS\NotificationBundle\Entity\NotificationComponent
    • Blog\NotificationBundle\Entity\Recipient extends DCS\NotificationBundle\Entity\Recipient
  • transporters 定义与有效服务关联的传输器列表
  • actions 是与传输器相关联的可访问操作列表

为了能够使用扩展包,我们需要配置通知器。以下是一个配置示例

services:
    blog.notification_bundle.notifier.new_comment:
        class: Blog\NotificationBundle\Notifier\NewCommentNotifier
        tags:
            - { name: dcs_notification.notifier }

这样我们就添加了 blog.notification_bundle.notifier.new_comment 通知器。

通知通知

dcs_notification.service.notification 服务代表通知流程的入口点。该服务通过以下方式使用 notify() 方法

$commentComponent = $this->componentManager->findOrCreateComponent($comment, $comment->getId());
$this->notificationService->notify($commentComponent, 'new_comment', array('url' => $currentUrl));

《notify()` 方法的第一个参数称为 subject。这可以是任何东西,只要它被包裹在 DCS\NotificationBundle\Model\ComponentManagerInterface 类型对象中。在示例中,我们决定将评论传递给 notify() 方法,因为评论中包含了大量关于正在发生的事情的信息:评论本身、评论作者、评论的帖子以及从帖子中获取的作者、帖子上的其他评论以及最后所有其他评论的作者。第二个参数是 new_comment,称为通知的 action。我们稍后会回到 action。第三个参数是可选的,包含我们想要以后能够访问的附加数据。

《notify()` 方法实际上并不处理通知,而是将其保存为“挂起”状态。

另一种传递数据给 notify() 方式

我们本可以决定以不同的方式实现通知,将评论作为第三个参数传递,并使主题更通用

$commentComponent = $this->componentManager->findOrCreateComponent('the_comment_subject', $comment->getId());
$this->notificationService->notify(
    $commentComponent,
    'new_comment',
    array(
        'url' => $currentUrl,
        'id' => $comment->getId()
    )
);

此更改将影响我们处理通知的方式。

处理挂起通知

《dcs_notification.manager.notification》服务公开了《findAllNotificationToSend()`》方法,它返回所有挂起通知。

有了挂起通知的列表,可以使用《dcs_notification.service.notifier》服务进行处理,如下所示

$notificationsToSend = $this->getContainer()->get('dcs_notification.manager.notification')->findAllNotificationToSend($limit);
$notifierService = $this->getContainer()->get('dcs_notification.service.notifier');
foreach ($notificationsToSend as $notification) {
    $notifierService->process($notification);
}

上述代码可以用于命令行命令集,通过 cron 定期运行。

通知器

到目前为止,我们有一个挂起通知的列表正在逐个处理。当调用 process() 方法时,发生两件重要的事情

  • 每个通知器都被询问是否知道如何管理当前通知,如果结果是肯定的,则将通知传递给它
  • 调用传输器

通知器是实现《DCS\NotificationBundle\Notifier\NotifierInterface》接口的类。

在我们的示例中,我们有一个新的通知,具有《new_comment》操作,并且可以使用有效的通知器来管理通知的接收者

<?php
namespace Blog\NotificationBundle\Notifier;

use DCS\NotificationBundle\Model\NotificationComponentInterface;
use DCS\NotificationBundle\Model\NotificationInterface;
use DCS\NotificationBundle\Model\NotificationManagerInterface;
use DCS\NotificationBundle\Notifier\Collection\RecipientCollectionInterface;
use DCS\NotificationBundle\Notifier\NotifierInterface;

use Blog\PostBundle\Entity\Post;
use Blog\PostBundle\Entity\Comment;
use Blog\PostBundle\Repository\CommentRepository;
use Blog\UserBundle\Entity\User;

class NewCommentNotifier implements NotifierInterface
{
    protected $notificationManager;
    protected $projectRepository;
    
    function __construct(NotificationManagerInterface $notificationManager, CommentRepository $commentRepository)
    {
        $this->notificationManager = $notificationManager;
        $this->commentRepository = $commentRepository;
    }
    
    public function supports(NotificationInterface $notification)
    {
        return $notification->getAction() == 'new_comment';
    }
    
    public function notify(NotificationInterface $notification, RecipientCollectionInterface $recipients)
    {
        $notificationComponents = $this->notificationManager->findAllNotificationComponent($notification);
        
        /** @var NotificationComponentInterface $notificationComponent */
        foreach ($notificationComponents as $notificationComponent) {
            if ($notificationComponent->getType() == 'comment') {
                /** @var Comment $comment */
                $comment = $notificationComponent->getComponent()->getData();
                if ($comment instanceof Comment) {
                    $commentAuthor = $comment->getAuthor();
                    $post = $comment->getPost();
                    $postAuthor = $post->getAuthor();
                    
                    if ($comment->getAuthor()->getId() != $postAuthor->getId()) {
                        $recipients->add('Blog\UserBundle\Entity\User', $postAuthor->getId());
                    }
                    
                    $commenters = $this->commentRepository->findAllCommentersToPost($post);
                    /** @var User $member */
                    foreach ($commenters as $commenter) {
                        if ($commenter->getId() != $postAuthor->getId() && $commenter->getId() != $postAuthor->getId()) {
                            $recipients->add('Blog\UserBundle\Entity\User', $commenter->getId());
                        }
                    }
                }
            }
        }
    }
} 

使用《Blog\NotificationBundle\Notifier\CommentNotifier》类,我们现在能够将正确的接收者添加到通知中:帖子的作者和所有评论者。通知准备好发送到传输器。

传输器

传输器是实现《DCS\NotificationBundle\Transporter\TransporterInterface》接口的类。接口要求开发人员实现两个方法:`setConfiguration()` 和 `send()`

<?php
namespace DCS\NotificationBundle\Transporter;

use DCS\NotificationBundle\Model\NotificationInterface;
use DCS\NotificationBundle\Model\RecipientInterface;

interface TransporterInterface
{
    public function setConfiguration($config);
    public function send(NotificationInterface $notification, array $additionalData, RecipientInterface $recipient);
} 

如果我们回顾《actions.new_comment.transporters.mail.config》配置,我们定义了一个选项,它指定与传输器关联的模板。这些选项通过 `setConfiguration()` 方法注入到 `blog_notification.transporter.mail` 服务中。

`send()` 方法负责执行发送通知的实际工作。它将接受一个 $notification 对象、一个 $components 数组和 一个 $recipient 对象。

  • $notification 对象包含需要发送的通知主题
  • $additionalData 包含传递给《$notificationService->notify()`》方法的第三个参数的数据
  • $recipient 是接收者

`send()` 方法的示例实现可能如下所示

public function send(NotificationInterface $notification, array $additionalData, RecipientInterface $recipient)
{
   if (!isset($this->config['template'])) {
       throw new \Exception('Template must be set in this transporter');
   }
   
   $recipient = $recipient->getComponent()->getData();
   $templateName = $this->config['template'];

   $attach = array();
   $context = array(
       'siteUrlHttp' => $this->siteUrlHttp,
       'subject' => $notification->getSubject()->getData(),
       'notification' => $notification,
       'recipient' => $recipient,
   );

   $email = null;
   if ($recipient instanceof User) {
       $email = $recipient->getEmailCanonical();
   } elseif (is_array($recipient) && isset($recipient['email']) && is_scalar($recipient['email'])) {
       $email = $recipient['email'];
   }

   if (!empty($email)) {
       $this->sendMessage($templateName, $context, $email, $attach);
   }
}

private function sendMessage($templateName, $context, $toEmail, array $attach = null)
{
    // send the email
}