Урок 4. Плагины

Друпал 8 урок 4 Плагины, создание кастомного блока

Содержание

Введение

Система плагинов имеет три основных элемента[1]:

  • Plugin Types — центральный контролирующий класс, который определяет как плагины этого типа будут определяться (идентифицироваться) и инстанцироваться.
  • Plugin Discovery — отвечает за процесс нахождения плагина.
  • Plugin Factory — отвечает за инстанцирование определенных плагинов, выбранных для текущего случая.

Кроме того, система плагинов включает в себя несколько ситуационно полезных компонентов:

  • Plugin Derivatives — позволяет одному плагину действовать вместо многих. Это полезно в случаях, когда ввод пользовательских данных может повлиять на доступные плагины. Например, блоки создаются и размещаются на странице с помощью плагина. Администратор сайта создает новый блок, и этот блок также должен быть доступен без определения нового плагина.
  • Discovery Decorators — это класс, который создает "обертку" для другого механизма обнаружения, чтобы обеспечить дополнительную функциональность. Использует паттерн Декоратор.

    Декоратор (англ. Decorator) — структурный шаблон проектирования, предназначенный для динамического подключения дополнительного поведения к объекту. Шаблон Декоратор предоставляет гибкую альтернативу практике создания подклассов с целью расширения функциональности. Декоратор предусматривает расширение функциональности объекта без определения подклассов[2].

  • Plugin Mappers — позволяют замапить что-либо (в большинстве случаев строку) с определенным плагин инстансом. Типы плагинов, которые используют данный подход могут возвращать полностью настроенные и инстанцированные плагины, основанные на произвольно определяемых именах, вместо того, чтобы требовать от разработчиков вручную настраивать инстанс плагина.

В Drupal 8 плагины являются объектно-ориентированной заменой для инфо хуков. В то же время плагины предоставляют более надежный механизм работы, нежели хуки. С плагинами можно заменить класс для конкретного плагина и выполнить совершенно другую логику как для ядра, так и для контрибных модулей, что несомненно очень полезно[3].
Большинство плагинов в Drupal 8 регистрируются в PHP файлах с помощью аннотаций, используя стандарт кодирования PSR-4[4]. Ниже приведены некоторые типы плагинов, которые предоставляются ядром:

  • Блоки
  • Филд форматтеры, филд виджеты
  • Все views плагины
  • Условия
  • Плагины миграции - source, process и destination.

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

  1. Аннотация находится в том же файле, что и класс плагина. Это позволяет быстро и просто создать новый плагин, скопировав файл текущего.
  2. Может содержать довольно сложную структуру для предоставления дополнительных метаданных. Кроме того, можно указывать какие строки должны быть переведены.
  3. Выигрыш по быстродействию. Больше нет необходимости загружать каждый класс в память для получения информации о нем. Текущая реализация, которую использует Друпал, просто парсит аннотацию как текст, не включая файл как PHP файл, тем самым сводя использование памяти к минимуму.

Располагаться кастомные плагины должны по следующему пути от корня сайта

1
/modules/custom/MYMODULE/src/Plugin/PLUGIN_TYPE/MYPLUGIN.php

Создание плагина на примере блока

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

  1. создать директорию src/Plugin/Block/ относительно директории вашего модуля.
  2. наследовать свой класс MyModuleBlock.php от базового класса Drupal\Core\Block\BlockBase.
  3. указать аннотацию.
  4. реализовать необходимые методы в собственном классе MyModuleBlock.php, которые требует интерфейс Drupal\Core\Block\BlockPluginInterface.

Пример класса MyModuleBlock.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
namespace Drupal\MYMODULE\Plugin\Block;
use Drupal\Core\Block\BlockBase;
 
/**
* Class MyModuleBlock.
*
* @Block(
*   id = "myblock",
*   admin_label = @Translation("My new Block")
* )
*/
class MyModuleBlock extends BlockBase {
 /**
  * {@inheritdoc}
  */
 public function build() {
   return 'My new block';
 }
}

Далее нужно сбросить кеш и перейти на страницу /admin/structure/block. Если вы на этой странице не нашли своего нового блока - это не повод расстраиваться:) Дело в том, что новые блоки не появляются в регионе Disabled (как это было в семерке). Они просто не показаны. Для того, чтобы добавить блок необходимо выбрать нужный регион и нажать кнопку Place block. Если вы не нашли свой блок во всплывающем окошке, то самое время перепроверить корректность написания плагина.

Домашнее задание

Ответ по прошлому заданию

Как и было обещано, в уроке по новой теме будут приведены ответы на домашнее задание из прошлой. Итак, для добавления ссылки нам необходимо объявить свой роутинг. Добавляем в папку модуля news файл news.routing.yml. С помощью свойства _role указываем какая роль должна иметь доступ к этой странице (по условию задачи - администраторы).

1
2
3
4
5
6
7
news.news_path_settings:
 path: '/admin/config/services/news'
 defaults:
   _controller: '\Drupal\news\Controller\NewsSettingsController::getSettingsPage'
   _title: 'News'
 requirements:
   _role: 'administrator'

Далее создаем папки src/Controller и располагаем их в директории модуля news, т.е. должен получиться вот такой путь — /modules/custom/news/src/Controller. Для построения формы существуют специальные классы FormBase и ConfigFormBase, но их мы рассмотрим в соответствующем уроке, посвященному Form API. А пока напишем свой очень простой контроллер NewsSettingsController.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
namespace Drupal\news\Controller;
 
/**
* Class NewsSettingsController.
*
* @package Drupal\news\Controller
*/
class NewsSettingsController {
 /**
  * Returns settings page.
  */
 public function getSettingsPage() {
   return [];
 }
}

Чистим кеш, переходим по адресу /admin/config/services/news и убеждаемся что все хорошо
news страница
Далее по заданию нужно добавить ссылку на страницу конфигурации, под блоком Web Services. Для этого нам необходимо создать другой *.yml файлик, который будет отвечать за ссылку — news.links.menu.yml. Его содержимое

1
2
3
4
5
news.news_menu_settings:
 title: 'News'
 parent: system.admin_config_services
 description: 'Configure the news settings, choose news vendors.'
 route_name: news.news_path_settings

news ссылка на странице конфигурации
Что здесь важно отметить: помимо заголовка и описания, есть два новых для нас свойства — parent и route_name.

  • parent — указывает на имя родительского роута, в данном случае роут, который регистрирует страницу /admin/config/services.
  • route_name — имя роута, на который ведет данная ссылка.

Задание по текущему уроку

Создать блок к модулю news. Разместить блок только на главной странице (в любом регионе) и сделать доступным зарегистрированным пользователям с помощью UI (не программно). В блоке выводить строку “This is a news block”.

Дополнительная информация по статье

  1. https://www.drupal.org/docs/8/api/plugin-api/plugin-api-overview - Plugin API из официальной документации на drupal.org.
  2. https://ru.wikipedia.org/wiki/Декоратор_(шаблон_проектирования) - паттерн Декоратор на wiki.
  3. https://www.drupal.org/docs/8/api/plugin-api/why-plugins - сравнение плагинов и хуков из официальной документации.
  4. http://www.php-fig.org/psr/psr-4/ - стандарт кодирования PSR-4.
  5. Версии программных продуктов, используемых в статье: Drupal 8.2.6

7 Comments

Аватар пользователя АК

Разместить блок только на

Разместить блок только на главной странице (в любом регионе) и сделать доступным зарегистрированным пользователям.

Сразу подумал, что нужно сделать это программно, а не через GUI и немного впал в ступор.

Спасибо за уроки. Подписался на RSS.

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

Аватар пользователя Анна

Здравствуйте.

Здравствуйте.
Попробовала создать пользовательский модуль строго придерживаясь ваших инструкций: просто создала файлы и папки и скопировала содержимое из ваших примеров.

Вот структура моих файлов:

web\modules\custom\news:
--news.routing.yml
--news.links.menu.yml
--news.info.yml

web\modules\custom\news\src\Plugin\Block\newsBlock.php
web\modules\custom\news\src\Controller\NewsSettingsController.php

В браузере: "На сайте произошла непредвиденная ошибка. Пожалуйста, повторите попытку позже."

В консоли редактора получаю ошибку: syntax error, unexpected 'class' (T_CLASS) и подчёркивание следующих строк:
class NewsSettingsController в файле NewsSettingsController.php
class News extends BlockBase в файле newsBlock.php

При обновлении (drush cr):
PHP Fatal error: Uncaught Error: Unsupported operand types in C:\xampp\htdocs\mysite\web\core\lib\Drupal\Core\Routing\RouteBuilder.php:163
и далее о-очень много текста.

В файле error.log (Apache):
[Wed Aug 15 22:21:23.267295 2018] [php7:notice] [pid 7324:tid 1960] [client ::1:50982] Uncaught PHP Exception Doctrine\\Common\\Annotations\\AnnotationException: "[Syntax Error] Expected PlainValue, got '\xc2' at position 11 in class Drupal\\news\\Plugin\\Block\\newsBlock." at C:\\xampp\\htdocs\\mysite\\vendor\\doctrine\\annotations\\lib\\Doctrine\\Common\\Annotations\\AnnotationException.php line 42, referer: http://localhost/mysite/web/admin/config/services

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

Аватар пользователя nightdevel

Добрый день, к сожалению

Добрый день, к сожалению дистанционно сложно понять, где у вас ошибка. Попробуйте следующие шаги:

  1. необходимо добиться момента на котором все работает (не фаталит). Важно понять на каком этапе произошла ошибка.
  2. удалите контроллер из папки Controller, закомментируйте подключение в файле services.yml
  3. скиньте кеш

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

Аватар пользователя Дмитрий

Из примера не понятно, что

Из примера не понятно, что файл в директории /modules/custom/news/src/Controller необходимо назвать NewsSettingsController.php я его назвал по другому и очень долго искал ошибку.