3

I have a exposed filter setup to filter content within a view.

The view is displaying a list of house designs. These designs need to be able to be filtered by number of beds, bathrooms etc.

"House Design" content type/node has a field (List (integer)) for bedrooms:

http://theexhale.com/screenshot_1.png

The allowed values for this field are 1,2,3,4,5 (bedrooms)

http://theexhale.com/screenshot_2.png

The issue I'm having is that the exposed filter is outputting all of these fields for sorting the view even though currently only values "2" and "4" are being used by the "House Design" content type.

http://theexhale.com/screenshot_5.png

QUESTION: Can the exposed filter be smart enough to check to see if values of a field/taxonomy vocab are actually being used by the displayed content type/node and then dynamically only display those values in the filter?

I want to do this to remove/lesson the possibility of no results when a user uses the filter.

Thank you :)

2

3 Answers 3

2

The concept's the same in Drupal 8 but the access of properties and handling is a little different from what's done on D7. Here's a working example of 3 exposed filters dependent on each other to allow only values available based on prior selections.

Tips: - View is built with page that has exposed form in block checked to yes. - Enable master display, make sure filter identify of exposed filter is same - Use BEF (better exposed filters) for menu select and autosubmit - See code comments

This is for D8:

/**


* @file
 * Provides functionality for handling exposed filters
 */

use Drupal\Core\Form\FormStateInterface;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\query\QueryPluginBase;
use Drupal\views\Plugin\views\query\Sql;
use Drupal\views\ResultRow;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\views\Entity\View;
use Drupal\Core\Form\FormState;
use Drupal\Core\Form;
use Drupal\taxonomy\Entity\Term;
use Drupal\Core\Entity;

/**
   * Implements hook_views_exposed_form_alter().
   *
   * alters exposed filter for equipment selector to limit filtered results in a new view
   */
function ahern_equipment_selector_form_views_exposed_form_alter(&$form,  &$form_state){
    if ($form['#id'] == 'views-exposed-form-ahern-exposed-filters-page-1') {
        // Get whatever values selected by user in all menus
        $input = &$form_state->getUserInput();

        // Form fields have given/arbitrary name from Filter identifier in Exposed filter settings
        $selected_what_moving = $input['moving'];
        $selected_where_working = $input['where'];
        $selected_height = $input['height'];

        // Again, form fields have given/arbitrary name from Filter identifier in Exposed filter settings
        // NOTE: make sure also match Filter identifier in Master display
        // Also keep in mind that any view duplicated/cloned from main view should be disabled as 
        // it can/will affect results
        $form['moving']['#options'] = _get_associative_array_from_view(
         'ahern_exposed_filters', // viewi id
          'default', // view dsplay id
          $selected_what_moving,
          'moving_chosen'
        );

        $form['where']['#options'] = _get_associative_array_from_view(
         'ahern_exposed_filters', // viewi id
          'default', // view dsplay id        
          $selected_where_working,
          'where_chosen'
        );

        $form['height']['#options'] = _get_associative_array_from_view(
         'ahern_exposed_filters', // viewi id
          'default', // view dsplay id
          $selected_height,
          'height_chosen'
        );
    }
}

function _get_associative_array_from_view($viewID, $viewDisplayID, $contextualFilter, $menu_chosen){
//ksm($contextualFilter,'context filter');

  $associativeArray = array();
  $associativeArray['All'] = t('- Any -');
  $viewResults = views_get_view_result($viewID, $viewDisplayID, $contextualFilter);

  foreach($viewResults as $viewRow) {

    switch ($menu_chosen) {
    // views_get_view_result returns rows with original field name so don't use filter identifier name to acceess, use field name
        case 'moving_chosen':
            //ksm($menu_chosen, 'target menu to filter to');
            $term_name = $viewRow->_relationship_entities['field_what_are_you_moving']->getName();
            $term_id = $viewRow->_relationship_entities['field_what_are_you_moving']->id();
            if (!in_array($term_name,$associativeArray)) {
                $associativeArray[] = $associativeArray[$term_id] = $term_name;
            }
            break;

        case 'where_chosen':
            $term_name = $viewRow->_relationship_entities['field_attr_where_are_you_working']->getName();
             $term_id = $viewRow->_relationship_entities['field_attr_where_are_you_working']->id();
             if (!in_array($term_name,$associativeArray)) {
                $associativeArray[] = $associativeArray[$term_id] = $term_name;
            }
            break;

        case 'height_chosen':
            //ksm($menu_chosen, 'target menu to filter to');
            $term_name = $viewRow->_relationship_entities['field_what_is_the_working_height']->getName();
            $term_id = $viewRow->_relationship_entities['field_what_is_the_working_height']->id();
            if (!in_array($term_name,$associativeArray)) {
                $associativeArray[] = $associativeArray[$term_id] = $term_name;
            }
            asort($associativeArray);
            break;

        default:
            break;
    }

  }

    $associativeArray = array_unique($associativeArray);
    return $associativeArray;
}
1

Sure see these examples. My google search terms were drupal exposed filters limit dynamic.

I like the 2nd example a tad better than the 1st. Using it as a template and making your own View that outputs allowed number of rooms used within the content type so far on the website, the code in a custom module could look like:

function MYMODULE_form_views_exposed_form_alter(&$form, &$form_state){

  if ($form['#id'] == 'views-exposed-form-MYVIEW-page') {

    $form['bedrooms']['#options'] = _get_associative_array_from_view(
      'mycustom_view_limit_bedroom_count', // view id
      'default', // view display id,
      // Look in the field_data_field_design_bedrooms table and get its
      // primary key for the key field, as its data value for the value field.
      // I forget the exact naming convention in Drupal for an integer
      // list field type offhand ...
      'field_data_field_design_bedrooms_id', // key field id
      'field_data_field_design_bedrooms_value_id' // value field id
    );
  }
}
?>
<?php
function _get_associative_array_from_view($viewID, $viewDisplayID, $keyFieldID, $valueFieldID){

  $associativeArray = array();
  $associativeArray['All'] = t('- Any -');
  // Your view will use Aggregation to show the unique list of room counts
  // by "id" -> "displayed count value" pairs.
  $viewResults = views_get_view_result($viewID, $viewDisplayID);

  foreach($viewResults as $viewRow) {
    $associativeArray[$viewRow->$keyFieldID] = $viewRow->$valueFieldID;
  }

  return $associativeArray;
}

My example code is not a guarenteed exact solution, but its not allowed to provide merely links to tutorials here -- so I'm providing some example code for you exemplifying the pieces of code that would go into a custom module.

0
1

I have just done this as well and based my solution on @vrwired's solution

(Drupal 8 Dynamic Dependent Dropdown Views Exposed Filters)

My view is a view of content teaser with 3 referenced vocabs on the content type.

Firstly I duplicated the entire view and called it the same as the first on with "Count" on the end and changed it to have no limit to the number of results returned and also to just return fields and only the title field.

This was because without it I wasn't getting the full list of terms I could filter by

All the things in CAPS are as descriptive as I could make them in 5 mins, you'll need to change them as required for your solution

<?php

/**
 * Implements hook_views_exposed_form_alter().
 *
 * Alters exposed filter for equipment selector to limit filtered results in a
 * new view
 */
function MYMODULE_form_views_exposed_form_alter(&$form, &$form_state) {
  if ($form['#id'] == 'views-exposed-form-VIEWID-page-1') {
    // Get whatever values selected by user in all menus
    $input = &$form_state->getUserInput();

    $args = [];
    foreach ($input as $arg) {
      if (!empty($arg)) {
        $args[] = $arg;
      }
    }

    $filters = ['FILTER_1', 'FILTER_2', 'FILTER_2'];
    foreach ($filters as $filter) {
      $form[$filter]['#options'] = MYMODULE_get_associative_array_from_view(
        'VIEWID_count', // viewi id
        'page_1', // view dsplay id
        $args, $filter
      );
    }
  }
}

/**
 * Filters out the unavailable options from the drop downs
 */
function MYMODULE_get_associative_array_from_view($view_id, $display_id, $args, $menu_chosen) {

  $options_array = array();
  $options_array['All'] = t('- Any -');
  $result = MYMODULE_get_view_result($view_id, $display_id, $args);

  foreach ($result as $row) {
    switch ($menu_chosen) {

      // views_get_view_result returns rows with original field name so don't
      // use filter identifier name to acceess, use field name
      case 'FILTER_1':
        $field = $row->_entity->get('FIELD_1')->getValue();
        break;

      case 'FILTER_2':
        $field = $row->_entity->get('FIELD_2')->getValue();
        break;

      case 'FILTER_2':
        $field = $row->_entity->get('FIELD_3')->getValue();
        break;

      default:
        break;
    }

    $target_id = $field[0]['target_id'];
    $term = MYMODULE_get_referenced_term($target_id);
    if (!empty($term)) {
      $term_name = $term->getName();
      $term_id = $term->id();
      if (!in_array($term_name, $options_array)) {
        $options_array[$term_id] = $term_name;
      }
    }
  }

  asort($options_array);

  return $options_array;
}

/**
 * Returns a term if in scope
 *
 * @param int $tid
 *
 * @return object
 */
function MYMODULE_get_referenced_term($tid = 0) {
  if (empty($tid)) {
    return;
  }

  $term = taxonomy_term_load($tid);

  if (empty($term)) {
    return;
  }

  return $term;
}

/**
 * Get the view result filtered by the arguments passed
 *
 * @param string $viewname
 * @param string $display
 * @param array $args
 *
 * @return boolean|array
 */
function MYMODULE_get_view_result($viewname, $display, $args) {
  // Get the view
  $view = \Drupal\views\Views::getView($viewname);

  if (!$view) {
    // The view could not be loaded. Add a watchdog message and leave the function
    \Drupal::logger('MYMODULE')->error('View @view not found', array('@view' => $viewname));
    return false;
  }

  // set the display machine name
  $view->setDisplay($display);

  // Set arguments for exposed filters
  // Note that you always need to use an array.
  // The arguments are passed in the order as they appear on the view
  $view->setArguments($args);

  // Execute the view
  // This function does not do any access checks on the view. It is the
  // responsibility of the caller to check $view->access()
  $view->preview();

  if (!empty($view->result)) {
    return $view->result;
  }
}

Your Answer

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

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