Programmatically sending emails with attachments in Drupal 10 using Symfony Mailer
Dmitry
Posted on April 8, 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.
Visit Configuration > System > Mailer and click Add policy.
Choose Mailer example builder (the label of your created EmailBuilder) as the type, then click Add and configure.
Add the desired content for the Subject and Body elements.
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.
Visit Configuration > System > Mailer > Transport, select SMTP, and click Add transport.
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:
- Host name:
mail
(specified indocker-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.
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.
Posted on April 8, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.