Статьи

Drupal 8 Сторонние настройки и псевдополя

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

Drupal 8 logo

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

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

Наш первый плагин

Внутри папки src/Form нашего модуля создайте класс с именем BasicForm.php (или как вы хотите его называть). Внутри у нас может быть это простое определение формы:

 <? php namespace   Drupal \reusable_forms\Form ; 

 use   Drupal \Core\Form\FormStateInterface ; 

 /** * Defines the BasicForm class. */ 
 class   BasicForm   extends   ReusableFormBase   { 

   /** * {@inheritdoc}. */ 
   public   function  getFormId ()   { 
     return   'basic_form' ; 
   } 

   /** * {@inheritdoc}. */ 
   public   function  buildForm ( array $form ,   FormStateInterface  $form_state )   { $form =  parent :: buildForm ( $form ,  $form_state ); 

     return  $form ; 
   } 

   /** * {@inheritdoc} */ 
   public   function  submitForm ( array & $form ,   FormStateInterface  $form_state )   { 
     // Handle form submission. 
   } 
 } 

Этот класс формы расширяет нашу базу форм и реализует все необходимые методы. Для получения дополнительной информации о формах и о том, как они работают в Drupal 8 , вы можете проверить одну из моих предыдущих статей на Sitepoint.com. Но, как вы можете видеть, мы вызываем родительский buildForm() чтобы логика, определенная нами в базовом классе, buildForm() место. Остальное — это детали реализации (а здесь мы мало что делаем). Но вы можете выполнять любую логику, какую захотите, и обрабатывать заявки любым удобным для вас способом.

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

Далее давайте создадим наш первый плагин. Внутри папки src/Plugin/ReusableForm нашего модуля у нас может быть файл BasicForm.php со следующим:

 <? php namespace   Drupal \reusable_forms\Plugin\ReusableForm ; 

 use   Drupal \reusable_forms\ReusableFormPluginBase ; 

 /** * Provides a basic form. * * @ReusableForm( * id = "basic_form", * name = @Translation("Basic Form"), * form = "Drupal\reusable_forms\Form\BasicForm" * ) */ 
 class   BasicForm   extends   ReusableFormPluginBase   {} 

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

Вот и все. Это все, что нужно для определения новых плагинов ReusableForm . Мы создаем класс формы со всеми необходимыми полями и логикой, а затем ссылаемся на него внутри простого класса плагина.

Конфигурация типа узла

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

Внутри файла reusable_forms.services.yml нашего модуля мы можем иметь это:

 services : plugin . manager . reusable_forms : 
         class :   Drupal \reusable_forms\ReusableFormsManager parent :  default_plugin_manager 

Теперь мы сможем получить доступ к менеджеру плагинов с помощью plugin.manager.reusable_forms сервиса plugin.manager.reusable_forms . И используя parent ключ в определении, мы указали, что все его зависимости могут быть найдены от родительского. Это классно!

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

 /** * Implements hook_form_BASE_FORM_ID_alter(). */ 
 function  reusable_forms_form_node_type_form_alter (& $form ,   FormStateInterface  $form_state )   { $form [ 'reusable_forms' ]   =  array ( 
     '#type'   =>   'details' , 
     '#title'   =>  t ( 'Reusable forms' ), 
     '#group'   =>   'additional_settings' , 
   ); 

   // Load the current node type configuration entity. $node_type =  $form_state -> getFormObject ()-> getEntity (); $form [ 'reusable_forms' ][ 'reusable_forms_enabled' ]   =  array ( 
     '#type'   =>   'checkbox' , 
     '#title'   =>  t ( 'Enable reusable forms' ), 
     '#description'   =>  t ( 'Check this box if you would like a reusable form on this node type.' ), 
     '#default_value'   =>  $node_type -> getThirdPartySetting ( 'reusable_forms' ,   'enabled' ,   0 ), 
   ); $form_plugins =  \Drupal :: service ( 'plugin.manager.reusable_forms' )-> getDefinitions (); $options =   []; 
   foreach   ( $form_plugins as  $name =>  $plugin )   { $options [ $name ]   =  $plugin [ 'name' ]; 
   } $form [ 'reusable_forms' ][ 'reusable_forms_enabled' ]   =  array ( 
     '#type'   =>   'radios' , 
     '#title'   =>  t ( 'Available forms' ), 
     '#default_value'   =>  $node_type -> getThirdPartySetting ( 'reusable_forms' ,   'plugin' ,   'basic_form' ), 
     '#options'   =>  $options , 
     '#description'   =>  t ( 'The available forms you can choose from for this node type.' ), 
     '#states'   =>  array ( 
       'visible'   =>  array ( 
         ':input[name="reusable_forms_enabled"]'   =>  array ( 'checked'   =>  TRUE ), 
       ), 
     ), 
   ); $form [ '#entity_builders' ][]   =   'reusable_forms_form_node_type_form_builder' ; 
 } 

Реализация hook_form_BASE_FORM_ID_alter подойдет для этого. Хотя мы не должны забывать использовать класс FormStateInterface вверху:

 use   Drupal \Core\Form\FormStateInterface ; 

Так что здесь происходит? Мы создаем новый набор полей для группировки двух полей формы, соответствующих нашей цели: флажок для включения повторно используемых форм и список выбора для выбора из существующих плагинов. В качестве опции для последнего мы создаем массив всех имен плагинов после того, как мы загружаем наш менеджер плагинов и запрашиваем у него все доступные определения. И используя магию #states мы гарантируем, что это последнее поле будет видимым, только если установлен флажок для включения форм.

В конце мы добавляем дополнительную функцию обратного вызова в группу #entity_builders которая будет срабатывать при сохранении объекта и предназначена для сопоставления значений с объектом. Итак, давайте посмотрим эту функцию сейчас:

 /** * Entity form builder for the node type form to map some values to third party * settings */ 
 function  reusable_forms_form_node_type_form_builder ( $entity_type ,   NodeTypeInterface  $type ,   & $form ,   FormStateInterface  $form_state )   { 
   if   ( $form_state -> getValue ( 'reusable_forms_enabled' )   ===   1 )   { $type -> setThirdPartySetting ( 'reusable_forms' ,   'enabled' ,   1 ); $type -> setThirdPartySetting ( 'reusable_forms' ,   'plugin' ,  $form_state -> getValue ( 'reusable_forms_enabled' )); 
     return ; 
   } $type -> unsetThirdPartySetting ( 'reusable_forms' ,   'enabled' ); $type -> unsetThirdPartySetting ( 'reusable_forms' ,   'plugin' ); 
 } 

И снова давайте воспользуемся NodeTypeInterface вверху:

 use   Drupal \node\NodeTypeInterface ; 

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

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

Схема конфигурации

Прежде чем перейти к фактическому отображению форм, нам также необходимо добавить определение схемы для новой конфигурации, которую мы храним. Внутри файла config/schema/reusable_forms.schema.yml нашего модуля мы можем добавить следующее:

 node . type .*. third_party . reusable_forms : type :  mapping label :   'Reusable Forms' mapping : reusable_forms_enabled : type :   boolean label :   'Whether to enable the reusable forms on this node type' reusable_forms_enabled : type :  sequence label :   'Available forms' sequence : type :   string label :   'Plugin name'       

Вид узла

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

Первое, что мы собираемся сделать, — это определить псевдополе сущности контента, которое будет настраиваться из узла Управление интерфейсом отображения . Еще внутри нашего .module файла мы можем иметь это:

 /** * Implements hook_entity_extra_field_info(). */ 
 function  reusable_forms_entity_extra_field_info ()   { $extra =  array (); $bundles =   NodeType :: loadMultiple (); $bundles =  array_filter ( $bundles ,   function ( $bundle ){ 
     return  $bundle -> getThirdPartySetting ( 'reusable_forms' ,   'enabled' )   ===   1 ; 
   }); 

   foreach   ( $bundles as  $bundle )   { $extra [ 'node' ][ $bundle -> Id ()][ 'display' ][ 'reusable_form' ]   =  array ( 
       'label'   =>  t ( 'Reusable form' ), 
       'description'   =>  t ( 'Reusable form' ), 
       'weight'   =>   100 , 
       'visible'   =>  TRUE , 
     ); 
   } 

   return  $extra ; 
 } 

И снова мы должны использовать NodeType вверху:

 use   Drupal \node\Entity\NodeType ; 

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

Очистив кеш, вы сможете увидеть новое псевдополе на странице конфигурации — хотя пока ничего не будет. Что подводит нас к последнему фрагменту головоломки, предоставляя соответствующую форму.

Чтобы сделать псевдополе, которое мы только что определили, полезным, нам нужно реализовать hook_entity_view (или его вариант) и отобразить его содержимое:

 /** * Implements hook_ENTITY_TYPE_view(). */ 
 function  reusable_forms_node_view ( array & $build ,   EntityInterface  $entity ,   EntityViewDisplayInterface  $display ,  $view_mode ,  $langcode )   { 
   if   ( $display -> getComponent ( 'reusable_form' ))   { $plugin_manager =  \Drupal :: service ( 'plugin.manager.reusable_forms' ); $node_type =   NodeType :: load ( $entity -> bundle ()); $plugin =  $node_type -> getThirdPartySetting ( 'reusable_forms' ,   'plugin' ); 
     if   (! $plugin )   { 
       return ; 
     } $build [ 'reusable_form' ]   =  $plugin_manager -> createInstance ( $plugin )-> buildForm ( $entity ); 
   } 
 } 

И давайте не будем забывать операторы использования вверху:

 use   Drupal \Core\Entity\Display\EntityViewDisplayInterface ; 
 use   Drupal \Core\Entity\EntityInterface ; 

Сначала мы проверяем, существует ли компонент reusable_form на дисплее этого узла (отображается ли он в пользовательском интерфейсе). Если это так, мы добавляем ключ reusable_form в массив визуализации, который строит это представление узла.

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

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

Вывод

Как мы видели в этой серии статей, Drupal 8 предоставляет нам инструменты для некоторых полезных вещей. С помощью системы плагинов мы создали основу для нашей повторно используемой функциональности. И затем мы подключились к различным точкам ядра Drupal, чтобы использовать эту логику. Мы видели, как любой объект конфигурации, который реализует ThirdPartySettingInterface может быть расширен для включения новых данных. И наконец, мы отображали релевантные данные при просмотре сущностей контента с помощью псевдополей.

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