Since Drupal 8.0.0-beta12 "all rendering must happen in a render context", as stated by this change record.
This is to avoid bubbleable metadata lost. So far, so good.
Let's say I'm implementing a Commerce payment notification. The important part is a server calls a Drupal URL to notify something. The controller, as far as I know, doesn't need any rendering context because the answer is simple (almost just a status response). Good.
Let's say I want to send an email on this event. I create an EventSubscriber that subscribes to the payment event that happens on this callback. I hook on the event, gather the email data, and call the send function. During sending, Drupal renders the content (is an HTML email!) and I got the error: since there's no rendering context Drupal throws an exception. The point the error is triggered is when Token::replace() is called:
$body = $this->token->replace($this->getBody(), $this->getTokenData());
How to fix this?
Option 1:
Use the BubbleableMetadata $bubbleable_metadata
fourth param of Token::replace(). This param docs states:
An object to which static::generate() and the hooks and functions that it invokes will add their required bubbleable metadata.
To ensure that the metadata associated with the token replacements gets attached to the same render array that contains the token-replaced text, callers of this method are encouraged to pass in a BubbleableMetadata object and apply it to the corresponding render array.
But because $body
is just a plain string I guess the bubbleable metadata is lost, so what's the point of caring?
Option 2: Follow the change record suggestion and use RenderContext::executeInRenderContext(). In that case I'd wrap the whole process:
$renderer = \Drupal::service('renderer');
$context = new RenderContext();
$that = $this;
$response = $renderer->executeInRenderContext($context, function() use ($that, $recipient) {
$that->mailer->send($recipient);
});
Again, I'm discarding the cacheable metadata.
I guess discarding that cacheable data is ok because the email is totally uncacheable, the whole body and its parts (because the replaced strings with tokens have URLs that would change on every email).
Are both approaches ok? Is one better than the other? Is there a third approach more suitable for this problem? I guess the problem can be summarized as how to deal with Bubbleable Metadata when there's no rendering context and no render array.