4

We have a custom view, myview, that has two exposed filters: country and category. For the country value we have a value that we want to be applied regardless of what is selected as the exposed filter that is not All Countries.

How would we alter the query after a value is selected so that we always append an or country = United States of America to whatever value is selected for the view's query?

Goal:

After the exposed filter has been applied, alter the query such that it will return whatever the user selected AND all other nodes where country = 'United States'.

Country value:

  1. All Countries
  2. Worldwide
  3. United States of America
  4. United Kingdom
  5. France
  6. Germany

What we were thinking of using hook_views_query_alter, but not sure how we can alter the query after a value has been selected.

mymodule.module

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

/**
 * Implements hook_views_query_alter().
 */
function mymodule_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {

  if ($view->id() == 'myview') {
    $or_statement = new Condition('OR');
    $or_statement->condition('field_country', '12', '=');
    $query->addWhere(0, $or_statement);
  }

}

Current filters:

  1. published = yes
  2. content type = article
  3. country (exposed filter, display all, single select)
  4. category (exposed filter, display all, single select)

When using ksm() to print the $condition_group, this is the results. It is not even showing the two exposed filters. condition group

Current Where Clause

10 is the value selected by the user from the exposed filter and 12 is the one we want to add.

WHERE (((node__field_country.field_country_target_id = '10')) AND (node__field_country.field_country_target_id = '12'))

Desired where clause

WHERE (((node__field_country.field_country_target_id = '10')) OR (node__field_country.field_country_target_id = '12'))

2 Answers 2

4

Using d70rr3s suggestion and this patch (required), I came across this solution that worked.

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

/**
 * Implements hook_views_query_alter().
 */
function mymodule_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
   if ($view->id() == 'myview'  && !empty($view->exposed_raw_input['field_country']) && $view->exposed_raw_input['field_country'] !== 'all') {
    // Traverse through the 'where' part of the query.
    foreach ($query->where as &$condition_group) {
      foreach ($condition_group['conditions'] as &$condition) {
        if ($condition['field'] == 'your_entity.field_country') {
          $field = 'node__field_country.field_country_target_id';
          $value = 'somevalue';
          $operator = '=';
          $query->addWhere(2, $field, $value, $operator);
        }
      }
    }
  }
}

Results:

WHERE (((node__field_country.field_country_target_id = '10')) OR (node__field_country.field_country_target_id = 'someValue')) AND (node_field_data.status=1) AND (node_field_data.type='article')

Filter Criteria where #1 and #2 from screenshot had to be applied:

filter criteria

1
2

Taken from the hook example. Assuming your filter name is field-country a possible solution could be:

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

/**
 * Implements hook_views_query_alter().
 */
function mymodule_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
   if ($view->id() == 'myview'  && !empty($view->exposed_raw_input['field_country']) && $view->exposed_raw_input['field_country'] != 'all') {
    // Traverse through the 'where' part of the query.
    foreach ($query->where as &$condition_group) {
      foreach ($condition_group['conditions'] as &$condition) {
        if ($condition['field'] == 'your_entity.field_country') {
           $filter_value = $view->exposed_raw_input['field_country'];
           if (!is_array($filter_value)) {
             $filter_value = [$filter_value];
           }

           $condition = [
             'field' => 'your_entity.field_country',
             'value' => array_merge($filter_value, ['United States Of America']),
             'operator' => 'IN',
           ];
        }
      }
    }
  }
}

I haven't fully test it but should work.

Also you can always implement your own ad-hoc filter plugin.

Update: Add a condition to check if the filter is multivalue.

Update: Remove strict comparison for filter input value as it may cause side-effects.

7
  • i was writing some ksm() statements and found the logic inside theif does not run since the condition is not present in the $condition_group. I have updated my question to reflect the filters. I am finding it strange that it is not showing in the $condition_group Aug 1, 2018 at 19:45
  • I am now getting Warning: array_merge(): Argument #1 is not an array in ns_general_views_query_alter() when using array_merge(). Any thoughts? Aug 1, 2018 at 20:16
  • The condition_group won't show if the filter is not present as it is exposed. If I did understand your question you want your extra OR to be added if the filter is present don't you? The error your are getting is because the filter may not be configured to support multiple values so it won't be an array. I'll update my answer to properly handle it both.
    – d70rr3s
    Aug 1, 2018 at 21:52
  • i would like the filter to be added after the user has selected a value for the exposed filter. For example, if the exposed filter is set to "France", i want to return the nodes where country = france AND nodes where country=United States of America. Essentially always return nodes where country = United States of America AND any values selected using the exposed filter. Aug 1, 2018 at 23:00
  • i have updated the question with the current where clause. Aug 2, 2018 at 0:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.