Перейти к основному содержанию
menu

В этой главе мы создадим модуль с административным интерфейсом. Этот модуль будет основан на многих концепциях создания модулей, которые были представлены ранее. Некоторые из понятий, которые мы рассмотрим в этой главе:

Создание маршрутов в routing.yml
Создание ссылок меню с использованием links.menu.yml
Создание базовых форм с помощью API форм
Управление настройками Drupal с использованием системы управления конфигурацией
Отправка почты с помощью сервиса MailManager
Использование нового сервиса токенов Drupal 8
После того, как эта глава будет закончена, вы должны хорошо разобраться со многими концепциями, которые лежат в основе почти каждого модуля, который вы напишете в будущем.

Модуль User Warning


В этой главе мы будем создавать модуль User Warning. Этот модуль позволяет администраторам отправлять пользователям предупреждение по электронной почте, когда этот пользователь нарушает условия обслуживания сайта или ведет себя неуместным образом. Модуль User Warning реализует следующие функции:

Модуль предоставит администраторам настройки конфигурации, включая текст письма по умолчанию
Это электронное письмо будет содержать токены Drupal, которые позволяют администратору заменять и / или добавлять переменные, специфичные для сайта, в электронное письмо.
Администраторы сайта смогут отправить пользователю электронное письмо через новую вкладку на странице своего профиля.
Предупреждающие письма будут отправляться с использованием почтовой реализации по умолчанию в Drupal


Начнем создавать наш модуль


Мы начнем, как и в третьей части этой серии , с создания новой папки для нашего модуля с именем user_warn в каталоге modules / custom в нашей установке Drupal. Затем мы можем создать файл user_warning.info.yml, как показано ниже:

name: User Warning
type: module
description: Exposes an admin interface to send behavior warning e-mails to users.
core: 8.x
package: Drupal 8 Module Development

Вы должны быть уже довольно хорошо знакомы с созданием этого файла. Мы также создадим наш файл user_warn.module и добавим реализацию hook_help (), чтобы администраторы сайта знали, что делает наш модуль.

<?php

/**
 * @file
 * User Warning module file
 *
 * This module allows site administrators to send a stock warning
 * e-mail to a specified user or users through the admin interface.
 * Administrators can configure the default e-mail including token replacement.
 */

/**
 * Implements hook_help().
 */
function user_warning_help($route_name, \Drupal\Core\Routing\RouteMatchInterface $route_match) {
  if ($route_name == 'help.page.first') {
    return t('User Warning allows site adminitrators to send a standard e-mail to site users to notify them of improper behavior.');
  }
}

В этом тоже нет ничего нового.

Система меню Drupal


По своей сути система меню отвечает за сопоставление маршрутов Drupal с методами, которые генерируют содержимое запрашиваемой страницы. Система меню также отвечает за контроль доступа к страницам Drupal, выступая в качестве одного из главных привратников безопасности Drupal.

Как мы узнали из прошлых статей , система меню Drupal построена поверх компонента Routing в Symfony2. Этот компонент отвечает за обработку входящих HTTP-запросов путем сопоставления внутренних маршрутов с URL-адресами на нашем сайте Drupal. Мы также узнали, что для создания маршрутов в системе нам нужно создать файл {modulename} .routing.yml и определить наши маршруты в формате YAML.

Итак, давайте продолжим и создадим наши маршруты.

Создание новых маршрутов


Для нашего модуля нам потребуется реализовать две новые страницы - страницу конфигурации для модуля User Warning и вкладку в области профиля пользователя, откуда администраторы могут отправлять фактические электронные письма с предупреждениями. Каждому из них потребуются свои собственные маршруты в нашем файле user_warning.routing.yml, который нам нужно поместить в корень папки модуля:

user_warn.configure:  
  path: '/admin/config/people/user_warning'
  defaults:
    _form: '\Drupal\user_warning\Form\UserWarningConfigForm'
    _title: 'User Warning Configuration'
  requirements: 
    _permission: 'administer users'

user_warning.user_tab:
  path: '/user/{user}/user-warning'
  defaults:
    _form: '\Drupal\user_warning\Form\UserWarningEmailForm'
    _title: 'Warning this user'    
  requirements:
    _permission: 'administer users'
  options:
    _admin_route: TRUE

Этот пример лишь поверхностно касается опций, доступных в системе меню. Для более подробной информации вы должны посмотреть документацию API  здесь и здесь .


Давайте рассмотрим содержимое этого файла. Как мы уже выяснили , мы создаем два новых маршрута: user_warn.configure и user_warning.user_tab . Мы следуем рекомендациям Drupal, объявляя наши новые маршруты в формате modulename.sub-name .

Теперь мы рассмотрим каждый из параметров в нашей декларации маршрута. Параметр path определяет URL на нашем сайте Drupal, с которым связан этот маршрут. 

path: '/admin/config/people/user_warning'

Когда приходит запрос на этот URL, Drupal преобразует его во внутренний маршрут и обрабатывает его соответствующим образом. Этот путь определяет не только расположение страницы, но и ее место в иерархии меню, причем каждая часть URL является дочерней по отношению к последней. В этом примере people - это потомок config , который сам является потомком admin .

Если запрошенный путь не существует, Drupal сгенерирует ответ 404 not found клиенту.

Если вы создаете пункт меню для администрирования сайта, он должен начинаться с admin . Это помещает его в административный интерфейс Drupal и применяет тему оформления администратора, определенную настройками сайта.

Свойство defaults содержит параметры, определяющие некоторые параметры, которые управляют способом представления ответа клиенту. Эти параметры могут включать формат, в котором данные возвращаются клиенту - например, при возврате машиночитаемых данных, таких как XML, мы можем использовать параметр _controller и определить метод, который возвращает данные. В нашем случае выше, мы использовали параметр _form в обоих случаях (потому что мы хотим генерировать формы), который ожидает в качестве значения класс, который реализует Drupal \ Core \ Form \ FormInterface .

_form: '\Drupal\user_warning\Form\UserWarningConfigForming'

Мы также можем определить заголовок сгенерированной страницы, используя параметр _title . Значение этого параметра автоматически становится переводимым и используется в качестве заголовка вашей страницы в теге HTML <title> и теге <h1> страницы .

_title: 'User Warning Configuration'

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

_permission: 'administer users'

Для получения полного списка свойств маршрутизации перейдите по  ссылке https://www.drupal.org/node/2092643 .


Любой пользователь, которому было назначено одно из этих разрешений, сможет получить доступ к этой странице. Любому другому будет предоставлена страница с отказом в доступе . Права доступа определяются модулями с помощью hook_permission () . Вы можете увидеть полный список определенных на данный момент разрешений в admin / people / permissions, как показано:

permission

Вы можете увидеть разрешение «администрировать пользователей» внизу этого списка. В предыдущем примере только роль администратора имеет это разрешение, и в результате только те пользователи, которым назначена эта роль, смогут получить доступ к нашей странице.

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

Использование подстановочных знаков в путях меню


Мы создали простой пункт меню, но иногда этого бывает не достаточно. В модуле User Warn мы хотим иметь пункт меню, который привязан к странице профиля каждого отдельного пользователя. Страницы профиля в Drupal находятся по пути user / <user_id> , так как нам создать отдельный пункт меню для каждого пользователя? К счастью, система меню позволяет нам использовать подстановочные знаки, когда мы определяем наши пути меню.

Если вы посмотрите на второй маршрут, определенный в предыдущем примере, вы увидите, что его определение немного отличается от первой записи.

user_warning.user_tab:
  path: '/user/{user}/user-warning'
  defaults:
    _form: '\Drupal\user_warning\Form\UserWarningEmailForm'
    _title: 'Warning this user'    
  requirements:
    _permission: 'administer users'
  options:
    _admin_route: TRUE

Разница в том, что путь определенный как {user} является одной из записей пути. Это указывает на подстановочный знак; здесь можно ввести что угодно, и иерархия пунктов меню будет сохранена. 

В этом примере мы называем этот подстановочный знак {user} , который указывает Drupal, что мы будем работать с объектом пользователя. Затем встроенная система ParamConverter пытается создать экземпляр реального пользовательского объекта на основе аргумента в URL-адресе при обработке запроса для этого маршрута. Если бы мы запросили URL / user / 1 / warning , ParamConverter по существу загрузил бы полностью построенный пользовательский объект пользователя с идентификатором 1 и пропустил бы его по маршруту. Затем этот объект становится доступным далее по линии нашего маршрута и в связанном классе или методе, который мы используем для генерации ответа.

Локальные задачи


До сих пор мы создали два новых маршрута в нашей системе в user_warn.routing.yml . Эти два новых маршрута заставят Drupal отвечать на входящие запросы с соответствующих URL. Но мы установили, что нашей целью также является создание вкладки на страницах профиля каждого пользователя, чтобы администраторы могли отправлять фактические предупреждения по электронной почте для данного пользователя.

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

Чтобы сообщить Drupal, что мы хотим создать локальную задачу, нам нужно создать файл modulename.links.task.yml в папке нашего модуля. Итак, мы продолжим и создадим user_warning.links.task.yml   в корне папки нашего модуля со следующим содержимым:

user_warning.user_tab:
  title: 'Warning'
  route_name: user_warning.user_tab
  base_route: entity.user.canonical

Подобно user_warning.routing.yml , мы начинаем с создания ключа локальной задачи, а затем присваиваем ему различные свойства.

Свойство title определяет строку, которая отображается как текст ссылки на вкладке. Снова; это значение передается через встроенную систему перевода.

ROUTE_NAME определяет машинное имя маршрута и присоединение нашей локальной задачи. В нашем случае мы определили значение user_warning.user_tab , которое является машинным именем второго элемента маршрута, который мы определили в user_warning.routing.yml . Drupal прочитает свойство path определенного маршрута и автоматически отобразит нашу вкладку на соответствующей странице.

Наконец, base_route - это маршрут, на котором отображается «корневая» вкладка, которая служит для группировки определенного набора локальных задач. Мы установили его на entity.user.canonical , так как он является корнем страницы пользователя в Drupal 8. В этом случае этот маршрут немного особенный, поскольку пользовательский объект является типом Entity в Drupal, и поэтому он соответствует глобальному шаблону маршрута, которому следуют все сущности Drupal. Эти шаблоны маршрутов предоставляются  классом UserRouteProvider , который возвращает объект коллекции маршрутов для стандартных операций с учетными записями пользователей, таких как просмотр (канонический), редактирование и удаление.

До сих пор мы создали два новых маршрута для нашего модуля и локальную вкладку задач на страницах профиля пользователя. Давайте очистим кэши на нашем сайте Drupal в разделе Конфигурация | Разработка | Производительность и посмотрите, появится ли наше локальное задание на странице профиля пользователя. Для этого мы заходим на страницу « Люди» и нажимаем на имя пользователя, что приводит нас к странице профиля этого конкретного пользователя по умолчанию.

form

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

А пока давайте вернемся к нашему первому определению маршрута и посмотрим, что мы там пытаемся сделать.

user_warning.configure:  
  path: '/admin/config/people/user_warning'
  defaults:
    _form: '\Drupal\user_warning\Form\UserWarningConfigForm'
    _title: 'User Warning Configuration'
  requirements: 
    _permission: 'administer users'

По сути, мы пытаемся сделать следующее: создайте новый маршрут в / admin / config / people / user_warning , который сгенерирует форму, созданную классом имен с пространством \ Drupal \ user_warning \ Form \ UserWarningConfigForm . Это объявление маршрута будет прослушивать входящие запросы по указанному URL и загружать класс, который мы определили для генерации ответа. Но было бы неплохо также создать ссылку административного меню на нашем сайте, чтобы мы могли легко получить доступ к этому пути. Как мы это делаем?

Создание ссылок меню


Чтобы создать ссылки меню, которые появляются на нашем сайте, нам нужно создать файл modulename.links.menu.yml в папке нашего модуля. Drupal автоматически читает этот файл и создает видимые ссылки на сайте, связанные с их соответствующими маршрутами. Давайте продолжим и создадим файл с именем user_warning.links.menu.yml в папке нашего модуля:

user_warning.settings:
  title:  Configure User Warning
  route_name:  user_warning.configure
  description: 'Configuration settings for User Warning.'    
  parent: user.admin_index

Давайте рассмотрим содержимое этого файла. Как и другие файлы YAML в Drupal, файл user_warning.links.menu.yml содержит иерархически организованный набор параметров конфигурации, определяемый именами машин в ссылках меню. Этим именам машин должно предшествовать имя модуля, в нашем случае это user_warning .

user_warning.settings:


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

title:  Configure User Warning


Следующий ключ, который мы определяем, это имя_маршрута . Этот ключ связывает нашу ссылку меню с именем маршрута, который мы определили в user_warning.routing.yml :

route_name:  user_warning.configure


Затем мы определяем ключ описания , который используется для предоставления небольшого справочного текста под ссылкой меню в пользовательском интерфейсе. Рекомендуется всегда указывать ссылку на меню, чтобы администраторы сайта понимали, куда эта ссылка приведет их и что они могут сделать на этой конкретной странице.

Description: 'Configuration settings for User Warning.'


Наконец, мы определяем другой ключ, который называется parent . Используя этот ключ, мы определяем родительский маршрут нашего пункта меню. Другими словами, это где мы можем определить, где наш пункт меню занимает свое место в иерархии меню. 

parent: user.admin_index


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

Поскольку наш модуль предоставляет функциональность, связанную с пользователем, имеет смысл сделать его форму конфигурации доступной в Configuration | Люди раздел нашего сайта. Внутренний путь этой страницы - admin / config / people . Теперь, когда мы знаем , что мы можем иметь быстрый поиск в основном модуле пользовательского user.routing.yml файла и найти название маршрута , который определяет этот путь:

user.admin_index:
  path: '/admin/config/people'
  defaults:
    _controller: '\Drupal\system\Controller\SystemController::systemAdminMenuBlockPage'
    _title: 'People'
  requirements:
    _permission: 'access administration pages'

Как вы можете видеть выше, имя маршрута, которое определяет искомый путь , называется user.admin_index , так что именно это мы используем в качестве значения родительского ключа в нашей конфигурации ссылок меню.

После очистки кэшей наш новый пункт меню должен появиться на странице конфигурации в разделе « Люди »:

 

form

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

Схема конфигурации предупреждения пользователя


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

Drupal ожидает еще один файл YAML, который определяет нашу схему конфигурации в формате modulename.schema.yml . Кроме того, он ожидает, что он будет помещен во вложенную подпапку папки нашего модуля в modulename / config / schema . Давайте создадим файл user_warning / config / schema / user_warning.schema.yml со следующим содержимым:

user_warning.settings:
  type: mapping
  label: 'User Warning settings'
  mapping:
    email:
      type: mail
      label: 'Email settings'      
    bcc:
      type: boolean
      label: 'BCC admin on all e-mails'

Как обычно, мы начинаем с указания имени схемы на первом шаге.

Затем мы переходим к определению фактической структуры нашей схемы конфигурации. Мы используем отображение как  тип , который действует как простое определение значения ключа различных свойств. В свойстве mapping мы определяем два новых сопоставления конфигурации: email и bcc . В обоих случаях мы определяем тип настройки, а затем ее метку для удобства чтения.

Свойство электронной почты хранит тему по умолчанию и основной текст сообщений с предупреждениями.

Тип почты это расширенный тип данных , который был создан исключительно в качестве ярлыка для определения параметров конфигурации , связанные с функциями электронной почты. Он предоставляет повторно используемый тип почты для текстов электронной почты, которые содержат тему и поле тела сообщения. Подробное описание параметров схемы конфигурации и дополнительную информацию можно найти на странице руководства по адресу https://drupal.org/node/1905070 .


Свойство bcc хранит простое логическое значение, используемое для определения того, хотим ли мы, чтобы администратор сайта получал копии электронных писем с предупреждениями по мере их отправки данному пользователю.

Теперь, когда мы определили схему параметров конфигурации нашего модуля, давайте перейдем к созданию реальных страниц, на которые указывают наши ранее созданные маршруты.

API формы


В стандартной веб-разработке одной из самых утомительных и невыгодных задач является определение HTML-форм и обработка их представлений. Разложите форму, создайте метки, напишите функцию отправки, разберитесь с обработкой ошибок, и хуже всего то, что от сайта к сайту большая часть этого кода является шаблонной - она ​​принципиально одинакова, отличается только представлением. API форм Drupal - это мощный инструмент, позволяющий разработчикам быстро и легко создавать формы и обрабатывать их. Это делается путем определения массивов элементов формы и создания проверки и отправки обратных вызовов для формы.

API форм также является важным элементом безопасности Drupal. Он предоставляет уникальные маркеры формы, которые проверяются при отправке формы, предотвращая атаки подделки межсайтовых запросов, а также автоматически проверяет обязательные поля, длины полей и множество других свойств элементов формы.

Хотя Form API является одним из самых полезных и мощных инструментов в наборе инструментов разработчика модуля, он также может быть одним из самых сложных. Более подробную информацию, помимо этого простого примера, можно найти по следующим адресам:

Краткое руководство по API формы: 
http://drupal.org/node/2117411 
 
Полный справочник по API формы: 
http://api.drupal.org/api/drupal/developer!topics!forms_api_reference.html/8


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

Создаем нашу первую форму


Для модуля User Warn нам нужна форма, которая позволяет администратору сайта вводить следующие элементы:

  • Строка темы для исходящей электронной почты
  • Основная копия исходящей электронной почты
  • Флажок, указывающий, должен ли администратор отправлять копию на исходящие электронные письма
  • Кнопка отправки


Мы продолжим и создадим эту форму. Давайте еще раз посмотрим на определение маршрута, которое мы создали ранее в user_warn.routing.yml :

user_warning.configure:  
  path: '/admin/config/people/user_warning'
  defaults:
    _form: '\Drupal\user_warning\Form\UserWarningConfigForm'
    _title: 'User Warning Configuration'
  requirements: 
    _permission: ‘administer users'

В свойстве defaults мы определяем ключ _form и устанавливаем его значение для пространства имен класса, расположенного  \ Drupal \ user_warning \ Form \ UserWarningConfigForm, чтобы Drupal знал, что результатом этого маршрута должна быть страница, содержащая форму. Следуя стандартам PSR-4, мы создадим указанный класс в папке user_warning / src / Form в файле с именем UserWarningConfigForm.php :

<?php

/*
 * @file  
 * Contains \Drupal\user_warning\Form\UserWarningConfigForm
 * 
 * Provides a configuration form for our User Warning module. By
 * extending the ConfigFormBase class we automatically have access to
 * Drupal's Configuration Management System.
 */

namespace Drupal\user_warning\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Configure the settings for User Warning
 */
class UserWarningConfigForm extends ConfigFormBase {
  
  /**
   * Specify the form ID.
   */
  public function getFormId() {
    return 'user_warning_config_form';
  }

  /**
   * Build the actual form.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    // Retrieve the stored configuration settings
    $config = $this->config('user_warning.settings');

    // Text field for the e-mail subject.
    $form['user_warning_e-mail_subject'] = array(
      '#type' => 'textfield',
      '#title' => t('Warning e-mail subject'),
      '#description' => t('The subject of the e-mail which will be sent to users.'),
      '#size' => 40,
      '#maxlength' => 120,
      '#required' => TRUE,
      '#default_value'  =>  $config->get('email.subject')
    );

    // Textarea for the body of the e-mail.
    $form['user_warning_e-mail_text'] = array(
      '#type' => 'textarea',
      '#rows' => 10,
      '#columns' => 40,
      '#title' => t('Warning e-mail text'),
      '#required' => TRUE,
      '#description' => t('The text of the e-mail which will be sent to users.'),
      '#default_value'  =>  $config->get('email.body')
    );
    // Checkbox to indicate if admin should be sent a Bcc on e-mails.
    $form['user_warning_bcc'] = array(
      '#type' => 'checkbox',
      '#title' => t('BCC admin on all e-mails'),
      '#default_value'  =>  $config->get('bcc'),
      '#description' => t("Indicates whether the admin user (as set in site configuration) should be sent on all warning e-mails."),
    );
    
    return parent::buildForm($form, $form_state);
  }
  
  /**
   * Validates the form - here you can specify your custom validation
   * rules.
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    parent::validateForm($form, $form_state);    
  }
  
  /**
   * Submits the form and saves the configuration settings.
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    
    
    
    // Save the configuration settings
    $this->configFactory->getEditable('user_warning.settings')
      ->set('email.subject', $form_state->getValue('user_warning_e-mail_subject'))
      ->set('email.body', $form_state->getValue('user_warning_e-mail_text'))
      ->set('bcc', $form_state->getValue('user_warning_bcc'))
      ->save();  
      
      parent::submitForm($form, $form_state);
  }

  protected function getEditableConfigNames() {
	return array('user_warning.settings');;
  }
}

Давайте посмотрим, что мы здесь делаем.


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

Затем мы определяем наше пространство имен и импортируем Drupal \ Core \ Form \ ConfigFormBase , который мы будем расширять, чтобы получить доступ к Системе управления конфигурациями Drupal и Drupal \ Core \ Form \ FormStateInterface, которые позволяют нам работать с данными формы:

namespace Drupal\user_warning\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Configure the settings for User Warning
 */
class UserWarningConfigForm extends ConfigFormBase {

Переходя к фактическому содержанию нашего класса, мы рассмотрим каждый шаг по мере продвижения. 

Метод getFormId ()


Первый метод, который мы реализуем в нашем классе, это метод getFormId () , который используется для указания уникального идентификатора нашей формы. Этот идентификатор используется Drupal для извлечения и построения нашей формы, поэтому мы должны реализовать этот метод, иначе мы получим ошибку. Рекомендуется использовать пространство имен для этого идентификатора на основе имени нашего модуля, обычно это будет что-то вроде <modulename> _ <description> _form .

/**
   * Specify the form ID.
   */
  public function getFormId() {
    return 'user_warning_config_form';
  }

Далее мы определим структуру формы, указав каждое поле ввода и связанные с ним функции.

Метод buildForm ()


После указания идентификатора нашей формы мы реализуем метод buildForm () . Это метод, который нам нужно использовать для определения фактической структуры нашей формы.

 /**
   * Build the actual form.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    // Retrieve the stored configuration settings
    $config = $this->config('user_warning.settings');

    // Text field for the e-mail subject.
    $form['user_warning_e-mail_subject'] = array(
      '#type' => 'textfield',
      '#title' => t('Warning e-mail subject'),
      '#description' => t('The subject of the e-mail which will be sent to users.'),
      '#size' => 40,
      '#maxlength' => 120,
      '#required' => TRUE,
      '#default_value'  =>  $config->get('email.subject')
    );

    // Textarea for the body of the e-mail.
    $form['user_warning_e-mail_text'] = array(
      '#type' => 'textarea',
      '#rows' => 10,
      '#columns' => 40,
      '#title' => t('Warning e-mail text'),
      '#required' => TRUE,
      '#description' => t('The text of the e-mail which will be sent to users.'),
      '#default_value'  =>  $config->get('email.body')
    );
    // Checkbox to indicate if admin should be sent a Bcc on e-mails.
    $form['user_warning_bcc'] = array(
      '#type' => 'checkbox',
      '#title' => t('BCC admin on all e-mails'),
      '#default_value'  =>  $config->get('bcc'),
      '#description' => t("Indicates whether the admin user (as set in site configuration) should be sent on all warning e-mails."),
    );
    
    return parent::buildForm($form, $form_state);
  }

Первое, что мы делаем в этом методе, это просим Drupal загрузить настройки конфигурации нашего модуля. Мы делаем это, вызывая $ this-> config ('user_warning.settings') , который возвращает сохраненные настройки конфигурации нашего модуля.

Метод config () , предоставляемый классом ConfigFormBase (который мы расширяем), запрашивает встроенную систему управления конфигурациями Drupal. Требуется единственный аргумент: машинное имя нашей записи конфигурации.

Как мы уже узнали в главе о Системе управления конфигурацией, параметры конфигурации можно получить с помощью метода get (), предоставленного ConfigFactoryInterface, и это именно то, что делает config () . 
Мы извлекаем настройки нашего модуля и сохраняем их в объекте $config , который мы будем использовать для заполнения полей формы сохраненными настройками.

Давайте перейдем к фактическим полям формы. Свойства элемента формы всегда начинаются со знака # , чтобы отличать их от вложенных полей формы. 

Мы создаем специально отформатированный ассоциативный массив и возвращаем его родительскому вызывающему методу. В этом случае каждый элемент в массиве соответствует элементу в нашей форме.

Давайте сначала посмотрим на предметную область в качестве примера.

$form['user_warning_e-mail_subject'] = array(

Каждый элемент в массиве $form имеет уникальную строку, которая становится атрибутом HTML- имени элемента  при визуализации формы. Этому элементу затем присваивается массив атрибутов.

Чтобы получить полную информацию о всех элементов формы, определенных Drupal, а также свойств, которые реализует каждый из них, посетите страницу  https://api.drupal.org/api/drupal/elements/8.2.x .


Первым атрибутом является «#type» , который определяет, какой элемент формы будет отображаться. Все стандартные элементы формы HTML имеют типы, а также некоторые определенные для Drupal элементы. В этом случае мы создаем базовое текстовое поле.

Следующие два атрибута, #title и #description, определяют метку элемента и необязательное описание. Хотя ни один из этих атрибутов технически не требуется, настоятельно рекомендуется указывать значения для обоих этих атрибутов при создании элементов формы. Когда форма отображается, атрибут #title станет меткой поля, а атрибут #description будет напечатан как текст справки, обычно под самим полем. 

'#title' => t('Warning e-mail subject'),
'#description' => t('The subject of the e-mail which will be sent to users.'),

Любые стандартные атрибуты HTML также доступны в виде свойств или атрибутов API формы. Например, см. Следующие две строки кода.

'#size' => 40,
'#maxlength' => 120,

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

Form API также добавляет некоторые удобные свойства для целей проверки, такие как «#required» .

'#required' => TRUE,

Когда для '#required' установлено значение TRUE, Drupal выдаст ошибку, если форма отправлена ​​без значения в этом элементе. Обязательные поля также отмечены звездочкой в ​​их ярлыках. Опять же, это происходит автоматически без какого-либо дополнительного кода. Drupal даже выделит поле, когда к нему будет применена ошибка! Такая обработка ошибок на лету и проверка формы является одной из причин, по которым API-интерфейс Form так полезен для разработчиков. Это действительно уменьшает объем рутинной работы, связанной с созданием и обработкой HTML-форм.

Переходя к следующим элементам, вы можете увидеть, как этот шаблон повторяется. Например, поле тела электронной почты типа «#textarea» реализует свойства «#rows» и «#columns» , как и соответствующие атрибуты HTML для текстовой области . Элемент флажка (указывающий, должен ли администратор отправлять BCC на исходящие электронные письма) и кнопка отправки одинаково просты в эксплуатации.

Элементы


Form API также поддерживает специальные элементы формы HTML5, такие как электронная почта , телефон , номер и т. д. Полный список поддерживаемых типов элементов в Drupal 8 можно найти по адресу https://drupal.org/node/1315186 .


Когда мы посещаем URL-адрес, который мы зарегистрировали ранее ( admin / config / people / user_warning ), мы получаем форму, как показано на следующем снимке экрана(не забывайте чистить кэш, после каждого добавления кода):

form

Управление конфигурацией в формах


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

 

/**
   * Build the actual form.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    // Retrieve the stored configuration settings
    $config = $this->config('user_warning.settings');

    // Text field for the e-mail subject.
    $form['user_warning_e-mail_subject'] = array(
      '#type' => 'textfield',
      .
      .
      .
      '#default_value'  =>  $config->get('email.subject')
    );

    // Textarea for the body of the e-mail.
    $form['user_warning_e-mail_text'] = array(
      '#type' => 'textarea',
      .
      .
      .
      '#default_value'  =>  $config->get('email.body')
    );
    // Checkbox to indicate if admin should be sent a Bcc on e-mails.
    $form['user_warning_bcc'] = array(
      .
      .
      .
      '#default_value'  =>  $config->get('bcc'),
    );
    
    return parent::buildForm($form, $form_state);
  }

Прежде всего, мы получаем объект конфигурации нашего модуля и сохраняем его в переменной с именем $config :

// Retrieve the stored configuration settings
$config = $this->config('user_warning.settings');

Затем мы используем этот объект, чтобы получить индивидуальные настройки для каждого поля в нашей форме и присвоить его свойству #default_value данного элемента, используя метод $config-> get () :

// Text field for the e-mail subject.
$form['user_warning_e-mail_subject'] = array(
  '#type' => 'textfield',
  '#title' => t('Warning e-mail subject'),
  '#description' => t('The subject of the e-mail which will be sent to users.'),
  '#size' => 40,
  '#maxlength' => 120,
  '#required' => TRUE,
  '#default_value'  =>  $config->get('email.subject')
);

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

Сохранение этих параметров конфигурации выполняется после отправки формы в нашей реализации submitForm () . Давайте подробнее рассмотрим, как формы отправляются в Drupal.

Процесс отправки формы


В нашем примере мы расширяем класс ConfigFormBase для создания нашей формы. Этот класс сам по себе является расширением FormBase , который является классом по умолчанию для расширения при создании форм в Drupal. Этот базовый класс предоставляет два метода, которые обрабатывают процесс отправки формы: validateForm () и submitForm () .

Метод validateForm () выполняет дополнительную проверку помимо того, что предоставляет Drupal. Например, если вы хотите проверить, является ли почтовый индекс действительным. Даже если проверка не удалась для одного элемента, все методы проверки по-прежнему вызываются, поэтому Drupal может возвращать несколько ошибок в одной форме. Однако, если какой-либо метод проверки завершается неудачно, выполнение никогда не переходит к методу submitForm () .

Как только форма проходит проверку, вызывается метод submit. Именно здесь и делается настоящая работа - сохранение настроек, отправка электронной почты и создание контента, среди прочего. Метод submitForm() - одна из главных рабочих лошадок модулей Drupal.

Итак, давайте реализуем этот метод в UserWarningConfigForm .

Метод принимает два аргумента: $form - это исходный массив API формы для отправленной формы, и $form_state , который является классифицированным объектом, содержащим информацию, относящуюся к этой отправке. В частности, $form_state содержит все отправленные значения формы, основанные на их свойствах имени.

/**
   * Submits the form and saves the configuration settings.
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    
    
    
    // Save the configuration settings
    $this->configFactory->getEditable('user_warning.settings')
      ->set('email.subject', $form_state->getValue('user_warning_e-mail_subject'))
      ->set('email.body', $form_state->getValue('user_warning_e-mail_text'))
      ->set('bcc', $form_state->getValue('user_warning_bcc'))
      ->save();  
      
      parent::submitForm($form, $form_state);
  }

Наша функция отправки, на самом деле, довольно проста. Мы извлекаем объект конфигурации нашего модуля и затем сохраняем отправленные данные с помощью метода set() . Наши потребности в проверке обрабатываются встроенной формой проверки в Drupal, поэтому нам даже не нужен метод проверки для этих данных.

Любой класс, который расширяет ConfigFormBase, должен реализовывать метод getEditableConfigNames и возвращать массив имен полей конфигурации, которые он редактирует. Создание форм конфигурации

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

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

Итак, давайте продолжим и заполниv нашу административную форму. 

Мы будем использовать следующий текст в качестве темы письма:

[user:name], вы получили предупреждение!

И следующий текст для тела письма:

Здравствуйте! [user:name],

Вы оставили комментарий в беседе на сайте [site:name] Предупреждаем, что Вы нарушаете правила сайта - такое выражение отношения к оппонентам недопустимо.

Sincerely
[site:name]
[site:mail]

Как вы можете видеть выше, мы также определили некоторые специальные строки в тексте, которые называются токенами . Это заполнители, которые будут динамически заменяться Drupal во время обработки текста. Например, токен [user: name] будет автоматически заменен именем пользователя, которому мы доставляем электронное письмо. 

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

protected function getEditableConfigNames() {
    return array('user_warning.settings');
  }

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

Теперь мы закончили с созданием административной формы для нашего модуля, которая хранит настройки конфигурации электронной почты по умолчанию. Давайте перейдем ко второй форме, которая появляется на вкладке Предупреждения каждого пользователя в его профиле.

Вкладка Предупреждения


Ранее в этой главе мы создали локальную задачу, которая появляется на страницах профиля каждого пользователя. Давайте вернемся к нашему файлу user_warning.routing.yml и рассмотрим вторую запись маршрута, которую мы создали:

user_warning.user_tab:
  path: '/user/{user}/user-warning'
  defaults:
    _form: '\Drupal\user_warning\Form\UserWarningEmailForm'
    _title: 'Warning this user'    
  requirements:
    _permission: 'administer users'
  options:
    _admin_route: TRUE

Как и в форме администрирования, на этом маршруте мы хотим отображать форму по определенному URL, как определено в ключе _form по умолчанию . В этой форме мы извлекаем ранее определенные параметры конфигурации и заполняем их в соответствующих полях, давая «модератору» возможность переопределять значения по умолчанию при предупреждении определенного пользователя. Мы указали ключ _form на пространство имен класса, расположенное по пути \ Drupal \ user_warning \ Form \ UserWarningEmailForm, так что давайте продолжим и создадим этот класс в user_warning / src / Form в файле с именем UserWarningEmailForm.php :

<?php

/*
 * @file
 * 
 * Provides a simple form with a single "Send warning" button
 * that administrators can use to send a warning email to a given user
 */

namespace Drupal\user_warning\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;

class UserWarningEmailForm extends FormBase {
    
/**
 * Specify the form ID.
 */
public function getFormId() {
  return 'user_warning_email_form';
}
  
  /**
   * Build the actual form.
   */
  public function buildForm(array $form, FormStateInterface $form_state, AccountInterface $user = NULL) {
    
    // Retrieve the settings we stored earlier.
    $config = $this->config('user_warning.settings'); 
    $subject = $config->get('email.subject');
    $body = $config->get('email.body');

    // Create the email subject field.
    $form['subject'] = array (
      '#type' =>  'textfield',
      '#default_value'  =>  $subject,
      '#title'  =>  'Subject',
      '#required' =>  TRUE
    );

    // Create the email body field.
    $form['body'] = array (
      '#type' =>  'textarea',
      '#default_value'  =>  $body,
      '#title'  =>  'Message body',
      '#required' =>  TRUE
    );
       		            
    // Pass on the user object for later use.
    $form['account'] = array (
      '#type' =>  'value',
      '#value'  =>  $user
    );
        
    // Output the submit button.
    $form['user_warning_send_warning'] = array (
      '#type' =>  'submit',
      '#value'  =>  $this->t('Send warning')
    );
    
    return $form;
  }
  
  /**
   *  Submits the form and sends the warning email.
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    
    // Initialize parameters.
    $account = $form_state->getValue('account');	
    $params['account'] = $account;
    $params['body'] = $form_state->getValue('body');
    $params['subject'] = $form_state->getValue('subject');
        
    // Retrieve the default email address of the site.
    $site_mail = $this->config('system.site')->get('mail');
    
    // Check if admin needs to be BCC'd on warning e-mails.
    if ($this->config('user_warning.settings')->get('bcc') == TRUE) {
      $params['bcc'] = $site_mail;
    }    
    
    // Retrieve the mailManager service.
    $mailManager = \Drupal::service('plugin.manager.mail');
	
    // Invoke MailManager::mail().
    $mailManager->mail('user_warning', 'warning', $account->getEmail(), $account->getPreferredLangcode(), $params, $site_mail, TRUE);
  }  
}

Эта форма очень похожа на форму администрирования, которую мы создали ранее в этой главе, но следует отметить несколько очень важных вещей. Давайте ближе посмотрим, что мы здесь делаем.

Прежде всего, мы импортируем пространство имен Drupal \ Core \ Form \ FormBase . Как следует из названия, этот класс действует как основа всех форм API форм, созданных в Drupal. Если вы помните, когда мы создавали административную форму для нашего модуля, мы импортировали ConfigFormBase , которая расширяет FormBase для поддержки системы управления конфигурацией Drupal в формах API форм. В этом случае мы не создаем форму конфигурации, поэтому нам не нужен Drupal для ненужной загрузки всех компонентов системы управления конфигурацией, поэтому мы расширяем FormBase в нашем классе.

use Drupal\Core\Form\FormBase;

class UserWarningEmailForm extends FormBase {

На следующем шаге мы определяем уникальный идентификатор нашей формы путем реализации getFormId() .

/**
 * Specify the form ID.
 */
public function getFormId() {
  return 'user_warning_email_form';
}

Затем мы переходим к определению структуры нашей формы путем реализации buildForm() . Важное отличие по сравнению с формой администрирования, которую мы создали ранее, заключается в том, что функция этой формы заключается в том, чтобы в конечном итоге отправить предупреждение по электронной почте пользователю, страницу профиля которого мы посещаем. Для этого нам необходимо знать, кто этот пользователь, с программной точки зрения. Другими словами, нам нужен доступ к объекту пользователя, которому мы хотим доставить это письмо.

Если вы быстро посмотрите на определение маршрута этой формы, вы увидите, что мы определили следующий динамический путь к нашему маршруту:

user_warn.user_tab:
  path: '/user/{user}/user-warning'

В фоновом режиме происходит то, что Drupal попытается преобразовать {user} в пользовательский объект, используя встроенную службу ParamConverter , и передаст его так, чтобы он был доступен на протяжении всего маршрута. В свою очередь, мы должны иметь доступ к переменной $user в нашем классе формы, как новый параметр. И так как мы уже узнали, что все пользовательские объекты являются экземплярами AccountInterface , мы определяем этот тип для $user в методе buildForm () :

public function buildForm(array $form, FormStateInterface $form_state, AccountInterface $user = NULL) {

И это в основном способ построения динамических маршрутов (с параметром upcasting) в Drupal 8. В нашем случае пользовательский объект с URL  user / {user} / user-warning будет загружен и сделан доступным в качестве параметра в  buildForm() метод нашего класса формы.

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

Как и ранее, мы получаем параметры конфигурации нашего модуля из системы управления конфигурациями и сохраняем их в переменной с именем $config .

// Retrieve the settings we stored earlier.
$config = $this->config('user_warning.settings');

Далее мы определяем поля API-формы объекта и тела и устанавливаем для их свойств #default_value значения, сохраненные в настройках конфигурации нашего модуля.

$subject = $config->get('email.subject');
$body = $config->get('email.body');

// Create the email subject field.
$form['subject'] = array (
  '#type' =>  'textfield',
  '#default_value'  =>  $subject,
  '#title'  =>  'Subject',
  '#required' =>  TRUE
);

// Create the email body field.
$form['body'] = array (
  '#type' =>  'textarea',
  '#default_value'  =>  $body,
  '#title'  =>  'Message body',
  '#required' =>  TRUE
);

Следующим шагом является удобная функция, которую вы будете часто использовать при разработке форм API форм. Мы узнали несколько абзацев ранее, что наш метод buildForm() получает параметр с именем $user, который представляет пользовательский объект пользователя, чью страницу профиля мы посещаем. Как мы уже установили, цель здесь состоит в том, чтобы позволить модераторам сайта отправить этому пользователю электронное письмо с предупреждением, отправив форму, которую мы создаем. Это означает, что нам нужно добавить обработчик отправки в нашу форму, где мы позаботимся об отправке электронного письма. Но только метод buildForm() имеет доступ к $ user объект и по этому, нам понадобится доступ к нему в нашем методе обработчика отправки. Поэтому здесь мы используем небольшую хитрость и «присоединяем» объект $user к форме, чтобы мы могли получить к нему доступ позже:

// Pass on the user object for later use.
    $form['account'] = array (
      '#type' =>  'value',
      '#value'  =>  $user
    );

Элемент value аналогичен скрытым HTML-полям с двумя явными преимуществами. Во-первых, элементы value могут содержать любые данные, которые вы хотите, а не только строки. Массивы, объекты или любая другая сложная структура данных могут храниться и передаваться в элементе значения . 
Второе преимущество заключается в том, что элементы значения не печатаются обратно в браузер в исходном коде HTML. Это может повысить безопасность ваших данных, запретив пользователям просматривать и / или изменять их.
Последнее, что мы делаем в нашей реализации метода bulidForm(), это добавляем кнопку отправки. Это, опять же, ключевое отличие между формой администрирования нашего модуля и текущей формой, которую мы создаем. В предыдущем примере мы не определяли кнопку отправки просто потому, что нам это не нужно. Класс ConfigFormBase автоматически добавляет это в нашу форму, поэтому нам не нужно беспокоиться об этом.

// Output the submit button.
$form['user_warning_send_warning'] = array (
  '#type' =>  'submit',
  '#value'  =>  $this->t('Send warning')
);

Как вы можете видеть выше, кнопка отправки в Form API определяется с помощью специального элемента submit, установленного как #type . Мы также установили его #VALUE на «Отправить предупреждение» , завернутые в встроенный translationManager  т() метод локализации. Это значение будет напечатано как текст кнопки при построении формы.

Наша форма заполнена. Давайте посетим страницу профиля пользователя и перейдем на вкладку «Предупреждения». Мы должны увидеть вывод, похожий на этот:

 

warning

Если все прошло хорошо, пользовательская форма предупреждения должна появиться, как указано выше, и наши предварительно определенные поля темы и тела должны содержать текст, который мы вставили в форму администрирования нашего модуля.

На данный момент мы еще не позаботились о доставке электронных писем. Итак, давайте сделаем это сейчас.

Отправка писем в Друпале


Drupal реализует собственную систему шаблонов электронной почты. Изначально может показаться, что эта система чрезмерно сложна, но она обеспечивает огромную гибкость для разработчиков модулей. Основные функции отправки электронной почты управляются службой MailManager , расположенной в папке core / lib / Drupal / Core / Mail / MailManager.php . Этот класс также действует как менеджер плагинов , то есть почтовая система Drupal предоставляет архитектуру на основе плагинов.

Мы узнаем больше о плагинах в следующей статье.


Технически, отправка электронной почты в Drupal - это многоэтапный процесс:

Вызывается MailManager :: mail () , указывающий, какая почта отправляется и какие параметры уникальны для этого конкретного сообщения (адрес электронной почты получателя, язык, на котором должна быть отправлена ​​почта и т. д.).
 
Затем Drupal создает сообщение электронной почты со стандартными заголовками в сочетании с информацией, переданной в MailManager :: mail () .
 
Вызваны реализации hook_mail () . Это где тема и тело письма добавляются.
 
Затем полностью составленный почтовый массив передается в hook_mail_alter () , что позволяет другим модулям изменять его (например, добавлять общую подпись ко всем исходящим электронным письмам).
 
Затем почта передается методу mail () активной почтовой системы для доставки.
Это довольно долгий процесс только для отправки простого электронного письма! Тем не менее, в большинстве случаев разработчикам придется беспокоиться только о первом шаге из приведенного выше списка: вызове MailManager :: mail () .

При тестировании функций электронной почты (локально или на удаленном компьютере) убедитесь, что ваш сервер настроен на отправку электронной почты. Если вы сомневаетесь, скачайте и установите очень популярный модуль аутентификации SMTP для Drupal отсюда:  https://www.drupal.org/project/smtp .

Этот модуль позволяет вам указать внешний SMTP-сервер (например, Gmail), который будет заботиться о доставке вашей электронной почты.


Вызов MailManager :: mail ()


Давайте подробнее рассмотрим нашу реализацию submitForm (), которая вызывается после отправки формы предупреждения:

/**
   *  Submits the form and sends the warning email.
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    
    // Initialize parameters.
    $account = $form_state->getValue('account');	
    $params['account'] = $account;
    $params['body'] = $form_state->getValue('body');
    $params['subject'] = $form_state->getValue('subject');
        
    // Retrieve the default email address of the site.
    $site_mail = $this->config('system.site')->get('mail');
    
    // Check if admin needs to be BCC'd on warning e-mails.
    if ($this->config('user_warning.settings')->get('bcc') == TRUE) {
      $params['bcc'] = $site_mail;
    }    
    
    // Retrieve the mailManager service.
    $mailManager = \Drupal::service('plugin.manager.mail');
	
    // Invoke MailManager::mail().
    $mailManager->mail('user_warning', 'warning', $account->getEmail(), $account->getPreferredLangcode(), $params, $site_mail, TRUE);
  }

Сначала мы создаем экземпляр службы MailManager, вызывая  \ Drupal :: service ('plugin.manager.mail'), и сохраняем его в переменной с именем $ mailManager . Затем мы вызываем  $ mailManager-> mail () , который позволяет нам начать процесс доставки электронной почты.

Этот метод требует, чтобы мы передали ему довольно много информации. Давайте рассмотрим каждый из этих аргументов подробно:

  • Первый аргумент указывает, какой модуль отправляет это сообщение. Мы устанавливаем это в 'user_warning', так как это тип электронной почты, предоставляемый нашим пользовательским модулем. Тем не менее, вы можете отправить письмо, реализованное другим модулем, если вам нужно. 
  • Второй аргумент, предупреждает , является ключом , который передается MailManager . Любая реализация электронной почты может определять несколько электронных писем, однозначно идентифицируемых текстовым ключом.
  • Третий аргумент содержит адрес получателя. Мы извлекаем это из пользовательского объекта для пользователя, профиль которого мы посетили, как передано в форме предупреждения.
  • Четвертый аргумент указывает, на каком языке следует отправлять почту. Это важно, поскольку отдельные пользователи могут указывать языковые предпочтения, отличающиеся от языка сайта по умолчанию. Мы должны соблюдать этот выбор, если это возможно, при отправке нашего электронного письма этому пользователю. Метод $ account-> getPreferredLangcode () облегчает эту задачу, возвращая выбор языка пользователя.
  • Пятый аргумент - это ассоциативный массив параметров, передаваемых в MailManager . Любая пользовательская информация, необходимая для создания электронной почты, должна быть размещена здесь. Мы создали массив с именем $params , который в нашем случае содержит тело и тему письма, а также флаг bcc на основе наших сохраненных настроек конфигурации.
  • Шестой аргумент содержит адрес электронной почты, с которого следует отправлять это письмо. Когда вы впервые установили Drupal, вы должны были указать административный адрес электронной почты. Этот адрес уже используется в качестве источника для других системных писем (например, для проверки учетной записи), поэтому имеет смысл использовать его и в качестве электронной почты отправителя. Адрес электронной почты хранится в Drupal как параметр конфигурации, поэтому мы получаем его, используя $this-> config ('system.site') -> get ('mail') .

 
Наконец, последняя переменная указывает, должна ли почта быть отправлена. Неудивительно, что почтовое сообщение в Drupal построено в специально структурированном ассоциативном массиве. В конце процесса создания почты этот массив обычно передается методу mail() указанной почтовой службы, который обрабатывает фактическую доставку почты. Однако, установив для этого параметра значение FALSE , MailManager пропустит этап доставки, что позволит вам взять структурированный массив, который он возвращает, и самостоятельно обработать доставку.
Это все хорошо, и у нас теперь есть полностью функциональный модуль. Тем не менее, есть еще одна вещь, которую мы должны рассмотреть более подробно.

Система токенов


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

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

Примером токена является [site: name] . Когда текст, содержащий этот токен, передается через replace() , он заменяется именем вашего сайта, как определено в Administer | Конфигурация | Информация о сайте . Если вы измените имя своего сайта, то в будущем весь текст, содержащий этот токен, будет отражать это изменение. Drupal предоставляет множество токенов, содержащих информацию о пользователях, узлах, конфигурации всего сайта и многое другое.

Токены также могут быть «связаны» - токен может ссылаться на другой токен, который может ссылаться на еще один. Например, токен [node: author] содержит имя автора узла, а токен [user: e-mail] содержит адрес электронной почты данного пользователя. Чтобы получить адрес электронной почты автора узла, вы можете связать их вместе с токеном [node: author: e-mail] .

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

Для получения дополнительной информации о том, как выставлять токены в вашем модуле, см. Следующие ссылки: 
http://api.drupal.org/api/function/hook_token_info/8 
http://api.drupal.org/api/function/hook_tokens/ 8


Реализация токенов в вашем тексте


Очевидное место, где User Warning может использовать токены, - это текст исходящих электронных писем. И на самом деле, мы уже использовали некоторые токены, когда заполняли форму конфигурации нашего модуля:

Здравствуйте! [user:name],

Вы оставили комментарий в беседе на сайте [site:name] Предупреждаем, что Вы нарушаете правила сайта - такое выражение отношения к оппонентам недопустимо.

Sincerely
[site:name]
[site:mail]

Этот текст содержит три токена:

  • [site: name] : название сайта, как описано ранее
  • [site: mail] : административный адрес электронной почты (это тот же адрес электронной почты, который возвращается config ('system.site') -> get ('mail')
  • [user: name] : логин данного пользователя

Как мы уже обсуждали, метод replace(), предоставляемый службой токенов, обеспечивает распознавание и динамическую замену заполнителей фактическими значениями во время обработки. Что действительно полезно, так это то, что при отправке электронных писем Drupal автоматически вызывает метод replace() . На самом деле это делается в реализации hook_mail() system.module, которая вызывается каждый раз, когда электронное письмо отправляется из нашей системы:

/**
 * Implements hook_mail().
 */
function system_mail($key, &$message, $params) {
  $token_service = \Drupal::token();

  $context = $params['context'];

  $subject = PlainTextOutput::renderFromHtml($token_service->replace($context['subject'], $context));
  $body = $token_service->replace($context['message'], $context);

  $message['subject'] .= str_replace(["\r", "\n"], '', $subject);
  $message['body'][] = $body;
}

Как вы можете видеть выше, и $subject, и $body содержат данные, которые были переданы через  $token_service-> replace() для обеспечения замены токена.

Теперь, если мы пойдем дальше и отправим форму предупреждения, мы должны получить электронное письмо с похожим содержанием:

Здравствуйте! [user:name],

Вы оставили комментарий в беседе на сайте [site:name] Предупреждаем, что Вы нарушаете правила сайта - такое выражение отношения к оппонентам недопустимо.

Sincerely
[site:name]
[site:mail]

Резюме


В действительности модуль User Warning, вероятно, имеет ограниченную полезность, но он помогает представить многие из основных концепций, которые разработчики Drupal будут использовать ежедневно. Теперь вы можете создавать страницы по определенному URL-адресу с помощью routing.yml и реализовывать формы на этих страницах с помощью API форм. Значения, представленные в этой форме, могут быть сохранены с помощью submitForm (), и вы узнали, как вы можете управлять конфигурацией вашего модуля. Вы также можете отправить результаты отправки формы в виде собственного электронного письма, используя динамические токены для замены текста.

assistant Теги

keyboard_arrow_up