| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- <?php
- declare(strict_types=1);
- namespace App\Service\Mailer;
- use App\Entity\Message\ReportEmail;
- use App\Enum\Core\EmailSendingTypeEnum;
- use App\Enum\Message\MessageStatusEnum;
- use App\Enum\Message\ReportMessageStatusEnum;
- use App\Enum\Utils\EnvironnementVarEnum;
- use App\Service\Mailer\Model\MailerModelInterface;
- use App\Service\ServiceIterator\Mailer\BuilderIterator;
- use App\Service\Utils\Environnement;
- use App\Service\Utils\StringsUtils;
- use App\Tests\Service\Mailer\MailerTest;
- use Doctrine\Common\Collections\ArrayCollection;
- use Doctrine\ORM\EntityManagerInterface;
- use Psr\Log\LoggerInterface;
- use Symfony\Bridge\Twig\Mime\TemplatedEmail;
- use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
- use Symfony\Component\Mailer\MailerInterface;
- use Symfony\Component\Mime\Address;
- use Symfony\Component\Mime\Email as SymfonyEmail;
- /**
- * Classe Mailer : Service assurant l'envoi d'un mail à un destinataire.
- */
- class Mailer
- {
- public function __construct(
- private readonly MailerInterface $symfonyMailer,
- private readonly string $opentalentNoReplyEmailAddress,
- private readonly BuilderIterator $builderIterator,
- private readonly StringsUtils $stringsUtils,
- private readonly EntityManagerInterface $entityManager,
- private readonly Environnement $environnement,
- private readonly LoggerInterface $logger
- ) {
- }
- /**
- * Main fonction qui itère les différentes étapes nécessaires à l'envoi d'un email
- * - Le Build
- * - Le Send
- * - Le Reporting.
- *
- * @throws TransportExceptionInterface
- *
- * @see MailerTest::testMain()
- */
- public function main(MailerModelInterface $mailerModel): ArrayCollection
- {
- // TODO: est-ce qu'on l'appellerait pas plutôt 'send' celle-ci? et est-ce qu'on ne passerait pas les autres en
- // private? j'ai peur qu'on se trompe parfois entre l'une et l'autre
- $builderService = $this->builderIterator->getBuilderFor($mailerModel);
- $emailsCollection = $builderService->build($mailerModel);
- $emailsCollection = $this->reduceEmailsCollectionInPreproduction($emailsCollection);
- /** @var Email $email */
- foreach ($emailsCollection as $email) {
- // si l'email n'a pas de destinataire, on ne l'envoi pas...
- if ($email->getEmailRecipients()->isEmpty()) {
- $email->getEmailEntity()->setStatus(MessageStatusEnum::NO_RECIPIENT);
- continue;
- }
- // Envoi du mail
- $this->send($email);
- // Persistance de l'entité Email
- $this->persistEmailEntity($email);
- }
- // Envoi du rapport
- $this->sendReport($emailsCollection);
- $this->entityManager->flush();
- return $emailsCollection;
- }
- /**
- * Fonction d'envoi.
- *
- * @throws TransportExceptionInterface
- *
- * @see MailerTest::testSend()
- */
- public function send(Email $email): void
- {
- // On créé le mail
- $symfonyMail = $this->createSymfonyEmail($email);
- $this->addRecipients($symfonyMail, $email);
- $this->setAntiSpam($email, $symfonyMail->getTo());
- $this->setSymfonyEmailContent($symfonyMail, $email);
- $this->addHeaders($symfonyMail, $email);
- // @todo
- // $this->addAttachments();
- // On tente d'envoyer
- try {
- $this->symfonyMailer->send($symfonyMail);
- $email->getEmailEntity()->setStatus(MessageStatusEnum::SEND);
- $email->getEmailEntity()->setDateSent(new \DateTime('now'));
- } catch (\Exception $e) {
- $this->logger->error('Error while sending email');
- $this->logger->error($e->getMessage());
- $email->getEmailEntity()->setStatus(MessageStatusEnum::FAILED);
- }
- }
- /**
- * Envoi le rapport d'envoi.
- *
- * @param ArrayCollection<Email> $emails
- *
- * @see MailerTest::testSendReport()
- */
- public function sendReport(ArrayCollection $emails): void
- {
- $reportEmail = $this->createReportEmail($emails);
- try {
- $this->symfonyMailer->send($reportEmail);
- } catch (TransportExceptionInterface $e) {
- $this->logger->error('Error while sending report');
- $this->logger->error($e->getMessage());
- }
- }
- /**
- * Création du Mail qui sera envoyé via le Mailer de Symfony.
- *
- * @see MailerTest::testCreateSymfonyEmail()
- */
- public function createSymfonyEmail(Email $email, bool $isSystemEmail = false): SymfonyEmail {
-
- $addressMailFrom = $isSystemEmail ? new Address('ne-pas-repondre@opentalent.com', 'Opentalent') :
- new Address($email->getFrom(), $email->getFromName());
- $symfonyEmail = (new SymfonyEmail())
- ->from($addressMailFrom)
- ->subject($email->getEmailEntity()->getAbout());
- if (!$isSystemEmail) {
- $symfonyEmail->replyTo($addressMailFrom)
- ->returnPath(Address::create('mail.report@opentalent.fr'));
- }
- return $symfonyEmail;
- }
- /**
- * @see MailerTest::testSetSymfonyEmailContent()
- */
- public function setSymfonyEmailContent(SymfonyEmail $symfonyEmail, Email $email): void
- {
- $symfonyEmail
- ->html($email->getContent())
- ->text($this->stringsUtils->convertHtmlToText($email->getContent()));
- }
- /**
- * Créé le Templated Email contenant le rapport d'envoi.
- *
- * @param ArrayCollection<Email> $emails
- *
- * @see MailerTest::testCreateReportEmail()
- */
- public function createReportEmail(ArrayCollection $emails): TemplatedEmail
- {
- [$delivered, $unDelivered] = $this->getDeliveredAndUndelivered($emails);
- /** @var Email $email */
- $email = $emails->first();
- return (new TemplatedEmail())
- ->from($this->opentalentNoReplyEmailAddress)
- ->subject(sprintf('Rapport d\'envoi du message : %s', $email->getEmailEntity()->getAbout()))
- ->htmlTemplate('@templates/emails/report.html.twig')
- ->context(
- [
- 'email_example' => $email->getEmailEntity(),
- 'delivered' => $delivered,
- 'unDelivered' => $unDelivered,
- ]
- )
- ->addTo(new Address($email->getFrom(), $email->getFromName()));
- }
- /**
- * Récupère les recipient delivered & undelivered de l'ensemble des emails.
- *
- * @param ArrayCollection<Email> $emails
- *
- * @return list<EmailRecipient[]>
- *
- * @see MailerTest::testGetDeliveredAndUndelivered()
- */
- public function getDeliveredAndUndelivered(ArrayCollection $emails): array
- {
- $delivered = [];
- $unDelivered = [];
- foreach ($emails as $email) {
- $emailRecipients = $email->getEmailRecipients();
- /** @var EmailRecipient $emailRecipient */
- foreach ($emailRecipients as $emailRecipient) {
- if (ReportMessageStatusEnum::MISSING === $emailRecipient->getSendStatus()) {
- $unDelivered[] = $emailRecipient;
- } elseif (ReportMessageStatusEnum::DELIVERED === $emailRecipient->getSendStatus()) {
- $delivered[] = $emailRecipient;
- }
- }
- }
- return [$delivered, $unDelivered];
- }
- /**
- * Persist l'Email.
- *
- * @see MailerTest::testPersistEmailEntity()
- */
- public function persistEmailEntity(Email $email): void
- {
- $emailEntity = $email->getEmailEntity();
- /** @var EmailRecipient $emailRecipient */
- foreach ($email->getEmailRecipients() as $emailRecipient) {
- $emailEntity->addReport($this->createReport($emailRecipient));
- }
- $this->entityManager->persist($emailEntity);
- }
- /**
- * Création du rapport.
- *
- * @see MailerTest::testCreateReport()
- */
- public function createReport(EmailRecipient $emailRecipient): ReportEmail
- {
- $reportEmail = new ReportEmail();
- $reportEmail
- ->setAddressEmail($emailRecipient->getEmailAddress())
- ->setAccess($emailRecipient->getAccess())
- ->setOrganization($emailRecipient->getOrganization())
- ->setDateSend(new \DateTime('now'))
- ->setStatus($emailRecipient->getSendStatus());
- // Return en deux temps car setStatus renvoi un AbstractReport et non un ReportEmail
- return $reportEmail;
- }
- /**
- * Reduit le nombre d'emails a envoyer si on ne se trouve pas en prod.
- *
- * @param ArrayCollection<Email> $emailsCollection
- *
- * @return ArrayCollection<Email>
- *
- * @see MailerTest::testReduceEmailsCollectionInPreproduction()
- */
- public function reduceEmailsCollectionInPreproduction(ArrayCollection $emailsCollection): ArrayCollection
- {
- if ('prod' !== $this->environnement->get(EnvironnementVarEnum::APP_ENV->value) && $emailsCollection->count() > 20) {
- $startEmails = $emailsCollection->slice(0, 10);
- $endEmails = $emailsCollection->slice($emailsCollection->count() - 11, 10);
- return new ArrayCollection([...$startEmails, ...$endEmails]);
- }
- return $emailsCollection;
- }
- /**
- * @see MailerTest::testAddHeaders()
- */
- public function addHeaders(SymfonyEmail $symfonyMail, Email $email): void
- {
- $symfonyMail->getHeaders()->addTextHeader('List-Unsubscribe', 'mailto:' . $email->getFrom() . '?subject=désabonnement');
- $symfonyMail->getHeaders()->addTextHeader('X-ID-OT', $email->getEmailEntity()->getUuid()->toString());
- }
- /**
- * On change le #__#ANTISPAM_PERSON_EMAIL#__# par la liste des emails afin d'éviter le spamming sur l'envoi en masse.
- *
- * @param list<Address> $addresses
- *
- * @see MailerTest::testSetAntiSpam()
- */
- public function setAntiSpam(Email $email, array $addresses): void
- {
- // map des Address pour ne conserver qu'un tableau d'email
- $to = array_map(function (Address $address) {
- return $address->getAddress();
- }, $addresses);
- $email->setContent(str_replace('#__#ANTISPAM_PERSON_EMAIL#__#', implode(',', $to), $email->getContent()));
- }
- /**
- * On ajoute les destinataires suivant le type d'envoi souhaité.
- *
- * @see MailerTest::testAddRecipients()
- */
- public function addRecipients(SymfonyEmail $symfonyMail, Email $email): void
- {
- $allReadySend = [];
- foreach ($email->getEmailRecipients() as $emailRecipient) {
- $addressMail = new Address($emailRecipient->getEmailAddress(), $emailRecipient->getName());
- // On envoi pas en double
- if (!in_array($addressMail, $allReadySend)) {
- $allReadySend[] = $addressMail;
- switch ($emailRecipient->getSendType()) {
- case EmailSendingTypeEnum::TO:
- $symfonyMail->addTo($addressMail);
- break;
- case EmailSendingTypeEnum::BBC:
- $symfonyMail->addBcc($addressMail);
- break;
- case EmailSendingTypeEnum::CC:
- $symfonyMail->addCc($addressMail);
- break;
- }
- }
- }
- }
- }
|