Помните, что из-за процесса разработки Drupal 8 на момент написания этой статьи некоторые части кода могли быть устаревшими. Взгляните на этот репозиторий, в котором я пытаюсь обновить пример кода и заставить его работать с последней версией Drupal 8.
В первой части этой серии статей о разработке модулей для Drupal 8 мы начали с основ. Мы увидели, какие файлы были необходимы, чтобы Drupal узнал о нашем модуле, как работает процесс маршрутизации и как программно создавать ссылки меню в качестве конфигурации.
В этом уроке мы пойдем немного дальше с нашим модулем песочницы, найденным в этом репозитории, и рассмотрим две новые важные функциональные возможности: блоки и формы. Для этого мы создадим пользовательский блок, который возвращает некоторый настраиваемый текст. После этого мы создадим простую форму, используемую для распечатки пользовательских значений на экран.
Drupal 8 блоков
Отличным нововведением в блочном API в D8 стал переход к созданию более заметных блоков, сделав их плагинами (совершенно новая концепция). Это означает, что они представляют собой функциональные элементы многократного использования (скрытые), поскольку теперь вы можете создать блок в пользовательском интерфейсе и повторно использовать его по всему сайту — вы больше не ограничены использованием блока только один раз.
Давайте продолжим и создадим простой тип блока, который выводит на экран Hello World! по умолчанию. Все, что нам нужно для работы — это один файл класса, расположенный в папке src/Plugin/Block
корневого каталога нашего модуля. Давайте назовем наш новый тип блока DemoBlock
, и, естественно, он должен находиться в файле с именем DemoBlock.php
. Внутри этого файла мы можем начать со следующего:
<?php namespace Drupal\demo\Plugin\Block; use Drupal\block\BlockBase; use Drupal\Core\Session\AccountInterface; /** * Provides a 'Demo' block. * * @Block( * id = "demo_block", * admin_label = @Translation("Demo block"), * ) */ class DemoBlock extends BlockBase { /** * {@inheritdoc} */ public function build() { return array( '#markup' => $this->t('Hello World!'), ); } /** * {@inheritdoc} */ public function access(AccountInterface $account) { return $account->hasPermission('access content'); } }
Как и во всех других файлах классов, мы начинаем с пространства имен нашего класса. Затем мы используем класс BlockBase
чтобы мы могли расширить его, а также класс AccountInterface
чтобы мы могли получить доступ к AccountInterface
в данный момент пользователю. Затем следует то, чего вы точно не видели в Drupal 7: аннотации.
Аннотации — это инструмент обнаружения PHP, расположенный в блоке комментариев того же файла, что и определение класса. Используя эти аннотации, мы сообщаем Drupal, что мы хотим зарегистрировать новый тип блока ( @Block
) с идентификатором demo_block
и admin_label
демо-блока (прошедшего через систему перевода).
Затем мы расширяем класс BlockBase
в наш собственный DemoBlock
, внутри которого мы реализуем два метода (наиболее распространенные из которых вы реализуете). Метод build()
является наиболее важным, поскольку он возвращает визуализируемый массив, который будет распечатан блоком. Метод access()
контролирует права доступа для просмотра этого блока. AccountInterface
параметр является экземпляром класса AccountInterface
который в данном случае будет текущим пользователем.
Еще одна интересная вещь, которую стоит отметить, это то, что мы больше не используем функцию t()
глобально для перевода, но мы ссылаемся на метод t()
реализованный в родительском классе.
И все, вы можете очистить кеш и перейти на страницу конфигурации Block layout
. Круто то, что у вас есть типы блоков справа (которые вы можете фильтровать), и вы можете разместить один или несколько блоков этих типов в различных регионах на сайте.
Конфигурация блока Drupal 8
Теперь, когда мы увидели, как создать новый тип блока для использования из пользовательского интерфейса, давайте коснемся API и добавим форму конфигурации для него. Мы сделаем так, чтобы вы могли редактировать блок, указывать имя в текстовом поле, и тогда блок скажет привет этому имени, а не миру .
Сначала нам нужно определить форму, которая содержит наше текстовое поле. Поэтому внутри нашего класса DemoBlock
мы можем добавить новый метод с именем blockForm()
:
/** * {@inheritdoc} */ public function blockForm($form, &$form_state) { $form = parent::blockForm($form, $form_state); $config = $this->getConfiguration(); $form['demo_block_settings'] = array( '#type' => 'textfield', '#title' => $this->t('Who'), '#description' => $this->t('Who do you want to say hello to?'), '#default_value' => isset($config['demo_block_settings']) ? $config['demo_block_settings'] : '', ); return $form; }
Эта реализация API формы должна выглядеть очень знакомой из Drupal 7. Однако здесь происходят некоторые новые вещи. Сначала мы извлекаем массив $form
из родительского класса (поэтому мы создаем существующую форму, добавляя наше собственное поле). Стандартный ООП материал. Затем мы получаем и сохраняем конфигурацию для этого блока. Класс BlockBase
определяет метод getConfiguration()
который делает это для нас. И мы demo_block_settings
значение demo_block_settings
как #default_value
если оно уже установлено.
Затем пришло время обработчику отправки этой формы, который обработает значение нашего поля и сохранит его в конфигурации блока:
/** * {@inheritdoc} */ public function blockSubmit($form, &$form_state) { $this->setConfigurationValue('demo_block_settings', $form_state['values']['demo_block_settings']); }
Этот метод также входит в класс DemoBlock
и все, что он делает, это сохраняет значение поля demo_block_settings
как новый элемент в конфигурации блока (с тем же именем для согласованности).
Наконец, нам нужно адаптировать наш метод build()
для включения имени, чтобы передать привет:
/** * {@inheritdoc} */ public function build() { $config = $this->getConfiguration(); if (isset($config['demo_block_settings']) && !empty($config['demo_block_settings'])) { $name = $config['demo_block_settings']; } else { $name = $this->t('to no one'); } return array( '#markup' => $this->t('Hello @name!', array('@name' => $name)), ); }
К настоящему времени это должно выглядеть довольно просто. Мы извлекаем конфигурацию блока и, если значение нашего поля установлено, мы используем его для печатного оператора. Если нет, используйте универсальный. Вы можете очистить кэш и протестировать его, отредактировав блок, назначенный региону, и добавив имя, чтобы передать привет. Следует помнить, что вы по-прежнему несете ответственность за дезинфекцию пользовательского ввода при печати на экране. Я не включил эти шаги для краткости.
Drupal 8 форм
Последнее, что мы собираемся исследовать в этом уроке, это как создать простую форму. Из-за ограниченного пространства я не буду рассматривать его аспект управления конфигурацией (хранение значений конфигурации, отправленных через формы). Скорее, я проиллюстрирую простое определение формы, представленные значения просто распечатываются на экране, чтобы показать, что это работает.
В Drupal 8 все функции определения форм сгруппированы внутри класса. Итак, давайте определим наш простой класс DemoForm
внутри src/Form/DemoForm.php
:
<?php /** * @file * Contains \Drupal\demo\Form\DemoForm. */ namespace Drupal\demo\Form; use Drupal\Core\Form\FormBase; class DemoForm extends FormBase { /** * {@inheritdoc}. */ public function getFormId() { return 'demo_form'; } /** * {@inheritdoc}. */ public function buildForm(array $form, array &$form_state) { $form['email'] = array( '#type' => 'email', '#title' => $this->t('Your .com email address.') ); $form['show'] = array( '#type' => 'submit', '#value' => $this->t('Submit'), ); return $form; } /** * {@inheritdoc} */ public function validateForm(array &$form, array &$form_state) { if (strpos($form_state['values']['email'], '.com') === FALSE ) { $this->setFormError('email', $form_state, $this->t('This is not a .com email address.')); } } /** * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state) { drupal_set_message($this->t('Your email address is @email', array('@email' => $form_state['values']['email']))); } }
Помимо ООП, все должно выглядеть очень знакомым для Drupal 7. API форм практически не изменился (за исключением добавления некоторых новых элементов формы и инкапсуляции этого класса). Итак, что происходит выше?
Во-первых, мы FormBase
пространство имен классом и используем базовый класс FormBase
чтобы мы могли расширить его с помощью нашего собственного класса DemoForm
. Затем мы реализуем 4 метода, 3 из которых должны выглядеть очень знакомыми. Метод getFormId()
является новым и обязательным и используется просто для возврата имени машины формы. Метод buildForm()
снова является обязательным, и он формирует форму. Как? Так же, как вы привыкли к Drupal 7. Метод validateForm()
является необязательным, и его назначение также должно быть совершенно ясно из D7. И наконец, метод submitForm()
выполняет обработку submitForm()
. Очень логично и организованно.
Так чего же мы пытаемся достичь с помощью этой формы? У нас есть поле электронной почты (новый элемент формы в Drupal 8), которое мы хотим заполнить. По умолчанию Drupal проверяет, является ли введенное значение адресом электронной почты. Но в нашей функции проверки мы удостоверимся, что это адрес электронной почты .com
и если нет, мы установим в поле ошибку формы. Наконец, обработчик отправки просто печатает сообщение на странице.
Последнее, что нам нужно сделать, чтобы использовать эту форму, — это предоставить маршрут для нее. Поэтому отредактируйте файл demo.routing.yml
и добавьте следующее:
demo.form: path: '/demo/form' defaults: _form: '\Drupal\demo\Form\DemoForm' _title: 'Demo Form' requirements: _permission: 'access content'
поdemo.form: path: '/demo/form' defaults: _form: '\Drupal\demo\Form\DemoForm' _title: 'Demo Form' requirements: _permission: 'access content'
Это должно выглядеть знакомо из предыдущей статьи, в которой мы перенаправили простую страницу. Единственная большая разница заключается в том, что вместо _content
при defaults
по defaults
мы используем _form
чтобы указать, что целью является класс формы. И поэтому значение — это имя класса, которое мы только что создали.
Очистите кеши и перейдите к demo/form
чтобы увидеть форму и проверить ее.
Если вы знакомы с drupal_get_form()
и вам интересно, как загрузить форму, как мы это drupal_get_form()
в Drupal 7, ответ — в глобальном классе Drupal . Таким образом, чтобы получить форму, вы можете использовать ее formBuilder()
и сделать что-то вроде этого:
$form = \Drupal::formBuilder()->getForm('Drupal\demo\Form\DemoForm');
Затем вы можете вернуть $form
который будет отображаемым массивом формы.
Вывод
В этой статье мы продолжили исследование разработки модуля Drupal 8 с двумя новыми темами: блоки и формы. Мы видели, как создать наш собственный тип блока, который мы можем использовать для создания блоков в пользовательском интерфейсе. Мы также узнали, как добавить пользовательскую конфигурацию и сохранить значения для последующего использования. Что FormBase
форм, мы увидели простую реализацию класса FormBase
которую мы использовали для вывода на экран значения, представленного пользователем.
В следующем уроке мы кратко рассмотрим формы конфигурации. Мы сохраним значения, предоставленные пользователем, используя систему конфигурации Drupal 8. Кроме того, мы рассмотрим сервисный контейнер и внедрение зависимостей и то, как они работают в Drupal 8. Увидимся тогда.