Drupal 8 Views tutorial for developers. Part I — Theory

Oleksandr Trotsenko
Oleksandr Trotsenko
5 min readJan 14, 2019

Have you ever wondered how you could extend Views functionality as a module developer?

You sure could leverage some alter hooks or theme layer to get things done, but it would be a ‘quick-n-dirty’ solution. On the contrary, by leveraging interface Views creators has thoughtfully designed, one could accomplish the same task in a more sustainable, reusable manner.

I am the author of Webform Views module, a module whose main goal is to offer lean, predictable integration of Webform Submissions with Views. Hence I had to form solid understanding of all the leverages Views module exposes to developers and I seek to share that knowledge with the Drupal community.

In this series of posts we will explore all the main extensible assets of Views:

  • Fields, i.e. how to make custom content available to a view
  • Filters, i.e. how to filter content in a view
  • Sorts, i.e. how to order content in a view
  • Arguments (additional option for filtering content in a view)
  • Relationship (something that goes inline with adding even more content available to a view)
  • hook_views_data(), i.e. how to explain your database layout to Views

Here are the fields, filters, sorts, arguments, and relationships depicted visually within Views admin UI:

A simple map that describes where each of the assets is visible on the Views UI.

However, let’s begin by considering what Views are? I’d say

Views is a configurable engine to display content.

That is, with Views without any ‘coding’ knowledge site builders can customize which and how their website’s content is displayed. How could Views offer that? It becomes feasible if we decompose the ‘which’ and ‘how’ from above into some more primitive building blocks, each backed by actual abstract self-sufficient piece of PHP code, so then site-builders just wire (assemble) those blocks together in such a sequence that meets their business requirements.

However, a view by itself is more of configuration thing— it is just a sequence of building blocks wired together. Execution of that view on top of some particular database (content) is what we see. Let’s keep these 2 concepts in mind (the ‘building blocks’ and the ‘database’) as they are the primary points whenever we speak of extending Views.

We shall begin with the ‘building blocks’. In Views terminology they are referred to as handlers. There are many kinds of handlers, to name a few: fields, sorts, filters… which is quite logical — while all of them are ‘building blocks’ some might be responsible for what properties of queried data is displayed whereas others might be responsible for the order in which data is displayed and yet others for filtering the whole set of data down to a more refined subset. To give a few examples, consider a field handler that displays label of a node, or consider a sort handler that orders data by node’s creation date.

Now, what does an abstract self-sufficient piece of PHP code sound to you in Drupal 8 terminology? Right! Plugins & Plugin API! I would strongly recommend you reading official D8 docs on Plugin API (which really nails it, by the way!) because all Views handlers are Plugins, therefore handler types (field, sort, filter, etc.) are nothing else but Plugin types.

Alright, that wasn’t too difficult, was it? Now imagine yourself the guy who created Views and you want to offer sorting by node’s creation and node’s update date. Both columns in database are identical in their data type (unsigned integer), logic for sorting is also identical (ORDER BY [column_name]). Would you copy-paste same code twice to only change the column name? Doesn’t sound smart at all. That is why we have this famous hook_views_data() hook. Imagine if all handlers were parameterized by column name, in other words you’d have a sort handler SortByInteger that would produce ‘ORDER BY $this->column_name’ kind of SQL snippet and you would then offer same SortByInteger handler twice: for ‘created’ and ‘updated’ columns. In fact all views handlers are parameterized on much more than just column name and because they are parameterized (thus they no longer have knowledge about particular database schema in which they are running) we need somehow to communicate to the Views engine which handlers are responsible for which particular database columns. This is exactly the job of hook_views_data() where you explain to Views which columns your database has and which specific Views handler should operate each specific column so you can reuse SortByInteger sort handler for both created and updated columns.

Another reason to have such intermediate mapping ‘column => Views handler‘ from hook_views_data() is the fact that sometimes your database schema is dynamic and you just cannot hard-code that data into PHP code — site-builder could attach/remove a field from a node type — you can’t foresee that and simply cannot hard code relation between Views handlers and the particular tables/columns your specific database has. Implementations of hook_views_data() are PHP code, so you can, for example, examine the currently attached fields to your node type and construct respective output for your hook_views_data() thus bypassing such limitation.

Having that said, Views nowadays are mostly used for displaying Entities. However, it is not their sole use-case. In fact with proper integration Views can display any data, not only Entities, stored in your database (to be precise Views can display data not only from a SQL database too!). To keep us focused on Views only and not to pollute this tutorial with other Drupal 8 concepts, let’s suppose we have the following tables in our MySQL database that we want to display through Views:

Table of fruits

CREATE TABLE `fruit` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Unique ID of the fruit.',
`label` varchar(255) NOT NULL COMMENT 'Label of the fruit',
`weight` float unsigned NOT NULL COMMENT 'Weight of the fruit. Units are stored in weight_unit column.',
`weight_unit` varchar(255) NOT NULL COMMENT 'Unit of the weight.',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Stores data on fruits'

Table of relations between fruits (some fruits are sweeter and/or bigger than others!):

CREATE TABLE `fruit_relations` (
`fruit_id` int(10) unsigned NOT NULL COMMENT '{fruit}.id of the fruit whose relation is described in this row.',
`relation` varchar(255) NOT NULL COMMENT 'Type of relation. Allowed values are: sweeter, bigger',
`fruit_id_related` int(10) unsigned NOT NULL COMMENT '{fruit}.id of the fruit that is related to the fruit described in this row.'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

We should probably have some additional foreign keys and indexes on those tables, but for simplicity I just omit them. Our focus is the table structure.

With these 2 simple tables we will learn how to introduce custom fields, sorts, filters, arguments, and relationships into Views cooking pot.

Stay tuned! In the next episode we will be exposing these tables to Views and making those columns visible as fields.

Click here for Part II

--

--