12.7. Плагины в Drupal 8. Выводим блок.

Drupal 8 blocks

В этом уроке мы разберем как выводить блоки программно через кастомный модуль в Drupal 8.

Примеры кода можно посмотреть на github:
https://github.com/levmyshkin/drupalbook8

Начнем с добавления файла содержащего PHP класс, именно так создаются блоки в Drupal 8 через кастомный модуль. Процесс создания файла такой же как и класса для страницы, как мы делали здесь:
12.3. Создаем кастомный Drupal 8 модуль. Вывод страницы программно.

Только нам нужно создать файл с plugin'ом:

modules/custom/drupalbook/src/Plugin/Block/FirstBlock.php

Drupal 8 block programmatically

Давайте скопируем этот код блока и потом разберем каждый из частей кода modules/custom/drupalbook/src/Plugin/Block/FirstBlock.php:

<?php

namespace Drupal\drupalbook\Plugin\Block;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;

/**
 * Provides a block with a simple text.
 *
 * @Block(
 *   id = "drupalbook_first_block_block",
 *   admin_label = @Translation("My first block"),
 * )
 */
class FirstBlock extends BlockBase {
  /**
   * {@inheritdoc}
   */
  public function build() {
    $config = $this->getConfiguration();

    if (!empty($config['drupalbook_first_block_settings'])) {
      $text = $this->t('Hello @name in block!', ['@name' => $config['drupalbook_first_block_settings']]);
    }
    else {
      $text = $this->t('Hello World in block!');
    }

    return [
      '#markup' => $text,
    ];
  }

  /**
   * {@inheritdoc}
   */
  protected function blockAccess(AccountInterface $account) {
    return AccessResult::allowedIfHasPermission($account, 'access content');
  }

  /**
   * {@inheritdoc}
   */
  public function blockForm($form, FormStateInterface $form_state) {
    $config = $this->getConfiguration();

    $form['drupalbook_first_block_settings'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Name'),
      '#description' => $this->t('Who do you want to say hello to?'),
      '#default_value' => !empty($config['drupalbook_first_block_settings']) ? $config['drupalbook_first_block_settings'] : '',
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->configuration['drupalbook_first_block_settings'] = $form_state->getValue('drupalbook_first_block_settings');
  }
}

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

Чтобы вывести блок, нужно теперь зайти в настройку блоков и добавить наш блок:

Drupal Block Layout

Выведем наш блок в регоине Left Sidebar или другой колонке, где вам будет удобно.

Drupal Block

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

Drupal 8 block

Теперь когда наш блок вывелся, давайте разберемся как работает код для добавления блока:

Drupal 8 custom block

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

namespace Drupal\drupalbook\Plugin\Block;

use Drupal\Core\Access\AccessResult;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;

Дальше в комментариях идет аннотация. Нам не нужно определять блок где-нибудь в YML-файлах, он будет автоматически подгружен друпалом с помощью аннатации.

/**
 * Provides a block with a simple text.
 *
 * @Block(
 *   id = "drupalbook_first_block_block",
 *   admin_label = @Translation("My first block"),
 * )
 */

Здесь мы указываем @Block, что это будет плагин блока. id, admin_label будет автоматически подхватывается друпалом, поэтому id должен быть уникальным. @Translation помогает перевести потом label блока через админку друпала.

class FirstBlock extends BlockBase {

Мы наследуемся от класса BlockBase, вы можете зажать ctrl и кликнуть на BlockBase в PhpStorm и посмотреть какие методы можно переопределить в нашем блоке, мы используем не все из них. Переопределять другие методы можно по мере необходимости.

Давайте разберем дальше методы класса в порядке от более понятному к менее понятному:

  /**
   * {@inheritdoc}
   */
  protected function blockAccess(AccountInterface $account) {
    return AccessResult::allowedIfHasPermission($account, 'access content');
  }

Мы уже рассматривали доступ к странице в этой статье, в блоках мы можем использовать те же права доступа:

http://drupalbook.ru/drupal/126-rout-s-parametrom

Здесь мы используем тот же класс AccessResult.

  /**
   * {@inheritdoc}
   */
  public function blockForm($form, FormStateInterface $form_state) {
    $config = $this->getConfiguration();

    $form['drupalbook_first_block_settings'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Name'),
      '#description' => $this->t('Who do you want to say hello to?'),
      '#default_value' => !empty($config['drupalbook_first_block_settings']) ? $config['drupalbook_first_block_settings'] : '',
    ];

    return $form;
  }

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

Drupal 8 block

Здесь вы можете увидеть наше поле Name, которое мы добавили в методе blockForm():

Drupal 8 block settings

Мы используем Drupal Form API для создания полей для формы:

https://www.drupal.org/docs/8/api/form-api

https://api.drupal.org/api/drupal/elements/8.5.x

Мы еще будем разбирать Form API в дальнейшем подробно и сделаем мультстеп попап форму. Пока что вы можете посмотреть какие еще типы полей вы можете использовать с помощью Form API. Каждое поле добавляется с помощью массива. Попробуйте добавить еще полей в форму блока.

  /**
   * {@inheritdoc}
   */
  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->configuration['drupalbook_first_block_settings'] = $form_state->getValue('drupalbook_first_block_settings');
  }

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

  /**
   * {@inheritdoc}
   */
  public function build() {
    $config = $this->getConfiguration();

    if (!empty($config['drupalbook_first_block_settings'])) {
      $text = $this->t('Hello @name in block!', ['@name' => $config['drupalbook_first_block_settings']]);
    }
    else {
      $text = $this->t('Hello World in block!');
    }

    return [
      '#markup' => $text,
    ];
  }

И теперь основной метод build(), который выводит контент блока. В этом методе мы проверяем конфигурацию блока и если у нас есть Имя, то мы выводим текст с именем. Обратите внимание на метод $this->t(), он позволяет перевести текст через админку друпала на другие языки. Даже если вы делаете сайт на русском языке, то лучше всего все текст писать на английском, а потом обернуть текст в $this->t() и перевести через админку друпала. Также у нас используется placeholder @name, он нужен для того чтобы переводить только нужный текст, а не значения полей, которые мы вводим в конфигарационных формах. Это позволяет избегать множества строк перевода, например если не использовать placeholder, то для перевода в админке будут доступны все варианты текста, которые пройдут через $this->t():

Hello Josh ...
Hello Mike ...
Hello Ivan ...
и так далее.

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

Примеры кода можно посмотреть на github: 
https://github.com/levmyshkin/drupalbook8