Mailer AbstractTransport doSend() method has no way to return data
Description
Hi, I have to send emails with Mailjet. In order to do it I Extended Symfony\Component\Mailer\Transport\AbstractTransport and implemented doSend method as following:
protected function doSend(SentMessage $message): void
{
$email = MessageConverter::toEmail($message->getOriginalMessage());
$response = Mailjet::post(Resources::$Email, ['body' => $this->buildEmail($email)]);
// workaround 1: set the messageId,
$message->setMessageId($response->getData()['Sent'][0]['MessageID']);
// workaround 2: set the messageId,
$message->appendDebug(json_encode($response->getData()));
// workaround 3: set custom headers:
$email->getHeaders()->add(new MetadataHeader('mailjet-message-id', $response['Sent'][0]['MessageID']));
// workaround 4: fire custom event:
$this->dispatcher->dispatch(new CustomEvent($response));
}
Mailjet returns me the mesageId which i need later, to verify the delivery of the message. (and resend the message if needed)
But I'd like to transmit more information that I received from Mailjet::post to the event dispatcher SentMessageEvent. Now I have 4 workarounds:
- Message Id is the only field available for me to override, so i cant send more information, also overriding it feels wrong.
- Debug is a string field, and encoding the data on one side to decode is in the listener also feels wrong.
- Adding custom Headers for each is inconvenient, since they can handle only string values.
- Dispatching custom event makes the firing of SentMessageEvent inside AbstractTransport::send() method a bit pointless.
Does it make sense to add one more parameter to Symfony\Component\Mailer\SentMessage like private array $context = []; with public setter.
If something is unclear or wrong in my reasoning please feel free to point it out.
Example
No response
But I'd like to transmit more information that I received from Mailjet::post to the event dispatcher SentMessageEvent.
Could you give more details ? Mailjet does not give you more than what you already know (receipients emails, subject, ..) and the message ID.
Does it make sense to add one more parameter to Symfony\Component\Mailer\SentMessage like private array $context = []; with public setter.
Not really, because this is really just a flag (most of the time triggered asynchronously by some background process) that means "hey, i just sent the message XYZ via the transport 123" .. (and with minimal debug information in... debug mode)
Maybe you can tell us what problem in your app you're trying to solve ?
(Side note: instead of going from scratch, you may want to start with the Symfony Mailjet Bridge)
Could you give more details ? Mailjet does not give you more than what you already know (receipients emails, subject, ..) and the message ID.
Mailjet returns messageID and messageUUID. I don't know them before sending the request. The messageID is basically Mailjet internal identifier, which I have to use if I want to get the updated info for the email delivery. https://dev.mailjet.com/email/reference/messages#v3_get_message_message_ID.
I'd like to send the messageID received from the Mailjet to the SentMessageEvent and persist it in my table. I don't want to persist my MailStatus object inside the doSend().
Maybe you can tell us what problem in your app you're trying to solve ?
My problem: I need to be 100% sure that the person received the email. So I save the MailStatus object(consists of MessageId, status and few other fields) in a table. A cron Job which looks over these mails, sends another Request to Mailject https://dev.mailjet.com/email/reference/messages#v3_get_message_message_ID , updates the delivery status and re sends another mail if previous failed.
The way i'd suggest you to do is something like that
(Will you send your emails with messenger ? The answer will be a bit different)
In both case there is a way to get a MessageId just before an email is sent and save it to your database. (either by using the one from the transport or setting a header value manually)
Then later the mail is really sent and THEN you get the real MessageId (from the transport) and you can associate it with yours and save the MailJet id in your database.
Later the message is received by your recipient, you receive a webhook from MailJet with their id, but as you stored it before, you can retrieve it and voilà.
(Side note: instead of going from scratch, you may want to start with the Symfony Mailjet Bridge)
So, I tried to use symfony/mailjet-mailer as you suggested. And I'm almost 100% sure that this https://github.com/symfony/symfony/blob/9a38e818f22dce779c9e3ca9459075326996583d/src/Symfony/Component/Mailer/Bridge/Mailjet/Transport/MailjetApiTransport.php#L102 is a bug. First of all Overriding the MessageId as I mentioned in workaround 1 is not the best choice? Second if we have multiple recipients in To (or cc/bcc) field there wont be just one Message id. Which again brings us to the point that I need an array field inside of Mailer\SentMessage where i can store result info.
This is the dump of $result variable:
In both case there is a way to get a MessageId just before an email is sent and save it to your database. (either by using the one from the transport or setting a header value manually)
I think there is a misunderstanding. I don't think that I need the messageId auto generated by symfony. I need exactly the messageIds of the recipient mails. which I can not know before the actual sending.
Lets make an example : I want to send an email From [email protected] to [email protected] and [email protected]. I composed the email with From : [email protected] To: [email protected], [email protected] Message ID : will be auto generated by symfony before sending. I don't need it?
Mailjet receives my mail request, and will try to sends 2 copies , 1 to [email protected] and 1 to [email protected]. But it gives me back his generated uniqueId for these copies. I need to store exactly them.
Later the message is received by your recipient, you receive a webhook from MailJet with their id, but as you stored it before, you can retrieve it and voilà.
Thanks a lot for the Webhook idea, I'll try to configure it later this day. but for this issue it feels not related?