Tutorial

How to have Nested Filter Groups in Drupal 8 / 9 / 10 Views

While working on a fairly complex website with very complex views setup, including tens of exposed filters I stumbled upon an issue where I needed nested filter groups. This time at least not exposed.

However, you can not make nested filter groups in Views at all. You can do AND and OR groups, but nesting them is impossible.

This of course creates problems when you need it, and there is no other option. So I searched half the internet for a solution and arguably developed my own.

I posted this solution into Drupal issue thread as well so whoever finds this in the future will have a better time than I had.

Pridal/a lubo dňa Po, 01/30/2023 - 06:17
Use HOOK_views_query_alter()

At this point you have no other option than to create custom module, so I will not go through the process at all. Just use this hook like that:

use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\query\QueryPluginBase;

function MYMODULE_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {

...

}

 

Add relationship to the fields you need to filter on

Adding relationship in Views is complete different way. Views API is optimised to always prefer / add LEFT JOINs over anything else (it is because some sql systems does not support RIGHT JOINs) so just use the code below:

// Add LEFT JOIN to First Field
		$configuration = [
		  'table' => 'node__field_YOUR_FIELD_NAME',
		  'field' => 'entity_id',
		  'left_table' => 'node_field_data',
		  'left_field' => 'nid'
		];
		$join = Drupal::service('plugin.manager.views.join')->createInstance('standard', $configuration);
		$query->addRelationship('node__field_YOUR_FIELD_NAME', $join, 'node_field_data');

 

Use addWhereExpression method from QueryPluginBase class

This is really not well documented in Drupal API docs but you can write whatever you see fit into addWhereExpression and it will run. So use the code below:

		$queryExpression = "
		(
			(node__field_YOUR_FIELD_NAME.field_YOUR_FIELD_NAME_value IN('1'))
			AND
			(node__field_SECOND_FIELD_NAME.field_SECOND_FIELD_NAME_value IN('2'))
		)
		OR
		(
			node__field_SECOND_FIELD_NAME.field_SECOND_FIELD_NAME_value IN('3')
		)
		";

		$group_id = $query->setWhereGroup('AND');
		$query->addWhereExpression($group_id, $queryExpression);

 

Full code!

I mean, you probably get the glimpse of how it works, but here is the full code for you if you do not like snipplets above.

use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\query\QueryPluginBase;

function MYMODULE_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {

	if ($view->id() == 'YOUR_VIEW_ID') {

		// Add LEFT JOIN to First Field
		$configuration = [
		  'table' => 'node__field_YOUR_FIELD_NAME',
		  'field' => 'entity_id',
		  'left_table' => 'node_field_data',
		  'left_field' => 'nid'
		];
		$join = Drupal::service('plugin.manager.views.join')->createInstance('standard', $configuration);
		$query->addRelationship('node__field_YOUR_FIELD_NAME', $join, 'node_field_data');

		 // Here you can add LEFT JOIN to second field...
		
		/*
		 * VIEWS ALTER LAYOUT:
		 *
		 
		- Exposed Filters -
		AND
		(
			(
				Field 1 = Value 1 AND
				Field 2 = Value 2
			)
			OR
			(
				Field 2 = Value 3
			)
		)
		*/

		// Here you basicly write whatever will run inside WHERE clause in query
		$queryExpression = "
		(
			(node__field_YOUR_FIELD_NAME.field_YOUR_FIELD_NAME_value IN('1'))
			AND
			(node__field_SECOND_FIELD_NAME.field_SECOND_FIELD_NAME_value IN('2'))
		)
		OR
		(
			node__field_SECOND_FIELD_NAME.field_SECOND_FIELD_NAME_value IN('3')
		)
		";
		
		$group_id = $query->setWhereGroup('AND');
		$query->addWhereExpression($group_id, $queryExpression);
		
	}
	
}

 

Might interest you

Tutorial
Sometimes you need to make an adjustements to a cart, or just simply compare the order total or any other property of current user's cart.…
Tutorial
So sometimes you just need to transliterate some kind of foreign (or local) language, and you need it to not have accents. I borrowed this…

Recommended

Article
32 views
For the past few days I am trying to comprehend why / how this blockchain even gained it'…
Tutorial
94 views
This sketch is quite easy, I used Arduino Nano with OLED 0.96″ display 128×64 resolution…
Tutorial
156 views
While working on a fairly complex website with very complex views setup, including tens…
Tutorial
14 views
In this case we have two options, either we use hook_user_presave() or we can create new…
Tutorial
15 views
When using Swiftmailer under Drupal 8 / 9 it automatically sets the headers for sender to…
Tutorial
7 views
Yes, IOS / Safari is the new internet explorer. Amount of time I spend on debugging…
Tutorial
43 views
There is a very handy function in Drupal 8 / 9, allowing developers refresh view when…
Tutorial
22 views
Often, when doing SEO checkups, SEO specialist come up with adding Schema.org…
Tutorial
174 views
I needed to test my contracts against USDC contract, specifically I needed ERC-721 mint…
Tutorial
85 views
If you are a newbie like I am and struggling with setting the proper MYSQL my.cnf config…
Tutorial
25 views
I had trouble to set this up properly, because documentation is quite misleading or often…
Article
72 views
As the title says, DO NOT in any circumstances install ANY bitcoin price extension to ANY…
Tutorial
278 views
This is (or should be) a working example of sending some Ether between two addresses.…
Module
45 views
This list was fetched from Zapper, with their /v1/token-list endpoint. Which you can…
Tutorial
143 views
In the last months I am being pretty much bombarded by my clients with asking what…