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'),
'group' => t('Content'),
'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 = $this->getEntity($values);
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.
Daniel Sipos
Danny founded WEBOMELETTE in 2012 as a passion project, mostly writing about Drupal problems he faced day to day, as well as about new technologies and things that he thought other developers would find useful. Now he now manages a team of developers and designers, delivering quality products that make businesses successful.
Comments
Where do we find the field
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 !
In reply to Where do we find the field by Luc (not verified)
How to add "Node Type Flagger" field to View
How to add "Node Type Flagger" field to View
Content: Node type flagger
In reply to How to add "Node Type Flagger" field to View by DeeZone (not verified)
I got the field in View but its not adding
I got the field in View but its not getting added, after i check the field in views and click add and configure fields nothing happens. The field adding page doesn't opens.
In reply to I got the field in View but its not adding by Pratt (not verified)
Figured it out
In case anyone else hits this, it happens if you put the code in a differently named module and forget to change the namespace. This results in an ajax error which causes the settings form to not show.
comment_status field
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.
views prerender in d8
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 .
html render element
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
add group in hook_views_data_alter()
the group property is now mandatory in hook_views_data_alter(), so use fe. Content:
Use
Use
instead of
to get the current entity because your views field might appear in a relation, in which case $values->_entity is actually the entity the view is for and not the entity your views field is for ...
In reply to Use by GeraldNDA (not verified)
Very useful hint
Very useful hint
In reply to Use by GeraldNDA (not verified)
Thanks! I updated.
Thanks! I updated.
How to call node custom fileds
new to drupal 8, in node i created two custom fields.. dates start and end dates..how can i call date fields values
Great article
it works like a charm. Thanks for writing this post.
What about filter for this field.
How can I filter the view by this custom field? Because usually filter use where statements for filtering, but our custom field don`t store in the table.
Contextual Filter
Hello,
Great information! I could create the custom field however I could not use it in Contextual filter. How could I acheive that?
entity instead of node
Okay so I spend hours trying to figure out how to get this to work for an entity instead of a node.
Then someone pointed out that node is actually just an entity type.
So all you gotta do is replace node with your entity type name.
My entity was drd_domain so I did:
Also make sure to change d8views to your own module name and edit the namespace in 2 locations to your own module's namespace, both at the top of NodeTypeFlagger.php
making your code great again ;-)
For some reason (NodeType API change), I had to change some code
In reply to making your code great again ;-) by Ivan H (not verified)
Hm, i don't think so.
Hm, i don't think so.
The
label()
method is on the EntityInterface so it should still return the title no?Code snippets didn't work for me
Hi
Thanks for the article.
I created the files manually but couldn't get it to work. One problem I believe is the lack of a <?php tag at the start of the .views.inc file. However the repo version worked fine so it might just be me ......
Cheers
Dave
Hi, thanks for this tutorial.
Hi, thanks for this tutorial. I'm trying to add a field on File entities and don't succeed.
I guess the issue resides in the array declaration
$data['file']['myfield']
as if I replacefile
bynode
it works. Do ou have an idea?nospam@briat.org
If your output is based on a dynamic data, don't forget to add cache metadata, eg :
Process description
In the 3rd paragraph - you state "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."
I'm confused... since when do modules have roots? As far as I know, there is no such thing as the root of a module... Maybe you meant the root of a certain directory tree?
In reply to Process description by Thomas (not verified)
Hey. It's a pretty common
Hey. It's a pretty common way of saying the root folder of the module.
D
Print the variable
How can I print the variable within double curly braces?
This is not printing anything:
{{ node_type_flagger }}
Lots of problems
Yes, this description is quite old but searching on the net almost invariably leads here. There is a lot wrong with what's described.
In reply to Lots of problems by Gábor (not verified)
Hello Gábor, I'm sorry but I…
Hello Gábor,
I'm sorry but I fail to see what are all those wrong things. One thing that was no longer up to date was the missing
group
key which was required.Well, if you are adding a field to your own entity type, yes, sure, you can/should use your own Views handler. The example in this article alters an exiting entity type, hence the alter hook. This is the correct procedure in this case. You are probably referring to
hook_views_data()
instead.I fail to see where I mentioned that you need to specify the entity name. In fact, say that we extend the node table definition by adding a new field called node_type_flagger. So yes, we are adding a "field" to the
node
table. If we wanted to extend the Node data table for example, we'd be adding to$data['node_field_data']
, but this was enough for this example.Available at filter criteria
Custom view field working ok but how can I set it available at filter criteria at my view? I think that I need to setup something like
'filter' => array(
// ID of filter handler plugin to use.
'id' => 'numeric',
),
at inc at hook_views_data()
Add new comment