Modify views filters programatically

We encourage users to post events happening in the community to the community events group on https://www.drupal.org.
josepvalls's picture

Hello.
I have a question and don't know exactly where to ask it. I've been reading some documentation but everything looks a little scattered.
I have a view with a Content Taxonomy field as a filter. It displays a nice drowpdown (force single is selected) and all works fine.
I like how a dropdown is displayed but sometimes I may need to allow selecting multiple terms.
What I did was to uncheck the force single which rendered a multiselect list and then I rendered my own dropdown widget on hook_preprocess_views_exposed_form.
When I selected items it did not filter anything as because in the views configuration it is a non force single, it seems to expect an array of parameters nevertheless.
Is there any way to have a dropdown and when some param is passed it changes the internal filtering to not force single? I've been digging around and came across hook_views_pre_build but can't find where the filter options and values are set.
Right now I'm already using hook_views_pre_build do remove columns when a filter is set (works beautifully).
I will attach my code just as reference. Any comments welcome.

<?php
//this is a theme function
//gets my nice dropdown back
function referup_preprocess_views_exposed_form(&$vars, $hook) {
   switch(
$vars['form']['#id']){
      case
'views-exposed-form-empleo-page-1':
     case
'views-exposed-form-empleo-page-2':
     case
'views-exposed-form-empleo-page-3':
     case
'views-exposed-form-empleo-page-4':
     case
'views-exposed-form-empleo-page-5':
     case
'views-exposed-form-empleo-page-6':
        
           unset(
$vars['form']['field_oferta_industria_value']['#processed']);
     unset(
$vars['form']['field_oferta_industria_value']['#defaults_loaded']);
      unset(
$vars['form']['field_oferta_industria_value']['#printed']);
      unset(
$vars['form']['field_oferta_industria_value']['#options']);
        
$vars['form']['field_oferta_industria_value']['#multiple'] = false;
         
$vars['form']['field_oferta_industria_value']['#size'] = 1;
         
$widget = $vars['widgets']['filter-field_oferta_industria_value'];
        
//$widget->widget = drupal_render($vars['form']['field_oferta_industria_value']);
     
drupal_set_message(var_export($vars['form']['field_oferta_zona_value'],true),'info');
// wont filter because if i uncheck the force single option i will be expecting a parameter as an array
// /empleo?field_oferta_industria_value[]=41&field_oferta_industria_value[]=17&field_oferta_industria_value[]=7
?>

<?php
function valls_support_views_pre_build(&$view) {
//removes a column if the filter for that column is set
 
if($view->name=='empleo'){
        if(isset(
$_GET['field_oferta_industria_value']) && $_GET['field_oferta_industria_value']!='All' && !is_array($_GET['field_oferta_industria_value']))){
      
// remove view hander properties for column
     
unset(
       
$view->display['default']->handler->options['fields']['field_oferta_industria_value'],
       
$view->display['default']->handler->options['style_options']['columns']['field_oferta_industria_value'],
       
$view->display['default']->handler->options['style_options']['info']['field_oferta_industria_value']
      );
      }
?>

Comments

Example in Node Relationships module

markus_petrux's picture

In Node Relationships module I'm changing the view filters dynamically, based on several factors. Maybe you can look at the code and see if the same method I use can help you here as well.

In summary, I doing this dynamic configurations of the view through custom implementation of hook_views_pre_view().

You can get the list of current filters using $view->display_handler->get_option('filters'). And you can enable a new set of filters using $view->display_handler->override_option('filters', $definition).

See functions noderelationships_customize_noderef_view() and noderelationships_customize_backref_view() that can be found in the file noderelationships.inc of the N.R. module.

Thank you. Works

josepvalls's picture

Thank you. Works beautifully!

<?php
function valls_support_views_pre_view(&$view) {
   if(
$view->name=='empleo'){
        if(
$industrias=valls_support_get_filters()){
//$industrias is an array
        
$new_view_filters = $view->display_handler->get_option('filters');
         if (empty(
$new_view_filters)) {
               
$new_view_filters = array();
           }
             
$new_view_filters['field_oferta_industria_value'] = array(
                  
'id' => 'field_oferta_industria_value',
                
'table' => 'node_data_field_oferta_industria',
                 
'field' => 'field_oferta_industria_value',
                 
'operator' => 'or',
                
'value' => $industrias,
                  
'group' => '0',
                
'exposed' => FALSE,
                  
'relationship' => 'none',
               );
        
$view_overrides = array();
        
$view_overrides['filters'] = $new_view_filters;
          foreach (
$view_overrides as $option => $definition) {
              
$view->display_handler->override_option($option, $definition);
           }
      }
  }
}
?>

This doesn't work when AJAX

Alexander Matveev's picture

This doesn't work when AJAX turned on =(

Basic Question

infoloscar's picture

Hi guys, I´m starting to develop in Drupal and I need something like this. In my site I´m getting the Zipcode of the logged user and I need to use this value on the Event (new content type) View Filter, the question is the following: what files I need to modify to make this change?

FluxSauce's picture

<?php
/**
* Implements hook_views_pre_view.
*/
function MODULE_views_pre_view(&$view) {
  switch (
$view->name) {
    case
'NAME': {
     
// Do not show filter for ROLE NAME.
     
global $user;
      if (
in_array('ROLENAME', $user->roles)) {
       
$view_filters = $view->display_handler->get_option('filters');
        unset(
$view_filters['FILTERID']);
       
$view->display_handler->override_option('filters', $view_filters);
      }
      break;
    }
  }
}
?>

Excellent Perfect for single

swap20's picture

Excellent

Perfect for single role, how to achieve this for multiple roles.

Thank you

Any solution for views

holdmann's picture

Any solution for views 3?

Thanks to reply.

Views 3

swim's picture

FYI the example above should work for views 3.

An example from one of my projects using dependent filters. Normally you would just target the filter field then value array.

  $tid = _MODULE_views_handle_argument($_SERVER['REQUEST_URI']);
  // Change dependent filter tid on arg.
  $view_filters = $view->display_handler->get_option('filters');

  if (isset($tid)) {
    $view_filters['views_dependent_filter']['controller_values'] = $tid;
  }
  $overrides = array();
  $overrides['filters'] = $view_filters;
  foreach ($overrides as $option => $definition) {
    $view->display_handler->override_option($option, $definition);
  }

Consider using hook_views_pre_execute()

cjoy's picture

If you came here looking for a way to programmatically alter a views filter (and not just the exposed filters), this may be useful:

Using hook_views_pre_view() to alter a filter did not affect the view results for me.
@Albright kindly pointed out that the query has already been built when hook_views_pre_view() is called and suggested using hook_views_pre_execute() instead. Worked for me:

/**
* Implements hook_views_pre_execute().
*/
function MODULE_views_pre_execute(&$view) {
  if ($view->name == 'VIEW_NAME') {
    $view_filters = $view->display_handler->get_option('filters');

    // ...
    // $views_filters contains an array of all filters, modify as needed.
    // ...

    $view->display_handler->override_option('filters', $view_filters);
  }
}

That did not work for me

erald's picture

That did not work for me either. Not sure why but used:
HOOK__views_query_alter(&$view, &$query)

Changed the filters there without any problems.

I

Marko B's picture

I used

views_pre_build(&$view)

without a problem to change values of filters (not exposed).

Drupal Specialist at http://adriadrop.com/

rhuffstedtler's picture

First, a quick clarification, hook_views_pre_view() does not execute after the query is built. According to the comment on https://api.drupal.org/api/views/views.api.php/group/views_hooks/7, hook_views_pre_view is actually the first to fire. That seems to match what I'm seeing in my experiments.

That said, the problem was unrelated to that. After some experimentation, I decided that hook_views_pre_build was the best place for me to do what I needed to do. I ended up with this block of code. It works fine if the filter is single select, but if I make it multiselect, I can see the changed filter when I dpm it, but it looks like the query that is executed still builds off the original filter.

/**
* Implements hook_views_pre_build
*
* This is to set the default filter options for the My Pages view to the uid of
* the currently logged in user
*
* For some reason, this works if the filter allows single select, but does not if
* it allows multi-select
*/
function inventory_app_views_pre_build(&$view) {
watchdog("my pages", "In views_post_build");
global $user;
if ($view->name == 'my_pages') {
$query_params = drupal_get_query_parameters();
if (!isset($query_params['field_assignee_target_id'])) {
//$view->display_handler->options['filters']['field_assignee_target_id']['value'] =
// array($user->uid => $user->uid);
$view_filters = $view->display_handler->get_option('filters');
$view_filters['field_assignee_target_id']['value'] =
array($user->uid => $user->uid);
$view->display_handler->override_option('filters', $view_filters);
dpm($view->display_handler, "AFTER OVERRIDE");
}
}
}

I've opened an issue for this, on the off chance that it is useful to anyone else: https://www.drupal.org/node/2546981

to simply modify the label of

liquidcms's picture

to simply modify the label of an exposed filter, i did this:

<?php
function mymodule_views_pre_build(&$view) {
  if (
$view->name == 'myview') {
    if (
$view->current_display == 'mydisplay') {
      
$view->filter['field_myfield']->options['expose']['label'] = 'XYZ';
    }
  }
}
?>

In case anyone is looking for

John Pitcairn's picture

In case anyone is looking for this for Drupal 8, here's how I modify a default exposed sort. The exposed input includes filters.

<?php

use Drupal\Views\ViewExecutable;

/**
* Implements hook_views_pre_execute().
*/
function mymodule_views_pre_build(ViewExecutable $view) {
  if ($view->id() != 'search') {
    return;
  }
  $filters = $view->getExposedInput();
  // Sort by title if there are no keywords or user sort.
  if (empty($filters['keys']) && empty($filters['sort_by'])) {
    $filters['sort_by'] = 'title';
    $view->setExposedInput($filters);
  }
}

I am trying to set the

Wisamx's picture

I am trying to set the exposed filter (category_id) dynamically but I have failed:

function mymodule_views_pre_build(Drupal\views\ViewExecutable $view) {

$category_id = 5;

//First Try
$view_filters = $view->display_handler->getOption('filters');
$view_filters['field_category_target_id']['value'][$category_id] = $category_id;
$view->display_handler->setOption('filters', $view_filters);

//Second Try
$filters = $view->getExposedInput();
$filters['field_category_target_id'] = $category_id;
$view->setExposedInput($filters);

}

could anybody help me