Creating a Drupal style plugin for a view
Motivation
This plugin will be implemented as part of achieving a goal to build a blog functionality with a complex design into a multilingual Drupal 8 website.
Background
If you are someone who is new to term style plugin, it’s better to take a step back and read where and how this exists in Drupal core.
Assuming that you have already known the basics of a View on Drupal 8;
This should be a good place to start if not;
https://www.drupal.org/docs/user_guide/en/views-concept.html
As a site builder you may have seen the /admin/structure/views UI and seen a couple of default views that ships with a fresh installation of Drupal (Vanilla Drupal installation).
[You can try this out online with https://simplytest.me/]
For example; below screenshot of /admin/content page is a result of admin/structure/views/view/content page view
If we take a look at the view configuration on admin/structure/views/view/content you would notice a section called Format.
Let’s see what it has by default, click on the Format hyperlink named Table to get the below screen that shows the default options available by Drupal for view styles.
These are coming from the classes at /core/modules/views/src/Plugin/views/style path.
And you will see the Table.php class which is responsible in this particular scenario.
If you open the /core/modules/views/src/Plugin/views/style/Table.php you will see that the Table class extends StylePluginBase. Take a moment to observe and understand the structure because this is needed for us to create a new style so that it would be displayed in views UI.
The code
To add the style plugin we need to create a new module. And I will call this module my_style
Like any other module in Drupal 8 we need to create
- my_style.info.yml
To let Drupal know about our new module
- my_style.module
To add the theme hook and hook_preprocess_HOOK code
- MyStyle.php
This will be our style plugin, and it will reside in a path similar to how Table.php in core exists.
Which is my_style/src/Plugin/views/style/MyStyle.php
This file will have the annotation and form for the style plugin
In the annotation we will let Drupal know which template file needs to be used.
- my_style_template.html.twig
MyStyle.php
Initial code structure will look like below, we need to inherit the code from StylePluginBase in the similar way how it is in core/modules/views/src/Plugin/views/style/Table.php and change the annotation and other code snippets so that this will be specific just for our plugin.
/**
* @file
* Definition of Drupal\my_style\Plugin\views\style\MyStyle.
*/
namespace Drupal\my_style\Plugin\views\style;
use Drupal\core\form\FormStateInterface;
use Drupal\views\Plugin\views\style\StylePluginBase;
/**
* Style plugin to render a list articles
* in special format.
*
* @ingroup views_style_plugins
*
* @ViewsStyle(
* id = "my_style_plugin",
* title = @Translation("Blog View Style"),
* help = @Translation("Render a list of articles in a special format."),
* theme = "my_style_template",
* display_types = { "normal" }
* )
*
*/
class MyStyle extends StylePluginBase {
/**
* {@inheritdoc}
*/
protected $usesRowPlugin = TRUE;
/**
* Set default options
*/
protected function defineOptions() {
$options = parent::defineOptions();
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
}
public function render() {
return parent::render();
}
}
The code above is enough for Drupal to identify the new plugin. It will work without adding any additional form options.
But in case you want to add extra classes just for this plugin you may add below code after parent::buildOptionsForm($form, $form_state);
// Extra CSS classes.
$form['classes'] = [
'#type' => 'textfield',
'#title' => t('CSS classes'),
'#default_value' => (isset($this->options['classes'])) ? $this->options['classes'] : 'paragraph-type-blog-list paragraph-view-mode-default',
'#description' => t('CSS classes for further customization of blog page.'),
];
The end result will look like below screenshots at the view UI once the module is completed and installed;
The plugin name is captured via the Title field of the annotation.
* title = @Translation(“Blog View Style”),
The form is loaded per the code in buildOptionsForm()
Now that the plugin is done all we need is the .info, .module and the twig template files to finish the job.
my_style.module
As you remember we added the name of the theme file in the annotation as well.
* theme = “my_style_template”,
So now Drupal will try to find a file called my_style_template.html.twig in it’s active theme template folder.
But since we are going to add a template file inside the module folder we need to tell Drupal to check for a file called my_style_template.html.twig under my_style/template folder.
To do so we add a simple code into .module file.
/**
* @file
* Code specific to Blog view /admin/structure/views/view/blog.
*/
/**
* Implements hook_theme().
*/
function my_style_theme($existing, $type, $theme, $path) {
return [
'my_style_template' => [
'variables' => [
'article_data_list' => [],
'max_pages' => '',
'label_prev' => '',
'label_next' => '',
],
'file' => 'my_style.theme.inc',
],
];
}
You may notice the additional key value pairs here which are to show the possibilities you have,
variables key:
May contain values arrays depending on your logic code from .module e.g. hook_preprocess_HOOK code
file key:
Is used to tell Drupal that a different file is used, and it contains specific code for this view. The hook_preprocess_HOOK code I used in the my_style.theme.inc will only execute once, and it will be cached by Drupal making the code execute faster.
Even though I have added some additional key value pairs in the above theme array, simply having the below code is enough for Drupal to find and execute the content in the twig file.
return [
'my_style_template' => [
'variables' => [],
],
];
my_style_template.html.twig
By default, simply having {{rows}} in this file will render the output. But as you can see there is a lot of flexibility here so that you can use your own variables, so this code needs to adjust according to the logic you have included in the module files hook_preprocess_HOOK.