[Mime] use isRendered
method to ensure we can avoid rendering an email twice
#59596
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Description:
After upgrading my project from Symfony 5.4 to 6.4, due this change, I encountered an issue where asynchronous email sending failed due to serialization problems with
TemplatedEmail
. According to the Symfony documentation, when sending an email asynchronously, the email instance must be serializable. While Mailer instances are inherently serializable,TemplatedEmail
requires that its context be serializable. If the context contains non-serializable variables, such as Doctrine entities, it's recommended to either replace them with serializable variables or render the email before calling$mailer->send($email)
.To address this, I decorated the Symfony
Mailer
to force the rendering of emails using theBodyRendererInterface::render(Message $message): void
method. However, this approach led to an exception indicating that the context was empty. The method successfully rendered the email, clearing its context, but the sameTemplatedEmail
object was rendered again later in the execution process, specifically in the MessageListener.To prevent this double rendering, I utilized the
TemplatedEmail::isRendered(): bool
method, as seen in the AbstractTransport. This ensures that if an email is already rendered, it won't be rendered again, preserving the context and preventing serialization issues.Solution
The proposed fix involves checking if the
TemplatedEmail
has already been rendered before attempting to render it again. By using theisRendered()
method, we can avoid unnecessary re-rendering, which can lead to empty contexts and serialization problems during asynchronous email sending.Impact
This correction allows classes extending
TemplatedEmail
to be sent asynchronously without encountering errors related to double rendering or context loss due to serialization issues, as outlined in the documentation.References
Symfony Mailer Documentation
MessageListener Implementation
AbstractTransport Implementation
This pull request aims to enhance the robustness of asynchronous email handling in Symfony by ensuring that TemplatedEmail instances are not rendered multiple times, thereby maintaining their context and serializability.
Original message:
Following up my comment on https://github.com/symfony/symfony/pull/47075/files.
I would recommend using the method TemplatedEmail::isRendered() the same way it is used in AbstractTransport.php.
Keeping like this prevents subclass of TemplatedEmail with computed html template to not be rendered twice, such as NotificationEmail.
This change will allow developers to create a subclass of template emails and render them before sending them async, without rendering them twice.