I've got an exposed filter in a view for Node Type. I'd like to override the exposed text field with the results of a query displayed in a select field. I've got a test module created with hook_form_alter. I'm having partial success, but am creating an error condition, "An illegal choice has been detected." I'm thinking that there's some form element that I'm not dealing with, but can't figure it out.

Is it possible to unset and override a form element in this manner?

thanks,

Peter

function psm_customsite_form_alter(&$form, &$form_state, $form_id) {
  // print $form_id;
  switch ($form_id) {
    case 'views_exposed_form':

	// First we pull the SQL statements and array values together
	$sql = 'SELECT  DISTINCT title from {node} WHERE  type = "tpp_titles" ORDER BY title';
		$results = db_query($sql); 
	// Run the query
		while ($fields = db_fetch_array($results)) { // Get the next result as an associative array
		foreach($fields as $key => $value) { // Iterate over all of the fields in this row
		$titles[] = $value; // creates a numerically indexed array with the correct values
		}
	}
	$keys = array_values($titles);  // we want the key equal to value for select menu, replace numerical index
	$values = array_values($titles);
	$titles = array_combine($keys, $values);

	unset($form['title']);
	
	// override form['title']
     $form['title']= array(
	  '#type' => 'select', 
	  '#title' => t('Title Name'), 
	  '#default_value' => '',
	  '#options' => $titles,
	  '#description' => t('Title names from the Title and Pay Plan.'),
	'#weight' => '-3'
	);
     $form['submit']['#value'] = t('Search'); // changed from Apply to Search
    // Create a reset button
    $form['reset'] = array(
    '#type' => 'markup',
    '#value' => '<input class="form-button" type="reset" value="Reset" onClick="javascript:window.location=\'/'.
    $form_state['view']->display[$current_display]->display_options['path'] .'\';" />',
      );
    break;

Comments

stevepofd’s picture

peter,
it looks like you are onto something with this, i couldn't figure it out and i gave up. i found a similar way with taxonomies. in which i let users define words in a vocabulary and then i could expose the taxonomy and choose a select box for filtering the results.
however, i would still prefer to understand the real way, so maybe you have sparked some inspiration to see if i can help with what you've got.
please let us all know if you get something as in my opinion the community would benifit a lot.

take care,
steve

dboune’s picture

<?php
  function MODULENAME_form_alter(&$form, &$form_state, $form_id) {
    switch($form_id) {
      case 'views_exposed_form':
        if (isset($form['qmf'])) {
          $content_field = content_fields('field_product_manufacturer');
          $db_info = content_database_info($content_field);
          $result = db_query("SELECT DISTINCT " . $db_info['columns']['value']['column'] . " FROM {" . $db_info['table'] . "}");
          
          $options[""] = "<Any>";
          while($row = db_fetch_array($result)) {
            $value = $row[$db_info['columns']['value']['column']];
            $options[$value] = $value;
          }
           
          $field = &$form['qmf'];
          unset($field['#size']);
          $field['#type'] = "select";
          $field['#options'] = $options;
          $field['#default_value'] = $content_field['widget']['default_value']['value'];
          
          $form['submit']['#value'] = t('Search');
        }          
      break;       
    }
  }  
?>
doublejosh’s picture

don't recognize that, it's not in the API, and it seems to be what's keeping me from getting the values for the select list.
thanks.

n/m

dboune’s picture

See: http://api.freestylesystems.co.uk/api/function/content_fields

Been there since drupal 5.

Edit: I just realized maybe you are trying to do this with non-cck fields.. which of course, won't work with my code. Sorry, I was looking at only a CCK implementation. If you need to do non-cck fields, you'll need to replace that bit of cck-specific code with something non-cck friendly. If I can get back around to it, I'll add that functionality to this code.

glennnz’s picture

@dboune

What is "qmf"?

Cheers

Glenn

Glenn
THECA Group

aajones’s picture

I couldn't get this code to work...I'm not sure what $form['qmf'] is, as I don't seem to have that value anywhere, and I can't get the code to work that way. As with the code above, replace MODULENAME with your module name, but also replace CCK_FIELD_NAME with the CCK Field's name.

Thanks, dboune, for providing a starting point.

<?php
  function MODULENAME_form_alter(&$form, &$form_state, $form_id) {
    switch($form_id) {
      case 'views_exposed_form':
        if (isset($form)) {
          $content_field = content_fields('CCK_FIELD_NAME',NULL);
          $db_info = content_database_info($content_field);
          $query = "SELECT DISTINCT " . $db_info['columns']['value']['column'] . " FROM {" . $db_info['table'] . "} ORDER BY " . $db_info['columns']['value']['column'] . " ASC";
          $result = db_query($query);
          $options[""] = "<Any>";
          while($row = db_fetch_array($result)) {
            $value = $row[$db_info['columns']['value']['column']];
            $options[$value] = $value;
          }
          $field = &$form['CCK_FIELD_NAME_value'];
          unset($field['#size']);
          $field['#type'] = "select";
          $field['#options'] = $options;
          $field['#default_value'] = $content_field['widget']['default_value']['value'];
          $form['submit']['#value'] = t('Search');
        }
      break;
    }
  }
?>
geshan’s picture

I changed the textbox to select box for a date form and to, but got an error at first An illegal choice has been detected. Please contact the site administrator. How do I get rid of this.

dboune’s picture

Replace all occurrences of "MODULENAME" with the name of your module...

I don't expect this to work in every possible situation. It could be a lot smarter. Or have an interface.. or do your dishes.. for now, just change the values in the MODULENAME_rewrite_field_list to match the identifiers in your views exposed filter and cck fields.

Ultimately, we need this concept *IN* CCK, maybe using CCK widgets appropriately. Until then..

<?php 

  function MODULENAME_menu() {
    $items["MODULENAME/autocomplete/%"] = array(
      'title' => 'Autocomplete for custom module',
      'page callback' => 'MODULENAME_autocomplete',
      'page arguments' => array(2, 3),
      'access arguments' => array('access content'),
      'type' => MENU_CALLBACK,
    );
    return $items;
  }

  function MODULENAME_rewrite_field_list() {
    $fields['qmf'] = array(
      'name' => 'field_product_manufacturer',
      'type' => 'dropdown',  
    );

    $fields['qc'] = array(
      'name' => 'field_product_color',
      'type' => 'autocomplete',
    );

    return $fields;
  }

  function MODULENAME_form_alter(&$form, &$form_state, $form_id) {
    switch($form_id) {
      case 'views_exposed_form': 
        $field_list = MODULENAME_rewrite_field_list();

        foreach($field_list as $field => $field_data) {
          if (isset($form[$field])) {
            $content_field = content_fields($field_data['name']);
            $db_info = content_database_info($content_field);
            $result = db_query("SELECT DISTINCT " . $db_info['columns']['value']['column'] . " FROM {" . $db_info['table'] . "}");

            // Dropdown Field
            if ($field_data['type'] == "dropdown") {
              $options[""] = "<Any>";
              while($row = db_fetch_array($result)) {
                $value = $row[$db_info['columns']['value']['column']];
                $options[$value] = $value;
              }
                   
              unset($form[$field]['#size']);
              $form[$field]['#type'] = "select";
              $form[$field]['#options'] = $options;
            }

            // Autocomplete Field
            else if ($field_data['type'] == "autocomplete") {
              $form[$field]['#autocomplete_path'] = 'MODULENAME/autocomplete/' . $field;
            }
            
            $form[$field]['#default_value'] = $content_field['widget']['default_value']['value'];
       
            // Change the name of the "Apply" button      
            $form['submit']['#value'] = t('Search');
          }
        }
      break;
    }
  }

  function MODULENAME_autocomplete($field, $value) {
    $field_list = MODULENAME_rewrite_field_list();
    if (count($field_list[$field])) {
      $matches = array();

      $content_field = content_fields($field_list[$field]['name']);

      $db_info = content_database_info($content_field);
      $column = $db_info['columns']['value']['column'];   
      $sql = "SELECT DISTINCT `" . $column . "` FROM {" . $db_info['table'] . "} WHERE `" . $column . "` LIKE '%%%s%%'";
      $result = db_query($sql, $value);
      while($row = db_fetch_array($result)) {
        $matches[$row[$column]] = check_plain($row[$column]);
      }
      
      drupal_json($matches);
      exit;
    }

  }
?>
doublejosh’s picture

In D6 there is a great new way to refer to forms...

MY_MODULE_form_ID_OF_FORM_alter(&$form, &$form_state) {

  // This way you don't have to check for the form id inside
  // and saves you from having one giant form alter function in your module.

}
maxoboc’s picture

Hi, this works awesome if you want to override input type of one field in an exposed form and thanx a lot for this code. But what if you would like to override input type of more than one field? For example I would like to change 3 exposed form fields to select lists. I tried to simply copy this portion

$fields['qmf'] = array(
      'name' => 'field_product_manufacturer',
      'type' => 'dropdown', 
    );

3 times and then I changed identifiers to match 3 different fields in exposed form and CCK (as per instructions). All 3 exposed form fields were changed to select lists which is perfect however the values are mixed up. It looks like database query needs to be corrected a bit but I do not know how. Any ideas? Thanx... ;-)

2ndmile’s picture

Thanks for this Damian!!

caspercash’s picture

Does this work in Drupal 7? I tried this one and content_fields() and content_database_info() are all undefined. Would be great if there is also a working code for D7. Thanks!

liquidcms’s picture

of course, if this was a cck textfield and you created a select list by entering a list of values when configuring the field - then when you added a views filter that was the "allowed values" version of the filter for that field, you could expose the filter and it would also be a select list.. :)

mugginsoft.net’s picture

Use a CCK select text field when creating your nodes and then override the select allowed values in the exposed filter block to allow filtering only on existant allowed values.

This variant assumes that you have used keys when constructing your CCK select list.
Also sorts the options array.

 function HOOK_form_views_exposed_form_alter(&$form, $form_state)
{
  $content_field = content_fields('field_make'); // identify your CCK content field in here
  $allowed_values = content_allowed_values($content_field);
  
  $db_info = content_database_info($content_field);
  $result = db_query("SELECT DISTINCT " . $db_info['columns']['value']['column'] . " FROM {" . $db_info['table'] . "}");
  
  $options["All"] = "<Any>";
  while($row = db_fetch_array($result)) {
    $key = $row[$db_info['columns']['value']['column']];
    $options[$key] = $allowed_values[$key];
  }
  asort($options);
  
  $field = &$form['field_make_value_many_to_one']; // identify your select element here
  $field['#options'] = $options;
}
momper’s picture

is this possible with the title of a node instead of a content field?

momper’s picture

? has someone an idea?

manumilou’s picture

This is the code I am using to replace a textfield exposed view filter with a select box. I wanted the dropdown box to contain the titles of all nodes of a certain type.

Paste this code in hook_form_alter. quartier is the content type I gather in the select box.

if ($form['#id'] == 'views-exposed-form-search-block-page-1') {
        // Build the SQL request to get the 'quartier' node titles
        $sql = "SELECT nid, title FROM node WHERE node.type='quartier'";
        // Run the query
        $results = db_query($sql);
        // Fetch the results
        $quartiers[""]=t("");
        while ($fields = db_fetch_array($results)) { 
            $quartiers[$fields['nid']] = $fields['title']; 
        }   
        
        unset ($form['nid']);
        // Override the existing textfield form with a dropdown box
        $form['nid']= array(
                                '#type' => 'select',
                                '#options' => $quartiers
                            );  
        $form['submit']['#value'] = t('Search');
    }   

I was also getting the notorious Illegal choice detected. Please contact your administrator..
Adding the line $quartiers[""]=t(""); fixed the issue.

Manu

masondib’s picture

I was also getting the notorious Illegal choice detected. Please contact your administrator..
Adding the line $quartiers[""]=t(""); fixed the issue.

I was wrestling with this "notorious" error for hours until I finally found this post. Brilliant simple fix. Thanks, Manu.

Replacing it with $quartiers[""]=t("<Any>"); makes it match the default style for Views dropdown filters.

tomrenner’s picture

Funny but true - this line fixed the prob for me, too! Thx for posting! :-)

pasive’s picture

Thanks for idea fixed my problem too.

For me it worked with;

<?php
$quartiers[""]= t("<Any>");
?>
yaworsk’s picture

THANK YOU! I skimmed over this initially and spent an hour or two on this pulling my hair out trying to fix this issue. I never would have thought of passing the value back to the form the same way views is expecting it.

zdean’s picture

This worked great! Is there a way to Sort the results in the dropdown? I'd like to get an alphabetical list of the nodes in the dropdown.

EDIT: nevermind, I just saw the sort code at the start of this this topic.

jh3’s picture

I've added a blank first element to the options array but cannot get rid of this error. Is there something else that needs to be done? I've been trying everything I can find for hours now and I'm having no luck. This seems like it should be so easy to accomplish with the form_alter hook but it's turning out to be ridiculously frustrating.

rar’s picture

The reason it fails is because it is looking for the #value which is set for nothing ("").

Put

 $form['FIELD_NAME_HERE']['#value'] = 'A_KEY_FROM_YOUR_OPTIONS_ARRAY';  

And you don't have to fake add stuff to the array.

P.S. Thanks for the videos they are helpful.

purabdk’s picture

I fixed this issue.
Using following two hooks we can change the defalut value of filter Programatically.

/**
 * hook_views_pre_view
 * @param type $view
 * @param type $display_id
 * @param type $args
 */
function MODULE_NAME_views_pre_view(&$view, &$display_id, &$args) {
  if ($view->name == 'VIEW_NAME') {
    $filters = $view->display_handler->get_option('filters');    
    $view->display_handler->override_option('filters', $filters);
  }
}



/**
 * hook__views_pre_build
 * @param type $view
 * @return type
 */
function MODULE_NAME_views_pre_build($view) { 
  if ($view->name=='VIEW_NAME') {    
    $view->display['page']->handler->handlers['filter']['filter_field']->value['value'] = 8;
    return $view;
  }     
}