Несомненно, Display Suite является одним из самых популярных модулей в истории предоставленных модулей Drupal. Это позволяет создавать макеты, поля и предоставляет все виды других мощных инструментов, которые мы используем для создания уровня представления наших сайтов на Drupal.
Одной из наиболее мощных функций 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' );
}
Они будут доступны для выбора в пользовательском интерфейсе под заголовком « Field
на странице «Управление отображением» типа контента. И мы сможем увидеть выбор, когда создадим фактическое поле для отображения. Но об этом в секунду.
Сводка конфигурации
Также рекомендуется, чтобы, если мы использовали настройки, определенные в пользовательском интерфейсе, у нас была сводка того, что было выбрано в виде простой строки, которая описывает это. Это печатается под заголовком Widget
страницы управления отображением типа контента.
Для этого нам нужно реализовать метод 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
на странице «Управление отображением» типа содержимого.
/** * {@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.
Кроме того, он может быть настроен на выбор из нескольких словарей, условия которых он затем будет отображать. И, наконец, мы можем даже указать форматер для печати этих терминов, связанных или в виде простого текста.