Drupal 8 Views tutorial for developers. Part III — Sorts & Filters

Oleksandr Trotsenko
Oleksandr Trotsenko
6 min readJan 17, 2019

Alright, I am glad to see you guys have made it this far! In this article we will explore how to use 2 additional handler types — sorts and filters.

In case you are wondering where Part I and Part II are — they are just around the corner: Part I, Part II.

Sort handlers, as you could guess, are responsible for sorting views rows. They are just as handlers as fields from the previous articles are. This means same mechanics apply:

  • You need to describe a certain column in hook_views_data() to make it possible to sort views by that column.
  • You should strive to reuse existing sort handlers as much as possible. In the rare occasion when no pre-existing sort handlers cover your needs, you always can code one yourself and then use it within hook_views_data().

So how do you establish to Views that for sorting on a given column it must use a given sort handler? Let’s grab fruit.label column and make it sortable in Views — just chime in a few more lines into hook_views_data():

Pay attention to the sort key of $views_data['fruit']['label'] array above. This is where we explain to Views how it can sort by that column. We picked up a really simple sort handler. If you want to get acquainted with more sort handlers Drupal 8 core ships with, then have a look into the folder ./core/modules/views/src/Plugin/views/sort. Just as it was with the fields, in case you are using another sort handler, explore docblock comment of the corresponding class to make sure you supply all the necessary definition items within your hook_views_data() that the sort handler expects to receive. And vice-versa, when you code a custom sort handler and it expects any additional definition items, be a well-mannered programmer — document it in the docblock.

Visually nothing changed because our data was already naturally sorted within DB storage.

Executed SQL (see that now we have the ORDER BY clause?):

SELECT fruit.label AS fruit_label, fruit.weight AS fruit_weight, fruit.weight_unit AS fruit_weight_unit
FROM
{fruit} fruit
ORDER BY fruit_label ASC
LIMIT 11 OFFSET 0

Speaking of custom handlers, now let’s ramp up and try to code a custom handler that will allow us to sort by fruit weight considering the unit of measure.

It will again be same two-step combination: 1) code a custom handler, 2) use it for a given column. By the way, are you starting to notice the charm of handlers — even though they might be of different type, they still share vast amount of usage patterns and behave more or less by the same laws. So here is our ‘unit aware’ weight sort:

I hope you can follow the logic from the code above. In summary, we need to multiply all the lb weights before we can compare them to the kg weights. So we do. Once we have a column in resultset that contains all weights normalized to kilograms, actual sorting is easy — ahm.. just sort by that column. In case you have not seen before the SQL construction of CASE … WHEN .. THEN … END, refer to this supplementary documentation.

Now we shall proceed to the step 2, i.e. leveraging this custom sort handler in our hook_views_data():

Look attentively through the sort key of the $views_data['fruit']['weight']. Apart from specifying the ID of our sort handler, we also supply it the 2 definition items it declares to receive there.

The produced SQL is (you should recognize this CASE .. WHEN .. THEN.. END construction):

SELECT fruit.label AS fruit_label, fruit.weight AS fruit_weight, fruit.weight_unit AS fruit_weight_unit, CASE fruit.weight_unit
WHEN 'kg' THEN fruit.weight
WHEN 'lb' THEN fruit.weight * 0.4535924
END AS weight
FROM
{fruit} fruit
ORDER BY weight ASC
LIMIT 11 OFFSET 0

It shouldn’t be difficult to recognize the part of SQL that was generated by our custom sort handler (the weight column in the field list and the ORDER BY weight ASC clause).

As you can see with this sort handler, turns out writing complex SQL within Views is barely harder than the SQL itself you need to write. (Well, don’t take the previous statement literally — Views will certainly kick you back in the butt more than once during your Drupal career.. I just through it’d be a nice phrase to mention here).

At this point we are ready to proceed to Views filters. With filters, we will explore the same path of adding a pre-existing filter on the fruit.label column and then coding a custom one which will be compatible with fruit.weight column.

Examine contents of the filter key of the $views_data['fruit']['label']. Also, by now you probably can guess where to look for a list of core filter handlers - right, it’s the folder ./core/modules/views/src/Plugin/views/filter.

It is time to venture for writing a custom filter handler. To keep you learning something new about handlers, we will utilize the notion of options this time. Each views handler can receive configuration from 2 sources: either definition (something we have seen already) or options. The difference is that the former comes from the developer, i.e. controlled by developer when $views_data is being assembled within hook_views_data(). It makes definition item a good candidate for low-level configuration, for e.g. specifying name of the column that holds weight units through a definition item makes perfect sense — developer who writes hook_views_data() is the guy who has most knowledge about what that column is.

On the other side, the later (options) come from site-builder and are specified on that pop-up form within Views UI you all probably have seen when you click on a handler. Therefore, options are ideal candidates for things you expect a site-builder might want to configure. In our ‘weight’ example, it could be the data point that specifies whether all the weights should be converted to kgs or to lbs.

In the weight filter referenced below, we are actually using both. Through handler definition we receive name of the column where weight units are stored, whereas through options we allow site-builders to specify actual filtering criteria (like ‘greater than X kg’ or ‘equals to Y lbs’ and so on). Additionally, because we are working with floating points, we also allow site-builder to specify the comparison precision (up to how many digits after the floating point should be considered whenever we compare to 2 floats).

Take your time to familiarize with the actual source code of this custom filter handler. It is enriched with comments enough to follow actual logic.

The only thing left to do in this part is to actually leverage this new custom filter handler for our particular weight and weight_unit columns:

That is it! Go have fun with the new weight filter in your view. You should be able to numerically filter on the weight regardless of the units, i.e. the unit conversion is seamlessly handled by the weight filter we have just coded.

Visual explanation of which methods of your filter handler correspond to certain parts of the configuration form on the Views UI.

This wraps up part III. The full code related to part III can be found here. Make sure to stay on the branch part-3 as each branch corresponds to the respective part. I hope you are still having fun learning Views and in the next chapter we will discover the concept of Views arguments.

Continue to the Part IV.

--

--