Allow for exposed filter options "Empty" and "Not empty" in the selection list to filter on (un)set values

Created on 7 August 2019, over 5 years ago
Updated 7 June 2023, over 1 year ago

There does not appear to be an out-of-the-box single-filter way to expose a field's values (e.g. boolean `-Any-`/Yes/No yes-no query) via the Views UI to answer the questions for any field on any entity:

  • Does this field have a value? Has it been set?
  • Is it empty (unset)?

Use Cases

  • User wants to narrow Views results to only entities/nodes whose XYZ field have not yet been filled. This could be for a newly-added field or for just general content cleanup.
  • User wants to find Media entities whose thumbnail :target_id is not empty (has one or more files attached).
  • User reference fields: Have any users been assigned to this entity, or not yet?

Workarounds

  • I have seen a field-specific snippet in D7 as an example that hooks hook_views_data_alter to provide such a query against a NULL result from a join, but I haven't tried its equiv in D8 yet. I think this is the "workaround" that should be in core and available for all fields...
  • For booleans, I can manually build a Grouped exposed filter to define each of the states Yes/No/-Any- for a specific field, but this doesn't work for fields where there can be a variable number of options.
  • Add additional exposed filters with the operators Is empty (NULL) and Is not empty (NOT NULL). This shouldn't be necessary from a UX perspective because these options can be included in main options list for this filter.
✨ Feature request
Status

Active

Version

9.5

Component
ViewsΒ  β†’

Last updated about 7 hours ago

Created by

πŸ‡ΊπŸ‡ΈUnited States texas-bronius

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • πŸ‡ΊπŸ‡ΈUnited States loopy1492

    This isn't really just an exposed filter issue.

    For example, after years of a site running:

    - I add a checkbox to a content type.
    - I want any content with the checkbox checked to not be visible in a view.
    - I add a filter to the view for "Not equal to true"
    - The view displays no results because the values of all the existing content are "null".

    I would expect, instead, all the previous content to show up because they are not set to true.

  • πŸ‡ΊπŸ‡ΈUnited States maskedjellybean Portland, OR

    This seems like it should be part of core. The only way to do achieve the same filtered result via core right now is to expose the operation filter. Then the user has to choose "Is empty (NULL)" from a separate filter and "- Any -" from the main filter. This is not good UX.

    In the meanwhile I was able to do this for a one off situation using a couple hooks and I wanted to share.

    The idea is to expose the operation filter and then hide it in a form alter. Also in the form alter, add a "None" option to the main filter. Then in a query alter, if None was selected, apply a JOIN and WHERE as though "Is empty (NULL)" were chosen from the operator filter.

    Things to know about the code example:
    - My field is a taxonomy reference field with the machine name field_prof_languages.
    - My view has the machine name profile_language_report.
    - field_prof_languages_target_id_op refers to the operation filter.
    - For what it's worth, you can't do this via an event subscriber because of this: https://www.drupal.org/project/hook_event_dispatcher/issues/3365175 πŸ› VIEWS_QUERY_ALTER doesn't allow unset() but equivalent hook does Active

    /**
     * Implements hook_form_FORM_ID_alter().
     *
     * PURPOSE: Add custom "None" option to Profile Language Report view filter.
     */
    function mymodule_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
      if ($form['#id'] === 'views-exposed-form-profile-language-report-page-1'
        || $form['#id'] === 'views-exposed-form-profile-language-report-data-export-1'
      ) {
        // Hide operations field.
        // We instead add "None" as option to main field
        // and alter the query in views query alter.
        $form['language_wrapper']['field_prof_languages_target_id_op']['#access'] = FALSE;
        $options = $form['language_wrapper']['language']['#options'];
        $form['language_wrapper']['language']['#options'] = array_slice(
            $options, 0, 1, TRUE) +
          [
            'None' => new TranslatableMarkup(
              '- None (no language set) -'),
          ] +
          array_slice($options, 1, count($options) - 1, TRUE);
      }
    }
    
    /**
     * Implements hook_views_query_alter().
     *
     * PURPOSE: Alter Profile Language Report view query based on
     * "None" option added in mymodule_form_views_exposed_form_alter.
     */
    function mymodule_views_query_alter(ViewExecutable $view, QueryPluginBase $query) {
      if (isset($view->getExposedInput()['language']) && $view->getExposedInput()['language'] === 'None') {
        $definition = [
          'table' => 'node__field_prof_languages',
          'field' => 'entity_id',
          'left_table' => 'node_field_data',
          'left_field' => 'nid',
        ];
        $join = \Drupal::service('plugin.manager.views.join')
          ->createInstance('standard', $definition);
        $query->addRelationship('node__field_prof_languages', $join, 'node_field_data');
        $query->addWhere(
          1,
          'node__field_prof_languages.field_prof_languages_target_id',
          NULL,
          'IS NULL'
        );
        // Remove unwanted where condition created by
        // our custom "None" option added in form alter.
        unset($query->where[0]);
      }
    }
    
  • πŸ‡§πŸ‡ͺBelgium beerendlauwers

    +1 on this. I needed a way to provide a checkbox exposed filter that indicated if an entity reference field was empty or not, and was surprised to find that this does not exist out of the box.

Production build 0.71.5 2024