Create a new mailer service to wrap symfony mailer

Created on 18 June 2025, 15 days ago

Problem/Motivation

Part of stage 2 of 🌱 [META] Adopt the symfony mailer component Needs review .

Create a new service that performs some additional Drupal-specific processing when sending an email. This mostly means copying various parts of the old mail system into the new one:

  1. Core MailManager
  2. Contrib mailsystem module
  3. Mailer plugins, such as Contrib Drupal Symfony Mailer Lite (DSM-L)
  4. plus potentially taking some new ideas from Contrib Drupal Symfony Mailer (DSM)

Proposed resolution

Class Mailer that implements MailerInterface. Functions of the mailer are:

  1. Set some defaults (currently in MailManager)
  2. Invoke events/callbacks (currently in MailManager)
  3. Switch render context to avoid pollution (currently in MailManager)
  4. Convert relative URLs to absolute (currently in MailManager)
  5. Ensure that subject is plain text (currently in MailManager)
  6. Switch theme (currently in mailsystem)
  7. Switch language (currently done in Commerce module, and would save more than half the code in user_mail())
  8. Switch account (currently done in Simplenews module, ensures security and personalisation)
  9. Render templates (currently done in DSM-L), which is a 2-stage process, first part module specific (done in simplenews, commerce) second part a generic wrapper which is generated separately for HTML and plain
  10. Convert to plain-text (currently done in DSM-L) - Symfony will do it, but in quite a different way from Drupal
  11. Inline CSS (currently done in DSM-L)
  12. Attachment access checking (some done in DSM-L)
  13. Act as a factory for Email objects (currently in DSM), which helps with dependency injection and allows Contrib code to override the Email class

The Email building pipeline proceeds in the following sequence:

  • Init phase: set language, theme, user account only
  • Switching: render context (to avoid leaking into current request), language, theme, user account (for access control and correct rendering of entities)
  • Build phase: allow modules to build the unrendered email
  • Rendering: render twig, replace tokens
  • Post-render phase: allow modules to process the rendered email
  • Send email
  • Post-send phase: allow modules to react to the sent email

The implementation can be divided between various classes, each one is independently replaceable for customisation.

  • Mailer class
  • Email class
  • Processor classes for specific function such as inline CSS

Remaining tasks

User interface changes

Introduced terminology

API changes

Possible interface for Mailer service:

  /**
   * Creates a new email.
   *
   * @param string $tag
   *   Tag used to identify the type or source of this email.
   *   @see \Drupal\symfony_mailer\EmailInterface::getTag()
   *
   * @return \Drupal\symfony_mailer\EmailInterface
   *   The new email.
   */
  public function newEmail(string $tag): EmailInterface;

  /**
   * Sends an email.
   *
   * @param \Drupal\symfony_mailer\InternalEmailInterface $email
   *   The email to send.
   *
   * @return bool
   *   Whether successful.
   */
  public function send(InternalEmailInterface $email): bool;

Example usage of the interface, taken from Contrib DSM module implementation of emails for the Core user module.


  /**
   * {@inheritdoc}
   */
  public function notify(string $op, UserInterface $user): bool {
    if ($op == 'register_pending_approval') {
      $this->newEmail("{$op}_admin")->setEntityParam($user)->send();
    }

    return $this->newEmail($op)
      ->setEntityParam($user)
      ->setTo($user)
      ->send();
  }


Data model changes

Release notes snippet

Feature request
Status

Active

Version

11.0 🔥

Component

mail system

Created by

🇬🇧United Kingdom adamps

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

Comments & Activities

Production build 0.71.5 2024