Shopware 6:SwagProductReviews邮件黑名单集成失效及事件改造需求
SwagProductReviews 集成 EmailBlackList 问题解决与改造方案
问题背景
使用SwagProductReviews插件通过定时任务发送评价邀请邮件,需要集成EmailBlackList插件实现:
- 拦截黑名单邮箱的邮件发送
- 标记黑名单对应的订单为“已发送”,避免定时任务重复处理
当前存在两个问题:
- 黑名单校验逻辑失效,黑名单邮箱仍能收到邮件
- 需要通过事件驱动实现插件间解耦,让
EmailBlackList监听发送前事件并决定是否拦截
现有代码问题分析
1. MailHelper.php sendMail 方法逻辑错误
原代码中黑名单校验的条件为:
if (!$testMail && !$this->getConfig()->isSendMails() && $this->isEmailBlacklisted($email, $context))
这里的逻辑完全错误——只有当插件配置禁止发送邮件时才会校验黑名单,这和需求完全相反,导致黑名单拦截从未触发。
2. 插件间耦合度高
当前通过装饰器MailHelperWithBlacklist.php修改发送逻辑,耦合度高且可能未正确注册,导致装饰器未生效。
解决方案
步骤1:修复黑名单校验逻辑
修改MailHelper.php的sendMail方法,修正条件判断:
public function sendMail(array $data, Context $context, array $templateData = [], bool $testMail = false): ?Email { $email = $data['email']; // 仅非测试邮件时校验黑名单,去掉错误的配置判断 if (!$testMail && $this->isEmailBlacklisted($email, $context)) { $this->loggerHelper->addDirectRecord(Logger::DEBUG, 'Sending Mail blocked by blacklist.'); return null; } if ($testMail) { $this->saveTemplateData($context, $data['technicalName'], $templateData); } return $this->getMailService()->send( $data, $context, $templateData ); }
步骤2:实现事件驱动的插件解耦
2.1 在SwagProductReviews中定义前置事件
创建事件类BeforeSendProductReviewInvitationEvent.php:
<?php namespace Swag\ProductReviews\Event; use Shopware\Core\Checkout\Order\OrderEntity; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\Event\ShopwareEvent; use Symfony\Contracts\EventDispatcher\Event; class BeforeSendProductReviewInvitationEvent extends Event implements ShopwareEvent { public const NAME = 'swag.product_reviews.before_send_invitation'; private OrderEntity $order; private string $email; private Context $context; private bool $blocked = false; public function __construct(OrderEntity $order, string $email, Context $context) { $this->order = $order; $this->email = $email; $this->context = $context; } public function getOrder(): OrderEntity { return $this->order; } public function getEmail(): string { return $this->email; } public function getContext(): Context { return $this->context; } public function isBlocked(): bool { return $this->blocked; } public function setBlocked(bool $blocked): void { $this->blocked = $blocked; } }
2.2 在MailHelper中注入事件调度器并触发事件
修改MailHelper的构造函数,注入EventDispatcherInterface:
use Symfony\Component\EventDispatcher\EventDispatcherInterface; class MailHelper implements MailHelperInterface { // 其他已声明的属性 private EventDispatcherInterface $eventDispatcher; public function __construct( // 其他原有依赖 EventDispatcherInterface $eventDispatcher ) { // 原有依赖赋值 $this->eventDispatcher = $eventDispatcher; } }
修改sendInvitationMail方法,在发送邮件前触发事件并处理拦截逻辑:
public function sendInvitationMail(Context $outerContext, OrderEntity $order, bool $testMail = false): bool { try { $message = null; $context = $this->getOrderContext($outerContext, $order); if (!is_null($order->getLineItems()) && count($order->getLineItems()) > 0 && !is_null($order->getOrderCustomer()) && !is_null($order->getOrderCustomer()->getEmail()) && !is_null($order->getOrderCustomer()->getCustomerId())) { if ($this->orderService->allProductsRated($order, $outerContext)) { $this->markInvitationAsAlreadyRated($order, $outerContext); if (!$testMail) { return true; } } $data = $this->prepareMailTemplate(MailHelperInterface::MAIL_TEMPLATE_INVITATION, $context, $order, $testMail); $salesChannel = $this->getSalesChannel($context, $order); if (!$testMail) { $data->set( 'recipients', [ $order->getOrderCustomer()->getEmail() => $order->getOrderCustomer()->getFirstName() . ' ' . $order->getOrderCustomer()->getLastName(), ] ); if ($this->getConfig()->isSendInvitationCopy()) { $storeOwnerEmail = $this->getStoreOwnerEmail(); $data->set('recipientsBcc', $storeOwnerEmail); } } $helper = new SwagProductReviewsHelper(); $orderHash = $helper->getOrderHash($order); $hashShort = substr($orderHash, 0, 6); // 新增:触发前置事件,检查是否被拦截 $event = new BeforeSendProductReviewInvitationEvent($order, $order->getOrderCustomer()->getEmail(), $context); $this->eventDispatcher->dispatch($event, BeforeSendProductReviewInvitationEvent::NAME); if ($event->isBlocked()) { $this->markInvitationAsSent($order, $context); $this->loggerHelper->addDirectRecord(Logger::INFO, 'Invitation blocked by blacklist, marked order as sent.', ['Order id' => $order->getId(), 'Order Number' => $order->getOrderNumber()]); return true; // 返回true让定时任务认为已处理 } $message = $this->sendMail( $data->all(), $context, [ 'order' => $order, 'salesChannel' => $salesChannel, 'hash' => $hashShort ], $testMail ); // 原有后续逻辑... } else { // 原有逻辑... } } catch (\Throwable $e) { // 原有异常处理... } }
2.3 在EmailBlackList中创建事件监听器
创建监听器类BlockReviewInvitationListener.php:
<?php namespace YourNamespace\EmailBlackList\EventListener; use Swag\ProductReviews\Event\BeforeSendProductReviewInvitationEvent; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; class BlockReviewInvitationListener { private EntityRepository $blacklistRepository; public function __construct(EntityRepository $blacklistRepository) { $this->blacklistRepository = $blacklistRepository; } public function __invoke(BeforeSendProductReviewInvitationEvent $event): void { $email = $event->getEmail(); $context = $event->getContext(); $criteria = new Criteria(); $criteria->addFilter(new EqualsFilter('email', $email)); $blacklistEntry = $this->blacklistRepository->search($criteria, $context)->first(); if ($blacklistEntry !== null) { $event->setBlocked(true); } } }
2.4 注册监听器
在EmailBlackList插件的src/Resources/config/services.xml中添加监听器注册:
<service id="YourNamespace\EmailBlackList\EventListener\BlockReviewInvitationListener"> <argument type="service" id="email_blacklist.repository"/> <!-- 替换为你的黑名单仓库ID --> <tag name="kernel.event_listener" event="swag.product_reviews.before_send_invitation" method="__invoke"/> </service>
步骤3:移除冗余的装饰器
事件驱动方案已替代MailHelperWithBlacklist.php的功能,可删除该装饰器及相关注册配置,降低耦合度。
效果验证
- 将邮箱加入黑名单,执行定时任务
- 查看日志,应出现
Invitation blocked by blacklist, marked order as sent.的记录 - 该订单不会被定时任务重复处理,且黑名单邮箱不会收到邮件
内容的提问来源于stack exchange,提问作者mlodhi
相关产品推荐
相关产品推荐

