Symfony 5.3:立即发送异步电子邮件
Symfony 5.3: Async emails sent immediately
编辑:这个问题出现在试图在同一个应用程序中同时拥有同步和同步电子邮件时。这一点没有说清楚。在撰写本文时,这是不可能的,至少不像这里尝试的那么简单。请参阅下面@msg 的评论。
配置为异步发送电子邮件而不是立即发送电子邮件的电子邮件服务。当 doctrine
或 amqp
选择为 MESSENGER_TRANSPORT_DSN
时会发生这种情况。 doctrine
传输成功创建 messenger_messages
table,但没有内容。这告诉我 MESSENGER_TRANSPORT_DSN
被观察到。 amqp
使用 RabbitMQ 'Hello World' 教程的简单测试表明它已正确配置。
我在下面的代码中遗漏了什么?
下面显示的序列摘要:添加机会 -> OppEmailService
创建电子邮件内容 -> 从 EmailerService
获取 TemplatedEmail()
对象(未显示) -> 提交 TemplatedEmail()
对象 LaterEmailService
,配置为异步。
messenger.yaml:
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
sync: 'sync://'
routing:
'App\Services\NowEmailService': sync
'App\Services\LaterEmailService': async
OpportunityController
:
class OpportunityController extends AbstractController
{
private $newOpp;
private $templateSvc;
public function __construct(OppEmailService $newOpp, TemplateService $templateSvc)
{
$this->newOpp = $newOpp;
$this->templateSvc = $templateSvc;
}
...
public function addOpp(Request $request): Response
{
...
if ($form->isSubmitted() && $form->isValid()) {
...
$volunteers = $em->getRepository(Person::class)->opportunityEmails($opportunity);
$this->newOpp->oppEmail($volunteers, $opportunity);
...
}
OppEmailService
:
class OppEmailService
{
private $em;
private $makeMail;
private $laterMail;
public function __construct(
EmailerService $makeMail,
EntityManagerInterface $em,
LaterEmailService $laterMail
)
{
$this->makeMail = $makeMail;
$this->em = $em;
$this->laterMail = $laterMail;
}
...
public function oppEmail($volunteers, $opp): array
{
...
$mailParams = [
'template' => 'Email/volunteer_opportunities.html.twig',
'context' => ['fname' => $person->getFname(), 'opportunity' => $opp,],
'recipient' => $person->getEmail(),
'subject' => 'New volunteer opportunity',
];
$toBeSent = $this->makeMail->assembleEmail($mailParams);
$this->laterMail->send($toBeSent);
...
}
}
LaterEmailService
:
namespace App\Services;
use Symfony\Component\Mailer\MailerInterface;
class LaterEmailService
{
private $mailer;
public function __construct(MailerInterface $mailer)
{
$this->mailer = $mailer;
}
public function send($email)
{
$this->mailer->send($email);
}
}
我最终将控制台命令创建为 运行 作为每日 cron
作业。每个命令调用创建和发送电子邮件的服务。用例是每天向注册用户发送少量电子邮件,通知他们影响他们的操作。示例如下:
控制台命令:
class NewOppsEmailCommand extends Command
{
private $mailer;
private $oppEmail;
private $twig;
public function __construct(OppEmailService $oppEmail, EmailerService $mailer, Environment $twig)
{
$this->mailer = $mailer;
$this->oppEmail = $oppEmail;
$this->twig = $twig;
parent::__construct();
}
protected static $defaultName = 'app:send:newoppsemaiils';
protected function configure()
{
$this->setDescription('Sends email re: new opps to registered');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$emails = $this->oppEmail->oppEmail();
$output->writeln($emails . ' email(s) were sent');
return COMMAND::SUCCESS;
}
}
OppEmailService:
class OppEmailService
{
private $em;
private $mailer;
public function __construct(EmailerService $mailer, EntityManagerInterface $em)
{
$this->mailer = $mailer;
$this->em = $em;
}
/**
* Send new opportunity email to registered volunteers
*/
public function oppEmail()
{
$unsentEmail = $this->em->getRepository(OppEmail::class)->findAll(['sent' => false], ['volunteer' => 'ASC']);
if (empty($unsentEmail)) {
return 0;
}
$email = 0;
foreach ($unsentEmail as $recipient) {
$mailParams = [
'template' => 'Email/volunteer_opportunities.html.twig',
'context' => [
'fname' => $recipient->getVolunteer()->getFname(),
'opps' => $recipient->getOpportunities(),
],
'recipient' => $recipient->getVolunteer()->getEmail(),
'subject' => 'New opportunities',
];
$this->mailer->assembleEmail($mailParams);
$recipient->setSent(true);
$this->em->persist($recipient);
$email++;
}
$this->em->flush();
return $email;
}
}
电子邮件服务:
class EmailerService
{
private $em;
private $mailer;
public function __construct(EntityManagerInterface $em, MailerInterface $mailer)
{
$this->em = $em;
$this->mailer = $mailer;
}
public function assembleEmail($mailParams)
{
$sender = $this->em->getRepository(Person::class)->findOneBy(['mailer' => true]);
$email = (new TemplatedEmail())
->to($mailParams['recipient'])
->from($sender->getEmail())
->subject($mailParams['subject'])
->htmlTemplate($mailParams['template'])
->context($mailParams['context'])
;
$this->mailer->send($email);
return $email;
}
}
编辑:这个问题出现在试图在同一个应用程序中同时拥有同步和同步电子邮件时。这一点没有说清楚。在撰写本文时,这是不可能的,至少不像这里尝试的那么简单。请参阅下面@msg 的评论。
配置为异步发送电子邮件而不是立即发送电子邮件的电子邮件服务。当 doctrine
或 amqp
选择为 MESSENGER_TRANSPORT_DSN
时会发生这种情况。 doctrine
传输成功创建 messenger_messages
table,但没有内容。这告诉我 MESSENGER_TRANSPORT_DSN
被观察到。 amqp
使用 RabbitMQ 'Hello World' 教程的简单测试表明它已正确配置。
我在下面的代码中遗漏了什么?
下面显示的序列摘要:添加机会 -> OppEmailService
创建电子邮件内容 -> 从 EmailerService
获取 TemplatedEmail()
对象(未显示) -> 提交 TemplatedEmail()
对象 LaterEmailService
,配置为异步。
messenger.yaml:
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
sync: 'sync://'
routing:
'App\Services\NowEmailService': sync
'App\Services\LaterEmailService': async
OpportunityController
:
class OpportunityController extends AbstractController
{
private $newOpp;
private $templateSvc;
public function __construct(OppEmailService $newOpp, TemplateService $templateSvc)
{
$this->newOpp = $newOpp;
$this->templateSvc = $templateSvc;
}
...
public function addOpp(Request $request): Response
{
...
if ($form->isSubmitted() && $form->isValid()) {
...
$volunteers = $em->getRepository(Person::class)->opportunityEmails($opportunity);
$this->newOpp->oppEmail($volunteers, $opportunity);
...
}
OppEmailService
:
class OppEmailService
{
private $em;
private $makeMail;
private $laterMail;
public function __construct(
EmailerService $makeMail,
EntityManagerInterface $em,
LaterEmailService $laterMail
)
{
$this->makeMail = $makeMail;
$this->em = $em;
$this->laterMail = $laterMail;
}
...
public function oppEmail($volunteers, $opp): array
{
...
$mailParams = [
'template' => 'Email/volunteer_opportunities.html.twig',
'context' => ['fname' => $person->getFname(), 'opportunity' => $opp,],
'recipient' => $person->getEmail(),
'subject' => 'New volunteer opportunity',
];
$toBeSent = $this->makeMail->assembleEmail($mailParams);
$this->laterMail->send($toBeSent);
...
}
}
LaterEmailService
:
namespace App\Services;
use Symfony\Component\Mailer\MailerInterface;
class LaterEmailService
{
private $mailer;
public function __construct(MailerInterface $mailer)
{
$this->mailer = $mailer;
}
public function send($email)
{
$this->mailer->send($email);
}
}
我最终将控制台命令创建为 运行 作为每日 cron
作业。每个命令调用创建和发送电子邮件的服务。用例是每天向注册用户发送少量电子邮件,通知他们影响他们的操作。示例如下:
控制台命令:
class NewOppsEmailCommand extends Command
{
private $mailer;
private $oppEmail;
private $twig;
public function __construct(OppEmailService $oppEmail, EmailerService $mailer, Environment $twig)
{
$this->mailer = $mailer;
$this->oppEmail = $oppEmail;
$this->twig = $twig;
parent::__construct();
}
protected static $defaultName = 'app:send:newoppsemaiils';
protected function configure()
{
$this->setDescription('Sends email re: new opps to registered');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$emails = $this->oppEmail->oppEmail();
$output->writeln($emails . ' email(s) were sent');
return COMMAND::SUCCESS;
}
}
OppEmailService:
class OppEmailService
{
private $em;
private $mailer;
public function __construct(EmailerService $mailer, EntityManagerInterface $em)
{
$this->mailer = $mailer;
$this->em = $em;
}
/**
* Send new opportunity email to registered volunteers
*/
public function oppEmail()
{
$unsentEmail = $this->em->getRepository(OppEmail::class)->findAll(['sent' => false], ['volunteer' => 'ASC']);
if (empty($unsentEmail)) {
return 0;
}
$email = 0;
foreach ($unsentEmail as $recipient) {
$mailParams = [
'template' => 'Email/volunteer_opportunities.html.twig',
'context' => [
'fname' => $recipient->getVolunteer()->getFname(),
'opps' => $recipient->getOpportunities(),
],
'recipient' => $recipient->getVolunteer()->getEmail(),
'subject' => 'New opportunities',
];
$this->mailer->assembleEmail($mailParams);
$recipient->setSent(true);
$this->em->persist($recipient);
$email++;
}
$this->em->flush();
return $email;
}
}
电子邮件服务:
class EmailerService
{
private $em;
private $mailer;
public function __construct(EntityManagerInterface $em, MailerInterface $mailer)
{
$this->em = $em;
$this->mailer = $mailer;
}
public function assembleEmail($mailParams)
{
$sender = $this->em->getRepository(Person::class)->findOneBy(['mailer' => true]);
$email = (new TemplatedEmail())
->to($mailParams['recipient'])
->from($sender->getEmail())
->subject($mailParams['subject'])
->htmlTemplate($mailParams['template'])
->context($mailParams['context'])
;
$this->mailer->send($email);
return $email;
}
}