Drupal 8: Пишем плагин Display Suite поля

В прошлой статье я написал как создавать собственные DisplaySuite поля в Drupal 7. После я решил посмотреть и разобраться как они работают в Drupal 8. И в этой статье я уже расскажу как создать те же самые поля, но в реалиях Drupal 8.

Что поменялось и как работают

Изменился подход. Теперь, как и большинство других хуков, hook_ds_fields_info() заменён системой плагинов. Это реально удобнее и позволяет сохранять модуль понятным и чистым. Это, к слову, решает проблему переизбытка кода в файле о котором я рассказывал в статье для D7.

Display Suite предоставляет два типа плагинов DsField и DsFieldTemplate. Обратите внимание, что для создания полей используется только DsField. Вы можете подумать что DsFieldTemplate это замена DS_FIELD_TYPE_THEME, но это не так. Этот тип плагинов позволяет объявлять обработчики полей. Если вы знакомы с DS, вы видели что есть возможность выбора Field Template после активации соответствующей функции. После этого появлялся выбор: Full reset, Expert, Minimal - вот это они и есть, а этот тип плагина позволяет добавлять их туда. Все поля теперь объявляются через один плагин.

Поля для DisplaySuite хранятся по адресу src/Plugin/DsField, у каждого поля свой файлик который содержит класс, который, в свою очередь, наследуется от DsFieldBase. Также, новой особенностью является то, что в D7 поле возвращало строку со значением, в D8 поле должно возвращать render array, и если вы хотите вернуть строку, то возвращать придется render array с markup. В основном концепция осталась прежней, но вот подход соответственно изменился.

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

P.s. далее по коду подразумевается что модуль, в котором пишется код, называется dummy.


Пример №1 - набор минимум


В данном примере показан набор минимум. В котором мы объявляем поле и выводим данные в нём, работая с сущностью.

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

Информация о поле указывается как и у всех в аннотации к классу. Плагин принимает следующие данные:

  • id: машинное имя поля. В нижнем регистре, латинские буквы и знак подчеркивания.
  • title: Человекопонятное имя поля, просто лейбл для админки.
  • entity_type: Название сущности для которой применимо поле. Сущность одна!
  • provider: Указываем кто предоставляет поле, в данном случае название нашего модуля.
  • ui_limit: bundle|view_mode - указываем на каких подтипах сущности и его форматах вывода будет доступно поле. Используйте знак * для значения 'all'. Несколько значений указываются в следующем формате: {"article|teaser", "news|*", "*|*"}

Листинг файла src/Plugin/DsField/WordCount.php

<?php

namespace Drupal\dummy\Plugin\DsField;

use Drupal\ds\Plugin\DsField\DsFieldBase;
use Drupal\Component\Render\FormattableMarkup;

/**
 * Поле которое выводит количество слов в содержимом.
 *
 * @DsField(
 *   id = "word_count",
 *   title = @Translation("DS: Word count"),
 *   provider = "dummy",
 *   entity_type = "node",
 *   ui_limit = {"article|full"}
 * )
 */
class WordCount extends DsFieldBase {
  /**
   * {@inheritdoc}
   *
   * Метод который должен вернуть результат для поля.
   */
  public function build() {
    // Записываем для удобства объект текущей сущности в переменную.
    $entity = $this->entity();
    // Проверяем есть ли значение в поле body. Если поле ничего не вернет
    // это воспринимается как пустое поле и оно не выводится.
    if ($body_value = $entity->body->value) {
      return [
        '#type' => 'markup',
        '#markup' => new FormattableMarkup(
          '<strong>Количество слов в тексте:</strong> @word_count',
          [
            '@word_count' => str_word_count(strip_tags($body_value))
          ]
        )
      ];
    }
  }
}

Теперь заходим в управление отображением типа материала article, формат вывода "Full content", и там должно появиться поле.

Созданное поле должно появиться в списке.

На странице материала теперь будет выводиться количество слов в тексте:

Результат обработки нашего поля.

Разумеется это не весть текст на скрине ;)

Если оно у вас не появилось, сбросьте кэш drush cr или перейдите по url /core/rebuild.php. Если вы не отключали кэширование, советую сделать отключение кэширование бэкенда.


Пример №2 - выбор формата

Для добавления своих форматов мы должны добавить метод formatters().

В D7 у поля типа DS_FIELD_TYPE_THEME была возможность указания форматов вывода, данные форматы становятся на выбор в админке, по принцппу как, например, у поля body, где вы вбираете выводить полный текст или обрезанный. В отличии от D7, в D8 не требуется объявлять одноименную theme функцию, которая будет вызвана при выборе соответствующего формата. Основываясь на том что вывод будет через render array, то вы можете использовать уже существующие theme функции, или же объявить свои для конкретных случаев. Говоря проще, theme функция объявляется при необходимости.

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

Листинг src/Plugin/DsField/WordFrequency.php

<?php

namespace Drupal\dummy\Plugin\DsField;

use Drupal\ds\Plugin\DsField\DsFieldBase;

/**
 * Поле выводит список повторяющихся слов из поля body.
 *
 * @DsField(
 *   id = "word_frequency",
 *   title = @Translation("DS: Word frequency"),
 *   provider = "dummy",
 *   entity_type = "node",
 *   ui_limit = {"article|full"}
 * )
 */
class WordFrequency extends DsFieldBase {

  /**
   * {@inheritdoc}
   * Это наш собственный метод в котором мы подготавливаем массив с
   * повторящимися полями и их количеством повторений. Сделано это лишь для
   * поддержания чистоты кода и читабельности, это не обязательный метод.
   */
  public function prepareWordFrequencyArray() {
    $entity = $this->entity();
    if ($body_value = $entity->body->value) {
      # Получаем весь список повторяющихся слов.
      $words_with_count = array_count_values(str_word_count(strip_tags($body_value), 1));
      # Удаляем значения меньше меньше 2.
      foreach ($words_with_count as $k => $v) {
        if ($v < 2) {
          unset($words_with_count[$k]);
        }
      }
      # Сортируем по убыванию.
      arsort($words_with_count);
      # Теперь нам надо создать массив со значениями, чтобы было проще отдавать
      # на рендер. Нам нужен чтобы ключ был любым а значение: "Слово х N".
      $results = [];
      foreach ($words_with_count as $word => $count) {
        $results[] = $word . ' x ' . $count;
      }
      # Возвращаем результат.
      return $results;
    }
  }

  /**
   * {@inheritdoc}
   */
  public function formatters() {
    # Возвращаем массив с возможными форматами: ключ => метка.
    return ['ol' => 'Нумерованный список', 'ul' => 'Маркированный список'];
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    $config = $this->getConfiguration();
    # Записываем выбранный формат в переменную.
    $list_type = $config['field']['formatter'];
    return [
      '#theme' => 'item_list',
      '#title' => 'Повторяющиеся слова:',
      '#items' => $this->prepareWordFrequencyArray(),
      '#list_type' => $list_type,
    ];
  }
}


В админке должен появиться выбор формата вывода:

Выбор формата вывода у поля.


Ну а при заходе на страницу, должны увидеть список:

Вывод списка в выбранном формате


Пример №3 - форма с настройками


Также у нас есть возможность объявлять форму с настройками для нашего поля. Чтобы какие-то данные пользователи могли менять прямо в админке. С этим нам поможет справиться метод settingsForm(), в котором мы и объявим форму настроек. Также тут есть ещё один метод settingsSummary(), он позволяет выводить краткую справку о настройках на странице полей, без необходимости раскрывать форму. Это очень удобно и полезно, только сразу оговорюсь, в методе settingsSummary() отсутствуют форматы поля, поэтому они пригодны исключительно для вывода значений настроек или собственной информации.

Пример поля, которое объявляет собственные настройки.

На картинке выше, поле Tags раскрыто, и перед нами форма с настройками для поля, а поля выше, Image, в столбце Widget выводится краткая информация о выбранных настройках для данного поля - это как раз и есть settingsSummary().

Теперь перейдем непосредственно к практической части. Пример может тупой, но лучше ничего не придумал. Допустим у нас на сайте есть какой-то раздел, например услуги, и клиент хочет чтобы под каждым материалом была справка типа: "Остались вопросы? Позвоните нам +7 (999) 123-45-67". В данном случае DS отличное для этого решение, в добавок, мы можем вынести номер телефона в настройки данного поля, чтобы в дальнейшем, кто будет управлять сайтом, без проблем мог его поменять. При этом не нужны никакие страницы с настройками, а также не придется хардкодить в темплейтах. Также в любой момент это позволит поменять позицию или вовсе отключить данную информацию.

Листинг файла src/Plugin/DsField/CallusForMore.php

<?php

namespace Drupal\dummy\Plugin\DsField;

use Drupal\Component\Render\FormattableMarkup;
use Drupal\ds\Plugin\DsField\DsFieldBase;
# Необходимо для формы.
use Drupal\Core\Form\FormStateInterface;

/**
 * Поле которое выводит количество слов в содержимом.
 *
 * @DsField(
 *   id = "call_us_for_more",
 *   title = @Translation("DS: Call us for more"),
 *   provider = "dummy",
 *   entity_type = "node",
 *   ui_limit = {"article|full"}
 * )
 */
class CallUsForMore extends DsFieldBase {

  /**
   * {@inheritdoc}
   *
   * Задаем настройки по умолчанию.
   */
  public function defaultConfiguration() {
    $config = [
      'telephone' => '+7 (999) 123-45-67',
    ];
    return $config;
  }

  /**
   * {@inheritdoc}
   * Данный метод должен возвращать форму в соответствии с Form API.
   */
  public function settingsForm($form, FormStateInterface $form_state) {
    # Получаем конфигурацию
    $config = $this->getConfiguration();
    # Название элемента формы должно равняться ключу в конфигах.
    $form['telephone'] = [
      '#type' => 'tel',
      '#title' => 'Номер телефона',
      '#default_value' => $config['telephone'],
      '#required' => TRUE,
    ];
    return $form;
  }

  /**
   * {@inheritdoc}
   *
   * В общем списке полей выводим информацию о номере телефона, чтобы не
   * приходилось загружать форму для проверки. Каждое значение массива будет
   * выводиться с новой строки.
   */
  public function settingsSummary($settings) {
    $config = $this->getConfiguration();
    return ['Номер телефона: ' . $config['telephone']];
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    # Получаем настройки с нашим телефоном.
    $config = $this->getConfiguration();
    return [
      '#type' => 'markup',
      '#markup' => new FormattableMarkup(
        '<strong>Остались вопросы? Позвоните нам:</strong> <a href="tel:@phone">@phone</a>',
        [
          '@phone' => $config['telephone'],
        ]
      )
    ];
  }
}

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

Вывод текущего телефона (по умолчанию взятый из кода)

А если мы нажмём на шестерёнку у данного поля, то у нас откроется наша форма с настройками:

Форма настроек для нашего поля.

А вот так это выводится в самом материале:

Результат поля с динамическим телефоном из настроек.

Вот и всё! Рассмотрел все основные методы для формирования поля. Как по мне, так подход в D8 на голову превосходит создание полей для D7. Он быстрее, проще и понятнее.

DsField_examples.tar.gz








Комментарии

добавить
Комментариев пока нет. Будете первым?
Чтобы комментировать, нужно авторизоваться

Советуем почитать


Почему стоит изучать Ruby on Rails
Администратор 0

Почему стоит изучать Ruby on Rails читать далее

Вы начинающий программист? Или просто думаете какой бы язык изучить? Очень рекомендуем вам обратить внимание на Ruby on Rails. Не смотря на обилие языков программирования и доступных фреймворков, Ruby on Rails очень популярен среди web-разработчиков. Всё благодаря функционалу и скорости разработки.

0 28.01.2018 17:12:42

Федеральная система
Сергей 0

Федеральная система "Город" читать далее

В прошлый раз описал процесс работы с платёжной системой Cyberplat, теперь хочу поделиться опытом работы с ФСГ (Федеральная система город).

Разработано сие чудо ЦФТ. Старались делать все по ГОСТ, поэтому произвести интеграцию не так просто, как хотелось бы (рассматриваем PHP).

0 11.07.2016 17:40:15

PHP Управление строками
Максим 0

PHP Управление строками читать далее

Мало кто из разработчиков задумывается о том, как устроено ядро PHP и что происходит «под капотом». Действительно, на практике большинству редко бывают нужны подобные знания, тем не менее обладать ими будет полезно. Статья рассказывает о том, как устроены строки в PHP и о различиях работы с ними в PHP 5 и 7.


Это мой первый перевод подобной статьи, тем более технически не самой простой. Обо всех неточностях пишите в комментариях или лично мне.

0 04.05.2016 23:28:31