Migration strategy for moving to Symfony Mailer

Created on 10 August 2023, over 1 year ago
Updated 23 October 2023, about 1 year ago

Problem/Motivation

One of the key challenges of 🌱 [META] Adopt the symfony mailer component Needs review is to create a smooth migration process for contrib modules and sites. We should allow site builders to choose when the key steps in the migration occur.

We need to migrate :

  1. the mailsystem itself: MailManager
  2. email transports: @Mail plug-ins implementing of Drupal\Core\Mail\MailInterface
  3. the API for altering mail: hook_mail_alter()
  4. the API for sending and building mail: MailManagerInterface and hook_mail()
  5. email configuration: user.mail.yml etc – upgrade to HTML body and all the other possibilities of a modern email interface as part of Create a system for configurable emails in Symfony Mailer Active

Proposed resolution

1) We will introduce the new mail system in a minor version, at first as experimental. The old mail system will eventually be deprecated, then removed in a major version. During the transition period, Core provides both mail systems. The site builder chooses the mail system using a global config setting. The choice of mail system also determines the choice of transports and hooks.

  • The old mail system will use old transports (@Mail plugins) and call old hooks (hook_mail_alter()).
  • The new mail system will use Symfony transports and some new events/plug-ins.
  • During the experimental period, there won't be a GUI for the setting.

However the choice of API for sending/building mail is not tied to the mail system. The site builder can enable the new mail system without waiting for all modules to upgrade their sending/building code. Drupal can automatically convert from the old sending/building API to the new one in most cases - see Create a BC mapper for MailManager to convert to Symfony Mailer Active .

2) Old-style @Mail plug-ins are completely different from symfony transports. Already symfony provides a variety of third-party plugins. As part of switching to the new mail system, the site builder needs to find or write a suitable symfony transport.

3) Contrib modules whose primary purpose is altering email have a choice:

  • support both APIs in the same version during the transition
  • switch to the new API in a new major version - in which case code should use hook_requirements() to raise an error for the incompatible mail system.

As part of switching to the new mail system, the site builder needs update the above Contrib modules and rewrite any custom code for altering mail.

4) Modules that send mail should switch to the new sending/building API as soon as conveniently possible. As part of this, they are encouraged to make use of new features such as HTML emails.

Modules can safely delete code for the old sending API, as Drupal can automatically convert from new to old. This is easy and safe because both APIs are similar and in both mail systems almost all the work is done on the building API. If modules use the new API in a different way, for example changing the key or parameters, then they need to implement a legacyMail() callback to assist with the conversion.

Drupal can also automatically convert from old to new building API. This is best effort, and it can lead to altered emails compared with using the old building code - especially if the module is using new features such as HTML emails. Therefore, modules have a choice how to transition to the new building API:

  • Use a minor version to add support for the new API, retaining the old hook_mail() API during the transition. Core modules will likely do this, giving full BC. It's less recommended for Contrib, see below.
  • Use a major version to switch to use the new API; new code uses hook_requirements() to raise an error for sites using the old mail system. Recommended for most Contrib modules.
  • Use a minor version to switch to use the new API. Document in the release note that there will be alterations to emails even for sites still using the old mail system. Good for Contrib modules if the emails haven't changed much.

Contrib modules could use the first option, however there is a complication. Sites could enable the new mail system, whist running a version of the contrib module that uses the old API. As soon as they upgrade to the new module version with support for both APIs, then the site will immediately switch to the new API, leading to changes in emails. Core will not include a config option to control the API switch in this case, however Contrib could do it. This complication doesn't apply to Core modules because they will already include code for the new API in the first Core release that contains the new mail system. This option could also work well for Contrib modules that release code for the new API simultaneously with the Core release containing the new mail system.

5) Create a system for configurable emails in Symfony Mailer Active will likely come to Core after the main mail system release, or it might always remain in Contrib. However we can already improve compatibility with the system with a simple change to Mailer code. Any code that reads configuration to set parameters on the email should be put inside if (getConfig()). This means it's easy to adopt a generic system for configurable emails later without needing to rewrite existing Mailer plugins.

Remaining tasks

User interface changes

API changes

Changes for module code that sends 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.

  1. Create a xxxMailer class as a @Mailer plugin, implementing xxxMailerInterface.
  2. Change code that calls MailManagerInterface::mail() to instead call xxxMailerInterface.
  3. Put code from hook_mail() into xxxMailer::build(). Put code that reads config inside if tests calling getConfig().
  4. If the new API uses a different ID or different params, implement xxxMailer::legacyMail() to perform the conversion.

Data model changes

Release notes snippet

Feature request
Status

Active

Version

11.0 🔥

Component
Mail 

Last updated 5 days 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
  • 🇨🇭Switzerland berdir Switzerland

    Note that my thoughts on this are in the meta issue 🌱 [META] Adopt the symfony mailer component Needs review , comment #89 and #93. IMHO that discussion fits into the meta issue and the proposal here doesn't quite match my proposal. If we do want to move it here then it should imho summarize the different approaches that have already been discussed over there.

  • 🇬🇧United Kingdom catch

    Coming from #3380476-5: Change core modules to use Symfony Mailer .

    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.

    This is where I get a bit lost, but I think it means this:

    1. Core modules define e-mails with both hook_mail_alter() and the new message templates, so we have duplicate e-mail definitions until we drop the old ones - which is the change in Change core modules to use Symfony Mailer Active .

    2. Sites can then opt-in to sending e-mails generated with the new message templates instead of hook_mail()/hook_mail_alter().

    For #2 to work, contrib modules that alter emails need to have been updated so they handle both approaches.

    However contrib modules that define e-mails may or may not have been updated at this point - if they only have the new message templates, those will be used, if they define both, the new message templates will be used, but if they only implement hook_mail(), we'll detect this and fall back to that for the specific module.

    I think that can work to allow everything to move at different times, the cost is keeping duplicates of e-mails definitions until Drupal 11 but they don't change often anyway.

    That leaves one more case though:

    What happens when a contrib module only defines e-mails using the new API (say because it's a brand new module) and not hook_mail(), and a site is still configured to send hook_mail() e-mails by default, would we 'fall forwards' to the template e-mails in that case?

    And then I guess in Drupal 11 we force everything to use the new system everywhere and drop hook_mail().

  • 🇬🇧United Kingdom adamps

    @Berdir

    the proposal here doesn't quite match my proposal

    I read your proposal carefully, and I believe I'm pretty close to it. I have included a global feature flag as you suggested.

    I made one deliberate change because I thought it wouldn't work otherwise. I also added a per-module flag. That's because often a contrib will module adds support for the new API in a patch version. If a site has the global feature flag enabled, then suddenly emails from this module will switch to use the new API without any warning/control. However the site might have hooks for that module using the old hook_mail_alter(). Does that make sense to you?

    Any other differences from your proposal are because I didn't understand what you meant. Also some of the details are likely not entirely right however it should become clearer once we have other pieces in place.

    ===

    @catch very good summary thank you.

    What happens when a contrib module only defines e-mails using the new API

    My idea is that the mailer can automatically map to the old API. The interfaces are pretty similar.

  • 🇬🇧United Kingdom adamps

    Issue summary updated. Now simpler, closer to suggestion from Berdir, and explained in a lot more detail.

Production build 0.71.5 2024