Урок 4. Плагины
Опубликовано вт, 07/02/2017 - 01:31
Содержание
Введение
Система плагинов имеет три основных элемента[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.
Аннотации, по большому счету, представляют собой структуру данных ключ-значение с поддержкой вложенности. Какие же преимущества использования аннотаций?
- Аннотация находится в том же файле, что и класс плагина. Это позволяет быстро и просто создать новый плагин, скопировав файл текущего.
- Может содержать довольно сложную структуру для предоставления дополнительных метаданных. Кроме того, можно указывать какие строки должны быть переведены.
- Выигрыш по быстродействию. Больше нет необходимости загружать каждый класс в память для получения информации о нем. Текущая реализация, которую использует Друпал, просто парсит аннотацию как текст, не включая файл как PHP файл, тем самым сводя использование памяти к минимуму.
Располагаться кастомные плагины должны по следующему пути от корня сайта
1 |
/modules/custom/MYMODULE/src/Plugin/PLUGIN_TYPE/MYPLUGIN.php |
Создание плагина на примере блока
Для того чтобы создать блок, необходимо выполнить несколько простых шагов:
- создать директорию src/Plugin/Block/ относительно директории вашего модуля.
- наследовать свой класс MyModuleBlock.php от базового класса Drupal\Core\Block\BlockBase.
- указать аннотацию.
- реализовать необходимые методы в собственном классе 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 и убеждаемся что все хорошо
Далее по заданию нужно добавить ссылку на страницу конфигурации, под блоком 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 |
Что здесь важно отметить: помимо заголовка и описания, есть два новых для нас свойства — parent и route_name.
- parent — указывает на имя родительского роута, в данном случае роут, который регистрирует страницу /admin/config/services.
- route_name — имя роута, на который ведет данная ссылка.
Задание по текущему уроку
Создать блок к модулю news. Разместить блок только на главной странице (в любом регионе) и сделать доступным зарегистрированным пользователям с помощью UI (не программно). В блоке выводить строку “This is a news block”.
Дополнительная информация по статье
- https://www.drupal.org/docs/8/api/plugin-api/plugin-api-overview - Plugin API из официальной документации на drupal.org.
- https://ru.wikipedia.org/wiki/Декоратор_(шаблон_проектирования) - паттерн Декоратор на wiki.
- https://www.drupal.org/docs/8/api/plugin-api/why-plugins - сравнение плагинов и хуков из официальной документации.
- http://www.php-fig.org/psr/psr-4/ - стандарт кодирования PSR-4.
- Версии программных продуктов, используемых в статье: Drupal 8.2.6
7 Comments
АК - вт, 07/03/2017 - 13:48
Разместить блок только на
Сразу подумал, что нужно сделать это программно, а не через GUI и немного впал в ступор.
Спасибо за уроки. Подписался на RSS.
Да, и есть небольшое читательское пожелание, по возможности сделать оглавление или хотя бы ссылки на предыдущий/следующий уроки.
nightdevel - вт, 07/03/2017 - 22:53
пожелание дельное, приму к
пожелание дельное, приму к сведению. Текст задания поправил.
Анна - ср, 15/08/2018 - 22:53
Здравствуйте.
Здравствуйте.
Попробовала создать пользовательский модуль строго придерживаясь ваших инструкций: просто создала файлы и папки и скопировала содержимое из ваших примеров.
Вот структура моих файлов:
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 - вс, 19/08/2018 - 15:44
Добрый день, к сожалению
Добрый день, к сожалению дистанционно сложно понять, где у вас ошибка. Попробуйте следующие шаги:
Если после этого фатал все равно есть - убирайте инициализацию блока.
Если решить проблему все равно не удается, то можете залить ваш модуль в какой либо файлообменник, либо лучше на гитхаб и скинуть мне ссылку через контакты.
Дмитрий - пн, 11/02/2019 - 11:20
Из примера не понятно, что
Из примера не понятно, что файл в директории /modules/custom/news/src/Controller необходимо назвать NewsSettingsController.php я его назвал по другому и очень долго искал ошибку.
Бот - пт, 12/07/2019 - 00:06
Здравствуйте! При переходе по
Здравствуйте! При переходе по "/admin/config/services/news" выдает: You are not authorized to access this page.
Чистка кэша, куки не помогает
Gotho Rioss - вт, 03/08/2021 - 14:17
ошибка в описании метода
ошибка в описании метода build должен возвращаться массив