Статьи

Использование и расширение Drupal 8 Mail API: Часть 2

В предыдущей статье мы рассмотрели, как мы можем программно отправлять электронные письма в Drupal 8. Мы также видели, как другие модули могут изменять эту исходящую почту. Сегодня мы рассмотрим, как мы можем использовать Mail API для расширения этого поведения по умолчанию. Целью является использование внешнего сервиса в качестве средства доставки электронной почты.

Для этого мы будем использовать Mandrill , хотя в центре внимания статьи будет не его API или как с ним работать, а скорее Drupal. И помните, рабочий модуль можно найти в этом репозитории Git .

Как мы видели в предыдущей статье, отправка электронного письма в Drupal 8 происходит путем запроса почтового менеджера, передачи некоторых параметров его методу mail() и настройки шаблона внутри реализации hook_mail() . Внутренний почтовый менеджер загружает соответствующий почтовый плагин, создает электронную почту и затем делегирует методу mail() любого загружаемого плагина.

Но кому это делегируется?

Важная вещь, которую нужно понять перед написанием нашего собственного плагина, — это процесс выбора почтового менеджера для загрузки плагинов. Другими словами, как мы узнаем, какой плагин он будет загружать, и как мы можем заставить его загружать свой собственный?

Конфигурационный массив system.mail.interface содержит все ответы. Он содержит идентификаторы доступных плагинов, которые определяются контекстом, в котором они используются. По умолчанию все, что у нас есть в этой конфигурации, это default => phpmail . Это означает, что плагин с идентификатором phpmail (класс PHPMail ) используется как запасной вариант для всех контекстов, которые не указаны иначе, т.е. по умолчанию.

Если мы хотим написать наш собственный плагин, нам нужно добавить еще один элемент в этот массив с идентификатором плагина в качестве его значения. Ключ для этого значения может быть одним из двух: имя компьютера нашего модуля (для загрузки плагина всякий раз, когда наш модуль отправляет электронные письма) или сочетание имени модуля и ключа шаблона электронной почты (для загрузки плагина всякий раз, когда наш модуль отправляет электронное письмо). используя этот конкретный ключ).

Примером последней конструкции является d8mail_node_insert , где d8mail — это имя нашего модуля, которое мы начали node_insert в предыдущей статье, а node_insert — это ключ шаблона электронной почты, который мы определили.

Итак, теперь, когда мы знаем, как происходит выбор почтового плагина, нам нужно убедиться, что этот конфигурационный массив содержит необходимую информацию, чтобы письма, отправленные с нашим модулем d8mail использовали новый плагин, который мы d8mail . Мы можем сделать это внутри реализации hook_install (), которая срабатывает только один раз при установке модуля:

d8mail.install :

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
 * Implements hook_install().
 */
function d8mail_install() {
  $config = \Drupal::configFactory()->getEditable(‘system.mail’);
  $mail_plugins = $config->get(‘interface’);
  if (in_array(‘d8mail’, array_keys($mail_plugins))) {
    return;
  }
 
  $mail_plugins[‘d8mail’] = ‘mandrill_mail’;
  $config->set(‘interface’, $mail_plugins)->save();
}

Не супер сложно, что происходит выше. Мы загружаем редактируемый объект конфигурации, представляющий конфигурацию system.mail , и добавляем новый элемент в массив interface : d8mail => mandrill_mail . Вскоре мы создадим почтовый плагин с идентификатором mandrill_mail который будет использоваться для всех писем, отправляемых модулем d8mail . Вот и все.

Но прежде чем двигаться дальше, мы должны убедиться, что это изменение отменено при удалении модуля. Для этого мы можем использовать функцию hook_uninstall (), которая вызывается при удалении модуля (в Drupal 8 больше нет отключений модуля).

Внутри того же файла:

01
02
03
04
05
06
07
08
09
10
11
12
13
/**
 * Implements hook_uninstall().
 */
function d8mail_uninstall() {
  $config = \Drupal::configFactory()->getEditable(‘system.mail’);
  $mail_plugins = $config->get(‘interface’);
  if ( ! in_array(‘d8mail’, array_keys($mail_plugins))) {
    return;
  }
 
  unset($mail_plugins[‘d8mail’]);
  $config->set(‘interface’, $mail_plugins)->save();
}

В реализации hook_uninstall() мы делаем противоположное ранее: мы удаляем наш идентификатор плагина, если он установлен.

Сценарий установки / удаления — это только один путь. Вы также можете создать форму администрирования, которая позволяет пользователям выбирать нужный плагин и в каком контексте. Но вам все равно нужно убедиться, что при отключении модуля, определяющего конкретный плагин, конфигурация больше не будет сохранять ссылку на этот плагин. В противном случае почтовый менеджер может попытаться использовать несуществующий класс и выдать всевозможные ошибки.

Как я уже упоминал ранее, мы будем работать с Mandrill API , чтобы проиллюстрировать нашу задачу. Итак, давайте загрузим библиотеку PHP Mandrill и сделаем ее доступной в нашей среде. Для этого нужно сделать три шага.

Во-первых, нам нужно получить библиотеку внутри Drupal. На момент написания это в основном означает добавление зависимости "mandrill/mandrill": "1.0.*" В корневой файл composer.json и запуск composer install . Имейте в виду, однако, что это также очистит установку Drupal из core/ папки и загрузит последнюю стабильную версию. Затем вам нужно отредактировать корневой файл index.php и изменить путь к автозагрузчику в соответствии с этими инструкциями . Надеюсь, это последнее действие не понадобится в ближайшее время, и я призываю вас следить за обсуждением будущего Composer в Drupal 8 для управления внешними библиотеками.

Во-вторых, нам нужно получить ключ API от Mandrill. К счастью, это мы можем легко сгенерировать на их страницах администрирования. Получив это, мы можем сохранить его в новом файле, созданном на нашем сервере, в любом месте:

1
2
~/.mandrill.key
/etc/mandrill.key

Мы также можем передать ключ в качестве параметра конструктора в основной класс Mandrill , но таким образом нам не нужно будет жестко кодировать его в нашем коде.

В-третьих, нам нужно создать сервис, чтобы мы могли использовать внедрение зависимостей для передачи класса Mandrill в наш плагин:

d8mail.services.yml :

1
2
3
services:
 d8mail.mandrill:
   class: Mandrill

В зависимости от того, как вы загрузили класс Mandrill в свое приложение, вам нужно будет изменить значение после class . При использовании подхода composer.json этого будет достаточно.

Наконец-то пришло время создать наш плагин. В Drupal 8 классы src/Plugin папку src/Plugin нашего модуля. Однако, в зависимости от их типа, они размещаются дальше в других каталогах (в нашем случае Mail ). Давайте напишем наш класс, который будет зависеть от библиотеки Mandrill API для отправки электронных писем:

src / Plugin / Mail / MandrillMail.php :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<?php
 
namespace Drupal\d8mail\Plugin\Mail;
 
use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Mail\MailInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Mandrill;
use Mandrill_Error;
 
/**
 * Defines the Mandrill mail backend.
 *
 * @Mail(
 * id = «mandrill_mail»,
 * label = @Translation(«Mandrill mailer»),
 * description = @Translation(«Sends an email using Mandrill.»)
 * )
 */
class MandrillMail implements MailInterface, ContainerFactoryPluginInterface {
 
  /**
   * @var Mandrill
   */
  private $mandrill;
 
  /**
   * @param Mandrill $mandrill
   */
  public function __construct(Mandrill $mandrill) {
    $this->mandrill = $mandrill;
  }
 
  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $container->get(‘d8mail.mandrill’)
    );
  }
 
  /**
   * {@inheritdoc}
   */
  public function format(array $message) {
    // Join the body array into one string.
    $message[‘body’] = implode(«\n\n», $message[‘body’]);
    // Convert any HTML to plain-text.
    $message[‘body’] = MailFormatHelper::htmlToText($message[‘body’]);
    // Wrap the mail body for sending.
    $message[‘body’] = MailFormatHelper::wrapMail($message[‘body’]);
 
    return $message;
  }
 
  /**
   * {@inheritdoc}
   */
  public function mail(array $message) {
 
    try {
      $vars = [
        ‘html’ => $message[‘body’],
        ‘subject’ => $message[‘subject’],
        ‘from_email’ => $message[‘from’],
        ‘to’ => array(
          array(’email’ => $message[‘to’])
        ),
      ];
 
      $result = $this->mandrill->messages->send($vars);
      if ($result[0][‘status’] !== ‘sent’) {
        return false;
      }
 
      return $result;
    }
    catch (Mandrill_Error $e) {
      return false;
    }
  }
}

Есть несколько вещей, на которые следует обратить внимание, прежде чем приступить к занятиям.

Во-первых, аннотации над классом. Это просто самый распространенный механизм обнаружения плагинов для Drupal 8. Ключ id соответствует значению, которое мы добавили в system.mail.interface конфигурации system.mail.interface ранее, в то время как остальные являются базовыми элементами определения плагинов.

Во-вторых, реализация интерфейса ContainerFactoryPluginInterface помощью которого мы определяем метод create() . Последний является частью процесса внедрения зависимостей, с помощью которого мы можем загрузить сервис Mandrill, который мы определили в файле services.yml ранее. Это значительно облегчает тестирование и считается наилучшей практикой.

Как я уже упоминал, почтовые плагины должны реализовывать интерфейс MailInterface который обеспечивает существование методов format() и mail() . В нашем случае первый делает то же самое, что и плагин PHPMail : небольшая обработка тела сообщения. Так что вы можете добавить свою собственную логику здесь, если хотите. Последний метод, с другой стороны, отвечает за отправку почты, в нашем случае, используя сам Mandrill API .

Как указано в документации Mandrill, мы создаем сообщение электронной почты внутри массива $vars используя значения, передаваемые почтовым менеджером через параметр $message . Они будут уже отфильтрованы через hook_mail() , hook_mail_alter() и собственный метод format() плагина. Осталось только отправить электронное письмо. Я не буду вдаваться в подробности использования Mandrill API, поскольку вы можете ознакомиться с документацией по всем параметрам, которые вы можете использовать.

После отправки электронного письма и получения от Mandrill статуса « sent мы возвращаем весь массив ответов, который содержит дополнительную информацию. Этот массив затем добавляется почтовым менеджером в его собственный массив возвращаемых данных с ключом в result . Если Mandrill имеет проблему, отклоняет электронное письмо или выдает исключение, мы возвращаем false . Это заставит почтовый менеджер справиться с этой ситуацией, зарегистрировав инцидент и напечатав сообщение о состоянии.

И это в значительной степени это. Мы можем очистить кеш и попытаться создать еще один узел статьи. На этот раз, уведомление по электронной почте должно быть отправлено Mandrill вместо PHP mail() . Однако hook_mail_alter() , когда это hook_mail_alter() реализация hook_mail_alter() стала излишней, поскольку нет никаких заголовков, которые мы на самом деле отправляем в Mandrill (а текст уже HTML). И в этом отношении довольно большая часть работы почтового менеджера не используется, поскольку мы не передаем это на Mandrill. Но это только для того, чтобы проиллюстрировать процесс того, как вы можете настроить это. Детали реализации остаются на ваше усмотрение.

И там у нас это есть. Мы реализовали наш собственный почтовый плагин, который будет использоваться d8module мы начали в предыдущей статье. И благодаря расширяемой природе Drupal 8, это даже не потребовало слишком много усилий.

Вам остается только усовершенствовать логику отправки почты и адаптировать ее к вашим обстоятельствам. Это может означать дальнейшую интеграцию между Mandrill и вашим экземпляром Drupal, создание хороших шаблонов или что у вас есть. Кроме того, важной оставшейся задачей является написание автоматических тестов для этой функции. И Drupal 8 также предлагает достаточно инструментов для этого.