1

We have a view for the Basic page content type, which has a Category field that allows to enter multiple values.
All values for Category are displayed (IoT, Cloud, Security), but there are only nodes that use IoT and Cloud. I want to display only IoT and Cloud for the Category exposed filter.

How can I limit the exposed filters to only display values for which content exists?

The view settings are the following.

Format
  • Show: Content | Teaser
  • Format: Unformatted List | Settings
Filter Criteria
  • Authored on (exposed)
  • Category (field_category) (exposed) | Settings (settings is set to Category tag, selection type = Dropdown)
  • Author (exposed)
  • Published (= Yes)
  • Content Type (= Basic Page)
Sort Criteria
  • Content: Authored on (desc)
Contextual Filters, Relationship
  • None
Exposed form
  • Exposed form style: Better Exposed Filters | Settings
  • Settings are set to default in Better Exposed Filters
Other
  • Use Ajax: Yes
  • Use aggregation: no
  • query settings: default settings

Here is what we tried. This only gets me the nodes that have a value for field_category and all term values for categories term individually.

function mymodule_form_views_exposed_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {

  $node_storage = Drupal::getContainer()->get('entity_type.manager')->getStorage('node');
  $node_ids = $node_storage->getQuery();

  // This returns all nodes with a value for *field_category*. 
  $nodes = $node_ids->condition('type','page')
    ->exists('field_category')
    ->condition('status', 1)
    ->sort('title')
    ->execute();

  // returns all taxonomy terms. 
  $category_tree = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree('categories');

}
2
  • 1
    See Change filter value in Views programmatically in hook. Basically you will want to query the DB to see if there is at least 1 result for each category and then override the filter based on that.
    – No Sssweat
    Jan 1, 2019 at 4:15
  • @NoSssweat using the link i updated the question with the code I tried, but still cannot get the list i need. Thoughts? Jan 2, 2019 at 21:07

1 Answer 1

2

This works for me:

function mymodule_form_views_exposed_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if (isset($form['tid']['#options'])) {
    foreach ($form['tid']['#options'] as $key => $value) {
      // Limit to numeric keys to exclude the 'All' option.
      if (is_numeric($key)) {
        // Start database query.
        $query = \Drupal::database()->select('taxonomy_index', 'ti');
        $query->fields('ti', ['nid']);
        $query->condition('ti.tid', $key);
        $nodes = $query->execute()->fetchAssoc();
        // Remove option if term has no instance.
        if (!$nodes) {
          unset($form['tid']['#options'][$key]);
        }
      }
    }
  }
}

You will probably need to adjust the 'tid' in $form['tid']['#options'] to the Filter identifier as defined in your views' filter settings. Credits for the quick and efficient database query go to https://drupal.stackexchange.com/a/219368/25154.

2
  • Instead of adding the database query inside the foreach, I retrieved all the nid first, then I queried outside of the foreach all of the terms I needed, and inside the foreach I was able to filter out and use unset as you have based on an if condition. This answer led to that solution so marking it as such. Jan 3, 2019 at 23:14
  • How would you further limit this list so only nodes of X content type are associated? Aug 17, 2020 at 11:46

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.