Change core modules to use Symfony Mailer

Created on 10 August 2023, over 1 year ago
Updated 25 August 2023, over 1 year ago

Problem/Motivation

As part of 🌱 [META] Adopt the symfony mailer component Needs review we need to change core modules to use the new Mailer.

Proposed resolution

The steps are described in Migration strategy for moving to Symfony Mailer Active

Remaining tasks

System module

User interface changes

API changes

Data model changes

Release notes snippet

Feature request
Status

Active

Version

11.0 🔥

Component
Mail 

Last updated 31 minutes ago

No maintainer
Created by

🇬🇧United Kingdom adamps

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @adamps
  • last update over 1 year ago
    Custom Commands Failed
  • @adamps opened merge request.
  • 🇬🇧United Kingdom catch

    3) Module code for sending emails
    Modules should change to use the new API as soon as possible, creating an Email Builder. Code for the old API can be removed.

    Change code that calls MailManagerInterface::mail() to instead call MailerInterface.
    Put code from hook_mail() into EmailBuilderInterface::build() if unrelated to config or config() if related to config.
    If the new API uses a different ID or different params, implement EmailBuilderInterface::legacyMail() to perform the conversion.

    This looks to me like once modules have templates and an e-mail builder that they'd be able to remove their hook_mail() implementations, but the MR isn't doing that - is that because they need to have duplicates (hook_mail() + templates) or is it an oversight in the MR?.

  • 🇬🇧United Kingdom adamps

    Thanks @catch, you ask a very good question. Of course you know the BC requirements for Core much better than I do. What I have done is to think carefully about how they apply to a migration strategy from the old mail system to the new one - see Migration strategy for moving to Symfony Mailer Active .

    Eventually of course we would delete hook_mail() implementations. However I believe we need to keep them for now.

    1. Module developers can choose when to switch from the old mail sending API (MailManagerInterface) to the new one (MailerInterface).
    2. Sites can choose when to switch from the mail system with old transports (@Mail plug-ins), hooks (hook_mail_alter()) and email contents to the new ones.
    3. We don't know what in order these switches will take place.

    When we commit this issue, that means Core modules switch to the new API for sending mails. We don't need 'if (new mail system)' or any code calling MailManagerInterface. However we must keep the same contents of emails sent and still call hook_mail_alter() with an old style $message array.

    1) With this patch, the new mailer implementing MailerInterface) calls MailManagerInterface. The two interfaces are quite close, we just need to call EmailBuilder::prepare() to find $to, $langcode, $reply. The mail manager will of course call hook_mail() so we need to keep it.

    2) The alternative is to cut out the old mail manager entirely. We would call EmailBuilder::build() then convert the EmailInterface to a $message. The problem is that the email contents won't be identical. For update mails, we've upgraded the body to HTML. For simplenews the $message array contains many non-standard parameters and the email contents will likely be quite different using the new API. And the new mail system will fix bugs of the old one that we are currently postponing because it's non-BC.

  • 🇨🇭Switzerland berdir Switzerland

    @catch: For now, this is just to explore how the API that modules use will look like. see the meta issue for BC discussions.

  • 🇬🇧United Kingdom catch

    Of course you know the BC requirements for Core much better than I do.

    Ha I usually know what the requirements are, but knowing how to meet them is the tricky bit.

    Started writing a comment but then realised it should probably be on Migration strategy for moving to Symfony Mailer Active so posting over there instead.

  • last update over 1 year ago
    Custom Commands Failed
  • 🇬🇧United Kingdom adamps

    I thought some more. I feel we have two important motivations/ideas:

    1) Building an email is done with a 'pipeline' of multiple phases. This is a pattern that exists many times in Drupal already. A form class has build, validate and submit. Rendering a page includes rendering some entities which calls view() but the processing and rendering is deferred until later, which can include a preRender callback. So the building code needs a structure to participate in the different stages.

    2) The act of sending email is a good example of a service. It should expose a clear interface without being forced to fit a structure that's not appropriate. There might be one function on the interface or many, and each could have arbitrary parameters. Each call to a function might send zero, one or many emails.

    ===

    Good news is that we can have both: the user module can implements both EmailBuilderInterface and UserMailerInterface. Not only that, we could use the same class to implement both. We can have a @Mailer plug-in, implementing EmailBuilderInterface, plus an implementation-specific mailer interface.

    Code that wants to send an email can write:

    Drupal::mailer('user')->mail('password_reset', $account);
    

    In Drupal.php:

    public static function mailer(string $id) {
      return static::getContainer()->get('plugin.manager.mailer')->createInstance($id);
    }
    

    Thoughts??

  • last update over 1 year ago
    Custom Commands Failed
Production build 0.71.5 2024