Need some help with your project? Contact me

Creating a custom Views field in Drupal 8

In this article I am going to show you how to create a custom Views field in Drupal 8. At the end of this tutorial, you will be able to add a new field to any node based View which will flag (by displaying a specific message) the nodes of a particular type (configurable in the field configuration). Although I will use nodes, you can use this example to create custom fields for other entities as well.

So let's get started by creating a small module called d8views (which you can also find in this repository):

d8views.info.yml:

name: Drupal 8 Views Demo
description: 'Demo module that illustrates working with the Drupal 8 Views API'
type: module
core: 8.x

In Drupal 7, whenever we want to create a custom field, filter, relationship, etc for Views, we need to implement hook_views_api() and declare the version of Views we are using. That is no longer necessary in Drupal 8. What we do now is create a file called module_name.views.inc in the root of our module and implement the views related hooks there.

To create a custom field for the node entity, we need to implement hook_views_data_alter():

d8views.views.inc:

/**
 * Implements hook_views_data_alter().
 */
function d8views_views_data_alter(array &$data) {
  $data['node']['node_type_flagger'] = array(
    'title' => t('Node type flagger'),
    'field' => array(
      'title' => t('Node type flagger'),
      'help' => t('Flags a specific node type.'),
      'id' => 'node_type_flagger',
    ),
  );
}

In this implementation we extend the node table definition by adding a new field called node_type_flagger. Although there are many more options you can specify here, these will be enough for our purpose. The most important thing to remember is the id key (under field) which marks the id of the views plugin that will be used to handle this field. In Drupal 7 we have instead a handler key in which we specify the class name.

In Drupal 8 we have something called plugins and many things have now been converted to plugins, including views handlers. So let's define ours inside the src/Plugin/views/field folder of our module:

src/Plugin/views/field/NodeTypeFlagger.php

<?php

/**
 * @file
 * Definition of Drupal\d8views\Plugin\views\field\NodeTypeFlagger
 */

namespace Drupal\d8views\Plugin\views\field;

use Drupal\Core\Form\FormStateInterface;
use Drupal\node\Entity\NodeType;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;

/**
 * Field handler to flag the node type.
 *
 * @ingroup views_field_handlers
 *
 * @ViewsField("node_type_flagger")
 */
class NodeTypeFlagger extends FieldPluginBase {

  /**
   * @{inheritdoc}
   */
  public function query() {
    // Leave empty to avoid a query on this field.
  }

  /**
   * Define the available options
   * @return array
   */
  protected function defineOptions() {
    $options = parent::defineOptions();
    $options['node_type'] = array('default' => 'article');

    return $options;
  }

  /**
   * Provide the options form.
   */
  public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    $types = NodeType::loadMultiple();
    $options = [];
    foreach ($types as $key => $type) {
      $options[$key] = $type->label();
    }
    $form['node_type'] = array(
      '#title' => $this->t('Which node type should be flagged?'),
      '#type' => 'select',
      '#default_value' => $this->options['node_type'],
      '#options' => $options,
    );

    parent::buildOptionsForm($form, $form_state);
  }

  /**
   * @{inheritdoc}
   */
  public function render(ResultRow $values) {
    $node = $values->_entity;
    if ($node->bundle() == $this->options['node_type']) {
      return $this->t('Hey, I\'m of the type: @type', array('@type' => $this->options['node_type']));
    }
    else {
      return $this->t('Hey, I\'m something else.');
    }
  }
}

We are defining our NodeTypeFlagger class that extends FieldPluginBase (which is the base plugin abstract class for the views field many plugins extend from). Just above the class declaration we use the @ViewsField annotation to specify the id of this plugin (the same one we declared in the hook_views_data_alter() implementation). We also use the @ingroup annotation to mark that this is a views field handler.

In our example class, we have 4 methods (all overriding the parent class ones).

Query

First, we override the query() method but leave it empty. This is so that views does not try to include this field in the regular node table query (since the field is not backed by a table column).

DefineOptions

The second method is the defineOptions() method through which we specify what configuration options we need for this field. In our case one is enough: we need to specify the node type which we want flagged in the Views results. We set a sensible default as the article node type.

BuildOptionsForm

The third method, buildOptionsForm() is responsible for creating the form for the configuration options we declared earlier. In our case we just have a select list with which we can choose from the existing node types.

Render

Lastly, the render() method which is the most important and which deals with output. We use it to actually render the content of the field for each result. Here is where we perform some business logic based on the currently set node type option and flag with a message whether or not the current result is in fact of the type specified in the configuration.

The $resultRow object is an instance of Drupal\views\ResultRow which contains data returned for the current row by Views and the entity object at the base of the query (in our case the node). Based on this information we can perform our logic.

Keep in mind you can use depedency injection to inject all sorts of services into this class and make use of them in your logic. Additionally, you can override various other methods of the parent class in order to further customize your field.

Conclusion

There you have it. A small custom module that demonstrates how to create a custom Views field (plugin). Relationships, filters, sorters and others work in similar way. I will be covering those in later articles. Stay tuned.

Comments

Hello,

First of all, thank you for all the tutorial you have done on Drupal 8.

I followed the guide but I can't figure out where the field should be added. Could you give me more precision about this.

Thank you !

How to add "Node Type Flagger" field to View

  1. Once all files have been created in custom module based on article, clear site cache
  2. Edit a View, add "Node Type Flagger" to view "fields"
  3. When adding "Node Type Flagger" field, select node type in settings of a node type that will be listing in the view output based on other "FILTER CRITERIA".
  4. Review view output. A new column in the view output will be listed called "NODE TYPE FLAGGER".
  5. Each row will have the "Hey, I'm of the type: " or "Hey, I\'m something else." text listed based on the settings for the field.

Content: Node type flagger

Hi Danny,

I found your article in a search to solve a simple (maybe) issue, not sure if I can achieve what I want with your code, but I believe it.

I want to display or filter based on comment_status field from node__comment table.

Meanwhile, yesterday I open this issue in Drupal,
https://www.drupal.org/node/2624538

( Sorry on my none drupal way of saying things, but my experience on Drupal is less then an year)

Thanks in advance.

Hi Danny,
I am new in drupal 8. I am doing theming in d8.I created a view and also did theming.In that view there is an image filed. I want to to add a class with in .In d7 i rendered views using views pre render.please help me for d8 .

Hi,
thank's for your tutorial. it's helpfull
i have a problem with the render function because i want disply a list of uri.
so i have the html code in the result instead the html list.
thank you

the group property is now mandatory in hook_views_data_alter(), so use fe. Content:

function d8views_views_data_alter(array &amp;$data) {
  $data['node']['node_type_flagger'] = array(
    'title' =&gt; t('Node type flagger'),
    'group' =&gt; t('Content'),
    'field' =&gt; array(
      'title' =&gt; t('Node type flagger'),
      'help' =&gt; t('Flags a specific node type.'),
      'id' =&gt; 'node_type_flagger',
    ),
  );
}

Add new comment

You can post comments in Markdown and basic HTML tags.
For code blocks, wrap your code within '~~~'. For example:
~~~
$var = 'my variable';
~~~