Thank you misterdidi for setting me on a right track.
The article you've provided helped, but in order to find out the solution, I had to study a few dozens more, and eventually the solution came around.
So, for this task the only certain solution I found is this:
- Read whatever is in "Radio filter".
- Alter "checkbox filter", so it would contain the value from "radio filter" in every option.
- Merge all "state fields" into one multi-value field, so we could...
- Use contextual filters to read "checkbox filter" value from URL and look for it in our multi-value field.
1. Reading value from Radio filter
I achieved this with views_pre_view hook:
function MY_MODULE_views_pre_view($view, $display_id, array &$args) {
// Check if it's the right view and display mode
if ($view->id() == 'MY_VIEW') {
switch ($display_id) {
case 'MY_DISPLAY':
//Get whatever value is in Radio Filter and write it into global variable
$filter_input = $view->getExposedInput();
global $arg;
$arg= $filter_input['RADIO_FILTER'];
}
}
}
(Here and forward, everything IN CAPS is the names you have to replace with the ones you use)
With this code, I read what's in the Radio Filter and put it into a global variable $arg.
2. Put value from Radio Filter into Checkbox Filter
For this part I used form_views_exposed_form_alter hook:
function MY_MODULE_form_views_exposed_form_alter(&$form, $form_state, $form_id) {
// Call the global variable with the value of Radio Filter
global $arg;
// Convert the raw value of the Radio Filter into something we can use for contextual filter
if ($arg == '1') {
$arg= 'AC';
}
elseif ($arg == '2') {
$arg= 'HEATER';
}
elseif ($arg == 'All') {
$arg= '';
}
$arg_on = ($arg . '_on');
$arg_off = ($arg . '_off');
// Check that it's the right form
if ($form['#id'] == 'EXPOSED_FILTER_FORM_ID') {
// We need different filter values, depending on whether value in Radio Filter is chosen, or if it's set to default ("All")
if ($arg != '') {
$form['CHECKBOX_FILTER']['#options'] = array(
$arg_on => $arg_on,
$arg_off => $arg_off,
);
}
else {
$form['CHECKBOX_FILTER']['#options'] = array(
'_on' => '_on',
'_off' => '_off', );
}
// If nothing is chosen, we should consider everything chosen.
if ($form['CHECKBOX_FILTER']['#value'] == '') {
$form['CHECKBOX_FILTER']['#value'] = [$arg_on, $arg_off];
}
}
}
This way I modify values in Checkbox Filter. Doing so makes them pretty much inoperable as exposed filters (I'm not sure why, but the moment the form is modified via hook - it doesn't filter anything anymore), but it provides URL parameters, which we will use for contextual filters.
3. Merging all parameter fields into one
This one is something I've expected to find a module for, but failed, so hook_entity_presave it is.
function MY_MODULE_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
// Here we prepare variables for every field we will need to merge ...
if ($entity->bundle() == 'MY_NODE_TYPE') {
$par_ac = ($entity->field_AC_CHECK->value);
$par_heater = ($entity->field_HEATER_CHECK->value);
$par_all = ($par_ac . ',' . $par_heater);
// ... and also check out if there's any "on" or "off" among them ...
if (str_contains($par_all, 'on')) {
$par_cont_on = '_on';
}
if (str_contains($par_all, 'off')) {
$par_cont_off = '_off';
}
// ... to merge them all into one field, and provide the overall "on" and "off" values for the cases when Radio Filter is not filled
$entity->field_par_comb = [$par_ac, $par_heater, $par_cont_on, $par_cont_off];
}
}
I'm not particularly happy with the fact that the fields are hard-coded and not dynamically gathered, maybe later I'll think of a way of de-hardcoding these.
But for now, we have a multi-value field that contains a state for every parameter, and we can finally...
4. Use contextual filter
As we found out in p. 2, the exposed Checkbox Filter doesn't do anything when it's modified. But we can make it check the URL parameter of a Checkbox Filter. This way, if we have "checkbox_filter=heater_on", the contextual filter will look in our multi-value field from p. 3 for "heater_on" value.
Since Checkbox Filter can contain several values, we should choose "OR" conjunction in a filter's settings.
Note that the Radio Filter should be "not required". It's because, when the view is loaded for the first time, default exposed filter values are NOT shown in URL parameters, for some reason. But, since the default Radio Filter value is "Any" (and we should hide it with CSS via :first-child selector), in our code there's a part that takes this case into account by replacing "All" Radio Filter value with ''. And, since we have special check and "_on"/"_off" values in our multi-value field, it will work fine:
- If the default "Any" value is chosen in Radio Filter, the code will not replace Checkbox Filter values.
- In view settings, default Checkbox Filter values are set to "_on" and "_off" and the filter is operable.
- When the user picks "AC", "Heater" or any other option in Radio Filter, it's being put into modified Checkbox Filter values and Contextual Filter kicks in.
This was a hell of a case for the first Drupal module development experience, but it works flawlessly. The customer is happy, I am happy.