Enhanced Views and Rules in Drupal 7
How to enhance Views and Rules in Drupal by creating custom modules.
Setting up the site
To start, we'll create a new composite field (or pseudo-field) for Views. To develop this module we first install Drupal and a few modules.
Drupal 7.31
Module list
- Chaos tools (https://www.drupal.org/project/ctools)
- Devel (https://www.drupal.org/project/devel)
- Entity API (https://www.drupal.org/project/entity)
- Module Filter (https://www.drupal.org/project/module_filter)
- Rules (https://www.drupal.org/project/rules)
- Token (https://www.drupal.org/project/token)
- Views (https://www.drupal.org/project/views)
Other
Or use Drush with the make file which you can download using this link or from the files link at the bottom of this article.
You will next need to enable the following modules:
- Administration
- Module filter (module_filter)
- Chaos tool suite
- Chaos tools (ctools)
- Development
- Devel (devel)
- Devel generate (devel_generate)
- Other
- Entity API (entity)
- Entity tokens (entity_token)
- Token (token)
- Rules
- Rules (rules)
- Rules UI (rules_admin)
- Views
- Views (views)
- Views UI (views_ui)
- drush
- en
- module_filter
- ctools
- devel
- devel_generate
- entity
- entity_token
- token
- rules
- rules_admin
- mymodule
- views
- views_ui
Then create some node types – type 1 and type 2 – each one with some integer fields. The fields use the default settings, but can be changed if required.
NOTE: this module will only expect single value fields. Adapting for multiple value fields is left as an exercise for the reader.
Add some dummy content using drush and devel.
$ drush genc 10 –types=type_1,type_2
Then create a new view.
On this View, we want to display the the total of the numeric fields on all the nodes, so we'll get to work developing our module.
Telling Drupal about the module
To start, we go through the standard way to create a new module (https://www.drupal.org/developing/modules/7).
So in our sites/all/modules directory we create a new folder called mymodule. And in there, create two files called mymodule.info and mymodule.module.
In mymodule.module add the php start tag
<?php
And in mymodule.info we add
name = My Module
description = Tutorial module for extending views
core = 7.x
package = Tutorial
Now we can see our module at /admin/modules.
Before activating it, we tell Drupal that Views is required to enable the module, so add a line to mymodule.info.
dependencies[] = views
After refreshing /admin/modules, we can enable the module.
The module, of course, does nothing right now, so let's disable it and then we can tell Views about it.
Telling Views
To let Views know about our module, we need to add some code to mymodule.module.
/**
* Implements hook_views_api().
*/
function mymodule_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'mymodule') . '/views',
);
}
With this, Views will try to load views/mymodule.views.inc, so we should now create this.
<?php
/**
* Implements hook_views_data
*
* Registers field to get progress from Entityform
*/
function mymodule_views_data() {
$data = array();
return $data;
}
Now we have a “complete” Views plug-in. Unfortunately it still does nothing. So next, we need to specify something for it to do.
To define our new field we add to the views/mymodule.views.inc file.
$data['node']['total'] = array(
'title' => t('Total'),
'help' => t('Sums the numeric fields in a node'),
'field' => array(
'handler' => 'mymodule_sum_fields',
),
);
Now when the module is enabled, we can find the new field in the list.
But if we try to add it we get:
As suggested, a “handler” needs to be created.
Creating a Handler
A handler contains the code which needs to be run. There are a few different handlers for views (https://api.drupal.org/api/views/views.api.php/group/views_handlers/7) but, for the moment, we are only interested in field handlers.
So let's create a file for the handler.
And tell Drupal about it in mymodule.info.
files[] = views/mymodule_sum_fields.inc
Now, in views/mymodule_sum_fields.inc, we'll sub-class one of the standard handlers, views_handler_field_numeric, and start to implement the handler.
Now when the module is enabled and the field is added, a lot of extra functionality is added from the parent class.
But a new problem has come up:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'node.total' in 'field list'
To fix this, we'll over-ride the query function in our handler and stop the addition of the field to the SQL query.
/**
* {@inheritdoc}
*/
function query() {
$this->ensure_my_table();
$this->add_additional_fields();
}
Now, with the module enabled and the field added we get can see the view.
All is going well except the output is correct. We are getting 0 for everything.
Another over-ridden function is needed. This time it's the render method.
To keep the added functionality of the numeric field handler, we'll copy the super class's code into our module and then change it as necessary.
/**
* {@inheritdoc}
*/
function render($values) {
$value = $this->get_value($values);
if (!empty($this->options['set_precision'])) {
$value = number_format($value, $this->options['precision'], $this->options['decimal'], $this->options['separator']);
}
else {
$remainder = abs($value) - intval(abs($value));
$value = $value > 0 ? floor($value) : ceil($value);
$value = number_format($value, 0, '', $this->options['separator']);
if ($remainder) {
// The substr may not be locale safe.
$value .= $this->options['decimal'] . substr($remainder, 2);
}
}
// Check to see if hiding should happen before adding prefix and suffix.
if ($this->options['hide_empty'] && empty($value) && ($value !== 0 || $this->options['empty_zero'])) {
return '';
}
// Should we format as a plural.
if (!empty($this->options['format_plural'])) {
$value = format_plural($value, $this->options['format_plural_singular'], $this->options['format_plural_plural']);
}
return $this->sanitize_value($this->options['prefix'], 'xss') . $this->sanitize_value($value) . $this->sanitize_value($this->options['suffix'], 'xss');
}
The piece of code that needs changing is the first line:
$value = $this->get_value($values);
As it stands, this gets the value from the database, but we don't want that.
First, we set $value to be 0 and load the node and put it into an entity wrapper.
$value = 0;
$node = entity_load('node', array($values->nid))[$values->nid];
$wnode = entity_metadata_wrapper('node', $node);
Next, set up a loop to go through each property and check if it is a field and it is an integer. And for each of these, add the field's value to the $value variable.
foreach ($wnode->getPropertyInfo() as $key => $val) {
if (isset($val['field']) && $val['type'] == 'integer') {
$value += $wnode->$key->value();
}
}
A refresh of the view shows us desired output.
We hope you found that useful. Happy Drupal-ing!