- Drupal 8 Module Development
- Daniel Sipos
- 782字
- 2021-07-02 15:45:21
Sending emails
We wanted to use our MailLogger to send out an email whenever we are logging an error. So let's go back to our class and add this logic.
This is what our log() method can look like:
/**
* {@inheritdoc}
*/
public function log($level, $message, array $context = array()) {
if ($level !== RfcLogLevel::ERROR) {
return;
}
$to = $this->configFactory->get('system.site')->get('mail');
$langode = $this->configFactory->get('system.site')->get('langcode');
$variables = $this->parser->parseMessagePlaceholders($message, $context);
$markup = new FormattableMarkup($message, $variables);
\Drupal::service('plugin.manager.mail')->mail('hello_world', 'hello_world_log', $to, $langode, ['message' => $markup]);
}
First of all, we stated that we only want to send mails for errors, so in the first lines, we check whether the attempted log is of that level and return otherwise. In other words, we don't do anything if we're not dealing with an error and rely on other registered loggers for those.
Next, we determine who we want the email to be sent to and the langcode to send it in (both are mandatory arguments to the mail manager's mail() method). We opt to use the site-wide email address (just as we did for the From value). We also use the same configuration object as we used earlier in the hook_mail() implementation. In the next code snippet, we will take care of injecting the config factory into the class.
Then, we prepare the message that is being sent out. For this, we use the FormattableMarkup class to which we pass the message string and an array of variable values that can be used to replace the placeholders in our message. We can retrieve these values using the LogMessageParser service the same way as the DbLog logger does. With this, we are basically extracting the placeholder variables from the entire context array of the logged message.
Lastly, we will use the mail manager plugin to send the email. The first parameter to its mail() method is the module we want to use for the mailing. The second is the key (or template) we want to use for it (which we defined in hook_mail()). The third and fourth are self-explanatory, whereas the fifth is the $params array we encountered in hook_mail(). If you look back on that, you'll note that we used the message key as the body. Here, we populate that key with our markup object, which has a _toString() method that renders it with all the placeholders replaced.
You may wonder why I did not inject the Drupal mail manager as I did in the rest of the dependencies. Unfortunately, the core mail manager uses the logger channel factory itself, which in turn depends on our MailLogger service. So if we make the mail manager a dependency of the latter, we find ourselves in a circular loop. So when the container gets rebuilt, a big fat error is thrown. It might still work, but it's not alright. So, I opted to use it statically, because, in any case, this method is very small and would be difficult to test due to its expected result being difficult to assert (it sends an email). Sometimes, you have to make these choices, as the alternative would have been to inject the entire service container just to trick it. However, that is a code smell and would not have helped anyway had I wanted to write a test for this class.
Even if I did not inject the mail manager, I did inject the rest. So, let's take a look at what we have now at the top:
/**
* @var \Drupal\Core\Logger\LogMessageParserInterface
*/
protected $parser;
/**
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* MailLogger constructor.
*
* @param \Drupal\Core\Logger\LogMessageParserInterface $parser
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
*/
public function __construct(LogMessageParserInterface $parser, ConfigFactoryInterface $config_factory) {
$this->parser = $parser;
$this->configFactory = $config_factory;
}
Let's check out the relevant use statements:
use Drupal\Core\Logger\LogMessageParserInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
There seems nothing out of the ordinary. We just take in the services I mentioned above. Also, let's quickly see the service definition of our mail logger as it looks now:
hello_world.logger.hello_world:
class: Drupal\hello_world\Logger\MailLogger
arguments: ['@logger.log_message_parser', '@config.factory']
tags:
- { name: logger }
We simply have two new arguments--nothing new to you by now.
Clearing the caches and logging an error should send the logged message (with the placeholders replaced) to the site email address (and from the same address) using the PHP native mail() function. Congratulations! You just sent out your first email programmatically in Drupal 8.
- Hands-On Machine Learning with scikit:learn and Scientific Python Toolkits
- Angular UI Development with PrimeNG
- PyTorch自動駕駛視覺感知算法實戰
- Wireshark Network Security
- Instant Typeahead.js
- Functional Programming in JavaScript
- CouchDB and PHP Web Development Beginner’s Guide
- 算法訓練營:提高篇(全彩版)
- 從0到1:Python數據分析
- Jupyter數據科學實戰
- Hands-On Nuxt.js Web Development
- MySQL程序員面試筆試寶典
- 透視C#核心技術:系統架構及移動端開發
- JavaScript程序設計實例教程(第2版)
- Flask Web開發實戰:入門、進階與原理解析