Статьи

Пользовательские поля набора отображения в Drupal 8

Несомненно, Display Suite является одним из самых популярных модулей в истории предоставленных модулей Drupal. Это позволяет создавать макеты, поля и предоставляет все виды других мощных инструментов, которые мы используем для создания уровня представления наших сайтов на Drupal.

Drupal 8 logo

Одной из наиболее мощных функций Display Suite (DS) является возможность создавать настраиваемые поля, которые могут отображаться в макетах DS вместе с фактическими значениями основного поля. В Drupal 7 это был очень популярный способ построения макетов и отображения динамических данных, которые не связаны строго с выводом какого-либо поля Field API на узле (или другом объекте).

Display Suite был портирован и поддерживается для Drupal 8. В зависимости от другого добавленного модуля под названием Layout Plugin , версия D8 предлагает многое из того, что мы имеем в Drupal 7 и, возможно, даже больше.

В этой статье мы рассмотрим, как мы можем создать собственное поле Display Suite в Drupal 8, используя новую архитектуру ООП и систему плагинов. Чтобы продемонстрировать это, мы собираемся создать поле DS, доступное только на узлах Article, которое можно использовать для отображения списка терминов таксономии из определенного словаря. И мы собираемся сделать так, чтобы последний мог быть настроен из пользовательского интерфейса, а именно администраторы смогут указать, какие термины словаря должны быть перечислены. Не очень много полезности в этом примере, я знаю, но он позволит вам понять, как все работает.

Если вы следуете, код, который мы пишем, доступен в этом репозитории внутри Demo модуля. Так что не стесняйтесь проверить это.

Drupal 8 плагинов

Большая часть функциональности, которая раньше объявлялась с помощью _info в Drupal 7, теперь объявляется с использованием плагинов в Drupal 8. Для получения дополнительной информации об использовании плагинов и создании собственных типов плагинов обязательно ознакомьтесь с предыдущей статьей Sitepoint, в которой говорится о только то.

Display Suite также использует новую систему плагинов, чтобы позволить другим модулям определять поля DS. Он предоставляет DsField плагина DsField который позволяет нам писать и поддерживать всю необходимую логику для такого поля внутри одного класса плагина (+ любые сервисы, которые мы можем внедрить в него). Таким образом, мы больше не реализуем hook_ds_field_info () и возвращаем массив информации о поле для каждого типа сущности, но создаем класс плагина с данными прямо в своей аннотации и соответствующей логикой внутри своих методов.

VocabularyTerms class

Давайте начнем с создания нашего класса плагина с именем VocabularyTerms внутри папки src/plugins/DsField нашего пользовательского модуля и аннотирования его для наших целей:

 namespace   Drupal \demo\Plugin\DsField ; 

 use   Drupal \ds\Plugin\DsField\DsFieldBase ; 

 /** * Plugin that renders the terms from a chosen taxonomy vocabulary. * * @DsField( * id = "vocabulary_terms", * title = @Translation("Vocabulary Terms"), * entity_type = "node", * provider = "demo", * ui_limit = {"article|*"} * ) */ 
 class   VocabularyTerms   extends   DsFieldBase   { 
 } 

Один только этот класс будет содержать всю нашу логику для нашего очень простого плагина DsField. Но вот пара замечаний о том, что мы имеем до сих пор:

  • Аннотация не требует пояснений: она предоставляет метаинформацию о плагине.
  • Класс расширяет DsFieldBase который обеспечивает базовую функциональность для всех плагинов этого типа.
  • На момент написания аннотация ui_limit была только что зафиксирована в HEAD, поэтому она может быть недоступна в используемой вами версии. Ограничение доступности поля для типов контента и режимов просмотра может быть сделано путем переопределения isAllowed() базового класса и выполнения там логики.

Конфигурация по умолчанию

Мы хотим, чтобы наше поле было настраиваемым: возможность выбирать из списка существующих словарей. Итак, давайте начнем с предоставления некоторых значений по умолчанию для этой конфигурации, так что, если пользователь ничего не выбирает, будет использоваться словарь Tags который поставляется с core. Для этого нам нужно реализовать метод defaultConfiguration() :

 /** * {@inheritdoc} */ 
 public   function  defaultConfiguration ()   { $configuration =  array ( 
     'vocabulary'   =>   'tags' , 
   ); 

   return  $configuration ; 
 } 

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

Форматтеры

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

 /** * {@inheritdoc} */ 
 public   function  formatters ()   { 
   return  array ( 'linked'   =>   'Linked' ,   'unlinked'   =>   'Unlinked' ); 
 } 

DS Field Formatter Options

Они будут доступны для выбора в пользовательском интерфейсе под заголовком « Field на странице «Управление отображением» типа контента. И мы сможем увидеть выбор, когда создадим фактическое поле для отображения. Но об этом в секунду.

Сводка конфигурации

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

DS Configuration Summary

Для этого нам нужно реализовать метод settingsSummary() и вернуть указанный текст:

 /** * {@inheritdoc} */ 
 public   function  settingsSummary ( $settings )   { $config =  $this -> getConfiguration (); $no_selection =  array ( 'No vocabulary selected.' ); 

   if   ( isset ( $config [ 'vocabulary' ])   &&  $config [ 'vocabulary' ])   { $vocabulary =   Vocabulary :: load ( $config [ 'vocabulary' ]); 
     return  $vocabulary ?  array ( 'Vocabulary: '   .  $vocabulary -> label ())   :  $no_selection ; 
   } 

   return  $no_selection ; 
 } 

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

Поскольку мы Vocabulary класс Vocabulary , нам также нужно использовать его вверху:

 use   Drupal \taxonomy\Entity\Vocabulary ; 

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

Форма настроек

Теперь, когда мы показываем, какая конфигурация была выбрана из пользовательского интерфейса, пришло время предоставить фактическую форму, которая позволит пользователю сделать это. Это станет доступным, если щелкнуть шестерню под заголовком « Operations на странице «Управление отображением» типа содержимого.

DS Field Settings Form

 /** * {@inheritdoc} */ 
 public   function  settingsForm ( $form ,   FormStateInterface  $form_state )   { $config =  $this -> getConfiguration (); $names =  taxonomy_vocabulary_get_names (); $vocabularies =   Vocabulary :: loadMultiple ( $names ); $options =  array (); 
   foreach   ( $vocabularies as  $vocabulary )   { $options [ $vocabulary -> id ()]   =  $vocabulary -> label (); 
   } $settings [ 'vocabulary' ]   =  array ( 
     '#type'   =>   'select' , 
     '#title'   =>  t ( 'Vocabulary' ), 
     '#default_value'   =>  $config [ 'vocabulary' ], 
     '#options'   =>  $options , 
   ); 

   return  $settings ; 
 } 

Как и прежде, нам нужно реализовать метод для этого. И внутри мы загружаем все имена словаря таксономии и подготавливаем массив опций для использования со списком выбора Form API. Последний является единственным элементом, который нам нужен для этой формы.

Рендеринг поля

Последнее, что осталось сделать, — это реализовать метод build() отвечающий за рендеринг содержимого нашего поля:

 /** * {@inheritdoc} */ 
 public   function  build ()   { $config =  $this -> getConfiguration (); 
   if   (! isset ( $config [ 'vocabulary' ])   ||   ! $config [ 'vocabulary' ])   { 
     return ; 
   } $query =  \Drupal :: entityQuery ( 'taxonomy_term' ) 
     -> condition ( 'vid' ,  $config [ 'vocabulary' ]); $tids =  $query -> execute (); 
   if   (! $tids )   { 
     return ; 
   } $terms =   Term :: loadMultiple ( $tids ); 
   if   (! $terms )   { 
     return ; 
    } 

   return  array ( 
     '#theme'   =>   'item_list' , 
     '#items'   =>  $this -> buildTermList ( $terms ), 
   ); 
 } 

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

Хотя нам здесь это и не нужно, в большинстве случаев вам потребуется доступ к сущности узла, которая в данный момент отображается. Это доступно внутри массива конфигурации под ключом entity . Более того, под ключом build вас есть фактический рендер-массив строящегося узла. Так что имейте это в виду и проверяйте другие элементы массива конфигурации самостоятельно для получения дополнительной информации.

Я хотел бы упомянуть еще несколько вещей, прежде чем мы рассмотрим фактический buildTermList() . Во-первых, для краткости, мы использовали сервис EntityQuery статически. В вашем проекте вы должны добавить его. Во-вторых, мы использовали класс Term статически для загрузки терминов таксономии. Опять же, вы должны ввести его в хранилище и использовать его для этой цели. И наконец, мы должны импортировать класс Term вверху с use :

 use   Drupal \taxonomy\Entity\Term ; 

Теперь, когда это ясно, давайте посмотрим на наш собственный buildTermList() :

 private   function  buildTermList ( array $terms )   { $config =  $this -> getConfiguration (); $formatter =  isset ( $config [ 'field' ][ 'formatter' ])   &&  $config [ 'field' ][ 'formatter' ]   ?  $config [ 'field' ][ 'formatter' ]   :   'unlinked' ; $items =  array (); 
   foreach   ( $terms as  $term )   { $items []   =  $this -> buildTermListItem ( $term ,  $formatter ); 
   } 

   return  $items ; 
 } 

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

 private   function  buildTermListItem ( Term  $term ,  $formatter )   { 
   if   ( $formatter ===   'linked' )   { $link_url =   Url :: fromRoute ( 'entity.taxonomy_term.canonical' ,  array ( 'taxonomy_term'   =>  $term -> id ())); 
     return  \Drupal :: l ( $term -> label (),  $link_url ); 
   } 

   return   SafeMarkup :: checkPlain ( $term -> label ()); 
 } 

Наконец, в buildTermListItem() мы либо возвращаем очищенный заголовок термина, либо ссылку на него в зависимости от средства форматирования.

Снова мы видим классы, которые должны быть введены, но использовались статически для экономии места. С риском походить на испорченную запись, имейте в виду, что вы должны ввести их. На данный момент мы должны использовать их сверху:

 use   Drupal \Core\Url ; 
 use   Drupal \Component\Utility\SafeMarkup ; 

Вывод

И вот у нас это есть, наш собственный плагин DsField в Drupal 8. Очистка кешей теперь сделает это поле доступным во всех режимах просмотра типа контента Article.

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