Email
Send email
It is relatively easy to send email from Drupal once you see the relationship between the calling function and hook_mail(), but it is a little counter-intuitive that you have to implement hook_mail() in the first place considering the call to $mail_manager->mail().
So first create a mail function in your module, then add the hook_mail() function. Here is an example of how to do this.
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
/**
* Function to send an email in your module.
*
* @param string $to
* The recipient of the email.
* @param array $params
* An array of parameters to use in the email.
* @param string $subject
* The subject of the email.
* @param string $body
* The body of the email.
*
* @return bool
* TRUE if the email was sent successfully, FALSE otherwise.
*/
function sendMyEmail($to, $params, $subject, $body) {
// Get the language manager service.
$language_manager = \Drupal::service('language_manager');
// Get the default language.
$default_language = $language_manager->getDefaultLanguage()->getId();
// Get the mail manager service.
$mail_manager = \Drupal::service('plugin.manager.mail');
// Set up the email parameters.
$module = 'my_module';
$key = 'my_email_key';
// Get the email headers.
$headers = [
'Content-Type' => 'text/html; charset=UTF-8; format=flowed; delsp=yes',
'From' => 'sender@example.com',
'Reply-To' => 'reply-to@example.com',
];
// Set up the email body.
$body_params = [
'message' => $params['message'],
'name' => $params['name'],
];
// Send the email.
$result = $mail_manager->mail($module, $key, $to, $default_language, $body_params, NULL, TRUE, $headers);
// Return the result.
return $result['result'];
}Note
Don`t forget to add the hook_email() to your .module file or no email will ever be sent!
/**
* Implements hook_mail().
*/
function my_module_mail($key, &$message, $params) {
switch ($key) {
case 'my_email_key':
$message['subject'] = t('My Email Subject');
$message['body'][] = $params['message'];
$message['body'][] = t('From: @name', ['@name' => $params['name']]);
break;
}
}General send email function with logging
Here is an example of a general send email function with logging. It is used to send an email and log the result. It also checks to see if the email is valid before sending it.
/**
* Send email notification.
*
* @param array $to_email_addresses
* Array of email addresses.
* @param string|null $from
* From email address.
* @param string $subject
* Message subject.
* @param string $message_body
* Message in plain text.
*
* @return array
* The render array for screen display.
*/
public function sendEmail(array $to_email_addresses, string|null $from, string $subject, string $message_body): array {
/*
* Notifications will only be sent from prod environment.
* Configured in settings.php (or settings.local.php)
* For development, use this in settings.local.php:
* $config['tabcblah.status_change_notifications']
* ['send_notifications'] = TRUE;
*/
$send_notifications = \Drupal::config('tea_teks.status_change_notifications')->get('send_notifications');
if (is_null($from)) {
$from = \Drupal::config('system.site')->get('mail');
}
$results = [];
$email_validator = \Drupal::getContainer()->get('email.validator');
if (!$send_notifications) {
$results[0]['status'] = 'success';
return $results;
}
foreach ($to_email_addresses as $to_email_address) {
// Check if it's a valid email address.
if (!$email_validator->isValid($to_email_address)) {
continue;
}
$language_code = \Drupal::config('system.site')->get('langcode');
// Get the mail manager service.
$mail_manager = \Drupal::service('plugin.manager.mail');
$headers = ['Content-Type' => 'text/html; charset=UTF-8'];
$params = [
'headers' => $headers,
'from' => $from,
'subject' => $subject,
'body' => $message_body,
];
// Send the email.
$response = $mail_manager->mail('tea_teks', 'tea_teks_public_comment_error_form_notification', $to_email_address, $language_code, $params, NULL, TRUE);
$response_message = t('Email to %to, from: %from, subject: %subject: Send result: %result.', [
'%result' => $response['result'] ? t('success') : t('fail'),
'%subject' => $subject,
'%from' => $from,
'%to' => implode('; ', $to_email_addresses),
]);
$results[] = [
'status' => $response['result'] ? t('success') : t('fail'),
'message' => $response_message,
];
if (!$response['result']) {
\Drupal::logger('tea_teks_srp')->error($response_message);
}
}
return $results;
}Note
Don't forget to add the hook_mail in your .module file. Here it is in the tea_teks module file.
/**
* Implements hook_mail().
*/
function tea_teks_mail($key, &$message, $params) {
$site_name = \Drupal::config('system.site')->get('name');
$site_mail = \Drupal::config('system.site')->get('mail');
switch ($key) {
case 'tea_teks_public_comment_error_form_notification':
$message['headers']['Reply-To'] = $site_mail;
// $message['headers']['Content-Type'] = 'text/html';
// $message['headers']['Content-Type']= 'text/html; charset=utf-8; format=flowed; delsp=yes';
$message['headers']['Content-Type'] = 'text/plain; charset=utf-8';
$message['headers']['From'] = $site_name .'<' . $site_mail . '>';
$message['subject'] = t('@subject', array('@subject' => $params['subject']));
$message['body'][] = Xss::filter($params['body'], ['a', 'b', 'br', 'em', 'i', 'strong', 'u', 'p']);
break;
case 'tea_teks_publisher_status_notification':
default:
$message['headers']['Reply-To'] = $site_mail;
$message['headers']['Content-Type'] = 'text/html';
$message['headers']['From'] = $site_name .'<' . $site_mail . '>';
$message['subject'] = t('@subject', array('@subject' => $params['subject']));
$message['body'][] = Xss::filter($params['body']);
break;
}
}Using tokens in hook_mail
Here is an example in a hook_mail call where tokens are used:
/**
* Implements hook_mail().
*/
function hello_world_mail($key, &$message, $params) {
switch ($key) {
case 'hello_world_log':
$message['from'] = \Drupal::config('system.site')->get('mail');
$message['subject'] = t('There is an error on your website');
$message['body'][] = $params['message'];
if (isset($params['user'])) {
$user_message = 'The user that was logged in: [current-user:name]';
$message['body'][] = \Drupal::token()->replace($user_message, ['current-user' => $params['user']]);
}
break;
}
}Useful helper functions
See docroot/core/lib/Drupal/Core/Mail/MailFormatHelper.php for more.
// Break a body field into paragraphs.
$message['body'] = implode("\n\n", $message['body']);
// Transforms an HTML string into plain text, preserving its structure.
$message['body'] = MailFormatHelper::htmlToText($message['plaintext']);
// Performs format=flowed soft wrapping for mail (RFC 3676).
$message['body'] = MailFormatHelper::wrapMail($message['plaintext']);and here are a few more.
MailFormatHelper::htmlToText()- Transforms an HTML string into plain text, preserving its structure.MailFormatHelper::wrapMailLine()- Wraps words on a single line.MailFormatHelper::htmlToMailUrls()- Keeps track of URLs and replaces them with placeholder tokens.MailFormatHelper::htmlToTextClean()- Replaces non-quotation markers from a piece of indentation with spaces.MailFormatHelper::htmlToTextPad()- Pads the last line with the given character.
Send quick test email with drush
You can use drush to quickly test sending an email. Here is an example of how to do this.
ddev drush eval "
\$params = ['subject' => 'Test email', 'body' => 'Test body', 'context' => []];
\Drupal::service('plugin.manager.mail')->mail('system', 'mail_test', 'test@example.com', 'en', \$params);
"Note
If you run DDEV's mailpit first using ddev mailpit you will see the email that you just sent out.
Troubleshooting
Mail sends but no subject or body
This means you either forgot to create the hook_mail module or that your key doesn't match. Try changing the key in your hook mail to be a default and see if that works
function tea_teks_mail($key, &$message, $params) {
$site_name = \Drupal::config('system.site')->get('name');
$site_mail = \Drupal::config('system.site')->get('mail');
switch ($key) {
case 'tea_teks_public_comment_error_form_notification':
$message['headers']['Reply-To'] = $site_mail;
$message['headers']['Content-Type'] = 'text/html';
$message['headers']['From'] = $site_name .'<' . $site_mail . '>';
$message['subject'] = t('@subject', array('@subject' => $params['subject']));
break;
default:
$message['headers']['Reply-To'] = $site_mail;
$message['headers']['Content-Type'] = 'text/html';
$message['headers']['From'] = $site_name .'<' . $site_mail . '>';
$message['subject'] = t('@subject', array('@subject' => $params['subject']));
$message['body'][] = Xss::filter($params['body']);
break;
}
}Reference
- Sending html mails in Drupal 8/9 programmatically An example Drupal module including Twig template by Joris Snoek - August 2020
- Sending Emails Using OOP and Dependency Injection in Drupal 8, 9 By Alex Novak - November 2020.
- How email works in Drupal - updated July 2021
- Sendgrid Integration Drupal module
- How to send a mail programmatically in Drupal 8 by Jimmy Sebastian - Mar 2022
- Sending Emails with Drupal Symfony Mailer - Oct 2023
- How to Configure the SMTP Module in Drupal 10 with Gmail, Since Google Removed Less Secure Apps - Apr 2024