Programmatically sending emails with attachments in Drupal 10 using Symfony Mailer

By dzhebrak, 7 April, 2024

Drupal Symfony Mailer is a module that introduces a new mail system based on the Symfony Mailer, the successor to the Swift Mailer library. It offers full support for HTML emails, file attachments, embedded images, and more.

This article details how to send HTML emails with attachments in Drupal programmatically. Let's assume you need to send a PDF file attached to an email. The PDF file can be stored as a media document, but depending on your requirements it could also be any file stored for example in a field of some content type.

Installation and Setup

The first step is to install and enable the Drupal Symfony Mailer module:

composer require 'drupal/symfony_mailer:^1.4'
vendor/bin/drush en symfony_mailer

For this demonstration, a custom module named mailer_example is created and enabled as well.

Creating a Custom Email Builder

In Drupal Symfony Mailer, the EmailBuilder is a plugin responsible for constructing the content of specific email types. It allows you to dynamically define the email's subject, body, available variables, and other settings based on the type of email being sent.

To create a custom EmailBuilder, you need to extend Drupal\symfony_mailer\Processor\EmailBuilderBase and override the build method:

<?php declare(strict_types=1);

namespace Drupal\mailer_example\Plugin\EmailBuilder;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\media\Entity\Media;
use Drupal\symfony_mailer\Annotation\EmailBuilder;
use Drupal\symfony_mailer\EmailInterface;
use Drupal\symfony_mailer\Processor\EmailBuilderBase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * @EmailBuilder(
 *     id = "mailer_example_builder",
 *     sub_types = {"default"},
 *     label = "Mailer example builder",
 *     common_adjusters = {"email_subject", "email_body",},
 *   )
 */
class ExampleEmailBuilder extends EmailBuilderBase implements ContainerFactoryPluginInterface {

  private const MEDIA_ID = 1;

  public function __construct(array $configuration, $plugin_id, $plugin_definition, private EntityTypeManagerInterface $entityTypeManager, private FileSystemInterface $fileSystem) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
  }

  #[\Override] public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static($configuration, $plugin_id, $plugin_definition, $container->get('entity_type.manager'), $container->get('file_system'),);
  }

  public function build(EmailInterface $email) {
    $file = $this->getMediaFile(self::MEDIA_ID);

    if ($file instanceof FileInterface) {
      $email->attachFromPath($this->fileSystem->realpath($file->getFileUri()));
    }
  }

  private function getMediaFile(int $mediaId): ?File {
    $media = $this->entityTypeManager->getStorage('media')->load($mediaId);

    if ($media instanceof Media) {
      return $this->entityTypeManager->getStorage('file')
        ->load($media->getSource()->getSourceFieldValue($media));
    }

    return NULL;
  }

}

Within the build method, you can define the email's subject, body, and attach files using $email->attachFromPath($filepath). You can add as many files as needed with this method.

Adding a New Symfony Mailer Policy

A Mailer Policy defines how emails are sent from your Drupal site. It allows you to configure the email subject, body, set the email priority, and more. 

  1. Visit Configuration > System > Mailer and click Add policy.
  2. Choose Mailer example builder (the label of your created EmailBuilder) as the type, then click Add and configure.
  3. Add the desired content for the Subject and Body elements.
Add Drupal Symfony Mailer policy

Remember that you can export the policy and place it in the config/install directory of your module.

Configuring SMTP Transport (Optional)

Depending on your setup, you might also need to add an SMTP transport.

  1. Visit Configuration > System > Mailer > Transport, select SMTP, and click Add transport.
  2. Specify your SMTP configuration (username, password, host name, port).

I use docker and mailhog/mailhog for testing emails in a local development environment, so my configuration looks like:

Mailer policy
  • Host name: mail (specified in docker-compose.yml)
  • Port: 1025 (default mailhog SMTP port)

Sending the Email

Use email_factory service for injecting email factory and creating a new email using newTypedEmail method:

/** @var \Drupal\symfony_mailer\EmailFactoryInterface $emailFactory */
$emailFactory = \Drupal::service('email_factory');

$email = $emailFactory->newTypedEmail('mailer_example_builder', '');
$email->setTo('test@example.com');
$email->send();

The method takes an EmailBuilder plugin id (specified in the annotation) and sub-type as arguments.

Mailhog

Conclusion

The Drupal Symfony Mailer module empowers you to send programmatic emails with flexibility and control. By leveraging EmailBuilder and Mailer Policies, you can create dynamic and customizable email templates with attachments. This approach enhances your email communication's efficiency, maintainability, and content tailoring capabilities.
 

Tags

Comments

Restricted HTML

  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
  • Web page addresses and email addresses turn into links automatically.