Extend Views with plugins
Where handlers act on one column, plugins control the machinery of a view (a configuration-defined query plus display, built in the admin UI): the style lays out the whole result set, the row renders one result, the pager pages, access guards the display. This guide writes a style plugin that renders a field as a comma-separated list.
Add a new layout or behavior to the Views UI that any view can select. Reach for a style plugin when the built-in table, grid, and list layouts do not fit; for one-off markup, a Twig template override of an existing style is lighter.
Prerequisites
Section titled “Prerequisites”- A custom module, per Scaffold a module.
- The core Views module (installed by the standard profile).
- Plugins for the discovery system.
Write the style plugin
Section titled “Write the style plugin”A style plugin extends StylePluginBase. Put it in src/Plugin/views/style/ and declare it with the #[ViewsStyle] attribute. Two protected properties decide its behavior: $usesRowPlugin (whether each row is rendered by a separate row plugin) and $usesFields (whether it works with the view’s fields).
<?php
declare(strict_types=1);
namespace Drupal\howto_views\Plugin\views\style;
use Drupal\Core\StringTranslation\TranslatableMarkup;use Drupal\views\Attribute\ViewsStyle;use Drupal\views\Plugin\views\style\StylePluginBase;
/** * Renders a view's first field as a comma-separated list. * * A style plugin controls how the whole result set is laid out (a table, * a grid, a list). This one joins one field across all rows into a single * line, instead of one block per row. */#[ViewsStyle( id: 'howto_comma_list', title: new TranslatableMarkup('Comma-separated list'), help: new TranslatableMarkup('Renders the first field of each row, joined by commas.'), display_types: ['normal'],)]final class CommaList extends StylePluginBase {
/** * This style renders fields directly, without a per-row plugin. * * @var bool */ protected $usesRowPlugin = FALSE;
/** * This style operates on fields. * * @var bool */ protected $usesFields = TRUE;
/** * {@inheritdoc} */ public function render(): array { $this->renderFields($this->view->result); $field_id = array_key_first($this->view->field);
$items = []; foreach (array_keys($this->view->result) as $index) { $items[] = strip_tags((string) $this->getField($index, $field_id)); }
return ['#markup' => implode(', ', $items)]; }
}Because this style sets $usesRowPlugin = FALSE, it renders fields itself: renderFields() prepares them, and getField() returns each rendered value. A style that wraps rows in markup instead sets theme: on the attribute and ships a Twig template.
Verify
Section titled “Verify”The example module ships a view that uses the style. After installing it:
ddev drush pm:install howto_viewsddev drush php:eval " \$v = \Drupal\views\Views::getView('howto_comma_titles'); print \Drupal::service('renderer')->renderInIsolation(\$v->preview());"prints the node titles joined by commas. The module’s kernel test renders the view and asserts the comma-joined output.
Variations
Section titled “Variations”Other plugin families
Section titled “Other plugin families”Each Views plugin family has its own base class, directory, and attribute:
| Plugin | Base class | Directory | Attribute |
|---|---|---|---|
| Style | StylePluginBase | src/Plugin/views/style | ViewsStyle |
| Row | RowPluginBase | src/Plugin/views/row | ViewsRow |
| Pager | PagerPluginBase | src/Plugin/views/pager | ViewsPager |
| Access | AccessPluginBase | src/Plugin/views/access | ViewsAccess |
| Argument default | ArgumentDefaultPluginBase | src/Plugin/views/argument_default | ViewsArgumentDefault |
An access plugin returns whether the current user may see a display; an argument-default plugin supplies a contextual filter value (the current user, a node from the route) when none is in the URL.
Related
Section titled “Related”- Extend Views with handlers for field, filter, sort, and argument handlers.
- Plugins for the discovery system.
- The
Views plugins topicon api.drupal.org.
Was this page helpful?
Drupal is a registered trademark of Dries Buytaert.