Hello,

I have 2 content types (event, venue). The content type event has a cck node reference to venue.
Now I have created a view with an exposed filter "node reference venue". Thereby a user can select a venue. As result he gets its events. That works. My problem is, that the selection is offered by a multi select list. Instead I would like to have the nice autocomplete text field.

Therefore my question:

How can I replace in an exposed filter the multi select list by the autocomplete text field??

Thx!!!

Comments

Remon’s picture

Hi slovan,
I faced your problem before, I had two content types (contact, organization). the content type contact has a node reference to an organization. I've created a view to list contacts which exposes an organization filter so I can filter contacts by their organization. I managed to convert organization select list to an autocomplete textfield using the following humble approach:-

1-Modify the select list:

function modulename_form_alter($form_id, &$form)
{
  if($form_id == 'views_filters_contacts_page')
  {
    //Change the type of organization filer to a textfield
    $form['filter1']['#type'] = 'textfield';
    //Set the autocomplete_path which refers to the menu callback that retrieves organization titles depending on the input of the text field
    $form['filter1']['#autocomplete_path'] = 'organization/autocomplete';
 }
}

2- Create menu item

function modulename_menu($may_cache) {
   $items = array(); 
   if ($may_cache) {
    $items[] = array(
    'path' => 'organization/autocomplete',
    'callback' => 'organization_autocomplete',    
    'type' => MENU_CALLBACK,
    'access' => TRUE,
    );
  }
  return $items;
}

3-Create the callback:

//This method return $string matches
function organization_autocomplete($string = '')
{
  $matches = array();
  if($string)
  {
    $result = db_query_range("SELECT nid, title FROM {node} WHERE type = 'content_organization' and LOWER(title) LIKE LOWER('%s%%')", $string, 0, 10);
    while ($organization = db_fetch_object($result)) {
      $matches[$organization->title] = check_plain($organization->title);
    }
  }
  print drupal_to_js($matches);
  exit();
}

after this step assure that your cache are clear.

4-The contacts view used to filter contacts according to organization nid, however, our handler method that returns organization matches, returns organization titles. so, in views_query_later hook we have to replace the supplied organization title with organization nid:

function modulename_views_query_alter(&$query, &$view, $summary, $level) {
 else($view->name == 'contacts_page')
  {
    for ($i = 0; $i < count($query->where_args); $i++)
    {
      if($query->where_args[$i] == 'field_organization_nid')
      {
        //field_organization_nid holds organization title at the moment
        $organization_title = $query->where_args[++$i];
        if($organization_title != '' && $organization_title != "**All**" && $organization_title != "OR")
        {
          $organization_nid = db_result(db_query("SELECT nid FROM {node} WHERE title = '%s'",$organization_title));
          $query->where_args[$i] = $organization_nid;
          break;
        }
      }
    }
  }
}

If you followed this approach your autocomplete textfield should be workin, however, i assure that there is a better approach out there, if you found one, please inform us.
Hope this would be helpful.

sergey_clark’s picture

I have the same problem.
Instead of "contact" content type I have "article" one,
instead of "organization" contenet type I have "author".

First step: In views.module I' ve found function views_form_alter and changed it:

if ($form_id == 'profile_field_form')
{
views_invalidate_cache();
}
// I have added this code
if ($form_id == 'views_filters')
{
$form['filter0']['#type']='textfield';
$form['filter0']['#autocomplete_path']='author/autocomplete';
}

And, really, I saw the result - the type of widget was changed to autocomplete text field.

Than I've taken the second step changing the function views_menu in views.module:

function views_menu($may_cache) {
   $items = array();
   if ($may_cache) {
    $items[] = array(
    'path' => 'author/autocomplete',
    'callback' => 'author_autocomplete',   
    'type' => MENU_CALLBACK,
    'access' => TRUE,
    );
  }
/* I've commented this 
if ($may_cache)
{
views_load_cache();
if (arg(0) == 'admin' && arg(2) =='modules')
{
views_invalidate_cache();
}
views_menu_standard_items($items);
}
else
{
views_menu_inline_items($items);
}
*/


  return $items;
}

Now when I am trying to input something in my autocomplete field for authors I get a 404 mistake: http://mysite/author/autocomplete not found.
What should I do to fix it?

Remon’s picture

1- You should NEVER modify core or contrib modules, the changes that you have made to views.module could be inserted in form_alter of any other module of yours. SO, if you have no modules of yours, you could create a new module and supposed that it's name is [xmodule.module],
you would define a form_alter function like this:

<?php
function xmodule_form_alter($form_id, &$form)
{
  if ($form_id == 'views_filters')
  {
    $form['filter0']['#type']='textfield';
    $form['filter0']['#autocomplete_path']='author/autocomplete';
  }
}
?>

2- I think that the menu item you have added is ok, but, I'd like you to be informed that the callback (author_autocomplete) should be defined in your module, this function will be resposible for retrieving the values that matches textfield's input.

3- From your code I think that you want to fill the autocomplete field with author names, so your callback could be like this:

<?php
//This method return $string matches
function author_autocomplete($string = '')
{
  $matches = array();
  if($string)
  {
    $result = db_query_range("SELECT nid, title FROM {node} WHERE type = 'content_author' and LOWER(title) LIKE LOWER('%s%%')", $string, 0, 10);
    while ($author = db_fetch_object($result)) {
      $matches[$author->title] = check_plain($author->title);
    }
  }
  print drupal_to_js($matches);
  exit();
}
?>

4-The articles view is used to filter articles according to authors nid, however, our handler method that returns authors matches, returns authors titles. so, in views_query_later hook we have to replace the supplied author's title with author's nid:

<?php
$j = array_search('field_author_nid', $query->where_args);
if($j)
  {
   $author_title = $query->where_args[++$j];
  
   if($author_title != '' && $author_title != "**All**" && $author_title != "OR")
    {
     $author_nid = db_result(db_query("SELECT nid FROM {node} WHERE title = '%s'",$author_title));
     $query->where_args[$j] = $author_nid;
  }
}
?>

Hope this would be helpful.

sergey_clark’s picture

Thanks a lot, Raemon for your help,
now I've done all in a separate module named views_filter_autocomplete.module.
My code is:

function views_filter_autocomplete_form_alter($form_id, &$form)
{
    if($form_id == 'views_filters')
    {
	$form['filter0']['#type']='textfield';
	$form['filter0']['#autocomplete_path']='author/autocomplete';
    }
}

function views_filter_autocomplete_menu($may_cache)
{
    $items = array();
    if ($may_cache)
    {
	$items[] = array(
	    'path' => 'author/autocomplete',
	    'callback' => 'author_autocomplete',
	    'type' => MENU_CALLBACK,
	    'access' => TRUE,
	);
    }
    return $items;
}

function author_autocomplete($string = '')
{
    $matches = array();
    if ($string)
    {
	$result = db_query_range("SELECT nid, title FROM {node} WHERE type = 'author' and LOWER(title) LIKE LOWER ('%s%%')", $string, 0, 10);
	while ($author = db_fetch_object($result))
	{
	    $matches[$author->title] = check_plain($author->title);
	}
    }
    print drupal_to_js($matches);
    exit();
}

function views_filter_autocomplete_views_query_alter(&$query, &$view, $summary, $level)
{
    $j = array_search('field_author_nid', $query->where_args);
    if ($j)
    {
	$author_title = $query->where_args[++$j];
	if ($author_title != '' && $author_title != "**All**" && $author_title != "OR")
	{
	    $author_nid = db_result(db_query("SELECT nid FROM {node} WHERE title = '%s'",$author_title));
	    $query->where_args[$j] = $author_nid;
	}
    }
}

Now I have no mistake messages, but my autocomplete list is empty (after text input it tryes to load data).
I've tried to debug sql-queries in phpmysql and I've got a right list of author names.
Maybe you have some ideas for debuging?

sergey_clark’s picture

#sorry, I cant delete this empty post

Remon’s picture

Maybe you should just clear your cache tables.

sergey_clark’s picture

Thank you, Raemon!
Your advise was very helpful for me.
Now my code works fine.
The last (working) version is (for another people who maybe would have close problem).
I have mysql database, so I should use db_query instead of db_query_range.
And also I have some strange problems with query string creation.
So at the end I have created it in simplest way.

function views_filter_autocomplete_form_alter($form_id, &$form)
{
    if($form_id == 'views_filters')
    {
    $form['filter0']['#type']='textfield';
    $form['filter0']['#autocomplete_path']='author/autocomplete';
    //to hide the element upper then autocomplete field
    unset($form['op0']);
    }
}

function views_filter_autocomplete_menu($may_cache)
{
    $items = array();
    if ($may_cache)
    {
    $items[] = array(
        'path' => 'author/autocomplete',
        'callback' => 'author_autocomplete',
        'type' => MENU_CALLBACK,
        'access' => TRUE,
    );
    }
    return $items;
}

function author_autocomplete($string = '')
{
    $matches = array();
    if ($string)
    {
    $query = "SELECT * FROM {node} WHERE type='author' and title like ('%".$string."%')";
    $result = db_query($query);
    while ($author = db_fetch_object($result))
    {
        $matches[$author->title] = check_plain($author->title);
    }
    }
    print drupal_to_js($matches);
    exit();
}

function views_filter_autocomplete_views_query_alter(&$query, &$view, $summary, $level)
{
    $j = array_search('field_author_nid', $query->where_args);
    if ($j)
    {
    $author_title = $query->where_args[++$j];
    if ($author_title != '' && $author_title != "**All**" && $author_title != "OR")
    {
        $author_nid = db_result(db_query("SELECT nid FROM {node} WHERE title = '%s'",$author_title));
        $query->where_args[$j] = $author_nid;
    }
    }
}
Remon’s picture

Congratulations, good to hear.

swolynski’s picture

I realize this is an old thread but I had a similar use case and needed my exposed node reference field in a view to function as an autocomplete field. I'm on drupal 6.x so I went ahead and translated the above solution. I figured I'd post back my translation since this was so helpful. Also, I did a bunch of digging but couldn't find any other solutions to this. If anyone knows of a better way to do this please feel free to pass on the info. I'm new to Drupal so any help would be appreciated...

<?php
// $Id$
/**
 *@file
 * Module name - Views Filter Autocomplete
 * Allows for node reference autocomplete fields in a specific exposed views filter
 *
 * Specific for 'mynodename' nodes & 'myviewname' view
 */

//--administration
function views_filter_autocomplete_menu()
{
    $items = array();
    $items['mynodename/autocomplete'] = array(
        'title' => t('Views Filter Autocomplete'),
        'page callback' => 'mynodename_autocomplete',
        'access arguments' => array('access content'),
        'type' => MENU_CALLBACK
    );
    return $items;
}

//--alter form - change select to textfield & autocomplete
function views_filter_autocomplete_form_alter(&$form, $form_state, $form_id)
{
    //only change for this view name
    $allowed_view_name = 'myviewname';
    
    if($form_id == 'views_exposed_form')
    {
        if (array_key_exists('view', $form_state))
        {
            if (array_key_exists('name', $form_state['view']))
            {
                if ($form_state['view']->name == $allowed_view_name)
                {
                    $form['field_mynodename_nid']['#type']='textfield';
                    $form['field_mynodename_nid']['#autocomplete_path']='mynodename/autocomplete';
                    $form['field_mynodename_nid']['#size']='60';
                    
                    /**
                    * add allowed options:
                    * this is used to get rid of the error generated from form validation -
                    * 'An illegal choice has been detected. Please contact the site administrator.'
                    * I have not tested for n-many nodes but this will most likely break down
                    * for large amounts of nodes
                    */
                   $options = Array();
                   $query = "SELECT title, nid FROM {node} WHERE type='mynodename'";
                   $result = db_query($query);
                   $options["All"] = '';
                   while ($mynodename = db_fetch_object($result))
                   {
                       $options[$mynodename->title] = $mynodename->nid;
                   }
                   $form['field_mynodename_nid']['#options'] = $options;

                }
            }
        }
    }
}

//--process autocomplete - query db for matches and send back to form for display
function mynodename_autocomplete($string = '')
{
    $matches = array();
    if ($string)
    {
        $string = trim($string);
        {
            //matches from the beginning of the title - use '%%%s%%' to match from anywhere in the title
            $query = "SELECT title FROM {node} WHERE type='mynodename' and title like '%s%%'";
            $result = db_query_range($query,$string,0,10);
            if ($result != false)
            {
                while ($mynodename = db_fetch_object($result))
                {
                    $matches[$mynodename->title] = check_plain($mynodename->title);
                }
            }
        }
    }
    print drupal_to_js($matches);
    exit();
}

//--change titles from nodes into node id's for db query
function views_filter_autocomplete_views_query_alter(&$view, &$query)
{
    if (is_array($query->where)){
        if (count($query->where) >= 1)
        {
            $index_value = array_search("node_data_field_mynodename.field_mynodename_nid = '%s'", $query->where[0][clauses]);
            
            if ($index_value)
            {
                $mynodename_title = $query->where[0][args][$index_value];
                if ($mynodename_title != '' && $mynodename_title != "**All**" && $mynodename_title != "OR")
                {
                    $mynodename_nid = db_result(db_query("SELECT nid FROM {node} WHERE title = '%s'",$mynodename_title));
                    $query->where[0][args][$index_value] = $mynodename_nid;
                }
            }
        }
    }
}
?>

Thanks.

ccshannon’s picture

subscribe.

Heilong’s picture

Subscribe...

momper’s picture

subscribe

killua99’s picture

subscribe...

[at]Killua99 ~~

syakely’s picture

So I just had to do the same thing for a Content Taxonomy Field.
Thank you swolynski for posting. I had some 'An illegal choice' errors to work through, on my version, I prefill the field with All, and have it go away when user selects the box. Also if the user puts in an illegal choice, I give them a drupal message saying that they have put in a choice that is not valid, and again prefill the box with All.

<?php

function mymodule_menu() {
  $items = array();
  $items['mymodule/autocomplete'] = array(
    'title' => t('mymodule'),
    'page callback' => 'mymodule_autocomplete',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK
    );
  return $items;
}

//--alter form - change select to textfield & autocomplete
function mymodule_form_alter(&$form, $form_state, $form_id) {
//only change for this view display exposed form.
//drupal_set_message($form['#id']);
  //my exposed view pages to use it on.
  $my_views_ids = array('views-exposed-form-mymodule-page-1', 
                        'views-exposed-form-mymodule-page-2', 
                        'views-exposed-form-mymodule-page-3',
                        'views-exposed-form-mymodule-page-4'
                        ); 

  if (in_array($form['#id'], $my_views_ids)) {  
    //drupal_set_message($form['#id']);
    if (array_key_exists('view', $form_state)) {
      if (array_key_exists('name', $form_state['view'])) {          
          global $send_message_once;
          $options = Array();
          $vocab = (taxonomy_get_tree(13));
          $options["All"] = '';
          foreach ($vocab as $term) {
           $options[$term->name] = $term->tid;
          }
          $exposed_filter_values = ($form_state['view']->get_exposed_input());
          if (!($exposed_filter_values['field_myfield_value'])) {
            $form['field_myfield_value']['#value']='All'; 
            $form['field_myfield_value']['#attributes']= array('onfocus' => 'this.value=""');
          }
          else {
           if (!(array_key_exists(($exposed_filter_values['field_myfield_value']), $options))) {
             if (!($send_message_once)) {
               drupal_set_message('There are no Operators named "'. $exposed_filter_values['field_myfield_value'] .'"');
               $send_message_once=TRUE;
              }
             $form['field_myfield_value']['#value']='All'; 
             $form['field_myfield_value']['#attributes']= array('onfocus' => 'this.value=""');
           }
          }
          $form['field_myfield_value']['#type']='textfield';
          $form['field_myfield_value']['#autocomplete_path']='mymodule/autocomplete';
          $form['field_myfield_value']['#size']='28'; 
          $form['field_myfield_value']['#options'] = $options;
      }
    }
  }
}

//--process autocomplete - query db for matches and send back to form for display
function mymodule_autocomplete($string = '') {
  $matches = array();
  if ($string) {
    $string = trim($string);
    {
      //matches from the beginning of the title - use '%%%s%%' to match from anywhere in the title
      $query = "
        SELECT term_data.tid AS tid,
        term_data.name AS term_data_name,
        term_data.vid AS term_data_vid
        FROM term_data term_data 
        LEFT JOIN vocabulary vocabulary ON term_data.vid = vocabulary.vid
        WHERE vocabulary.vid = '13' and term_data.name like '%s%%'
      ";
      
      $result = db_query_range($query,$string,0,10);
      if ($result != false) {
        while ($mytermname = db_fetch_object($result)) {
          $matches[$mytermname->term_data_name] = check_plain($mytermname->term_data_name);
        }
      }
    }
  }
  print drupal_to_js($matches);
  exit();
}

//--change term names to tids.
function mymodule_views_query_alter(&$view, &$query) {
  if (is_array($query->where)){
    if (count($query->where) >= 1) {
      $index_value = array_search("node_data_field_myfield.field_myfield_value = '%s'", $query->where[0][clauses]);
      if ($index_value) {
        $mynodename_title = $query->where[0][args][$index_value];
        if ($mynodename_title != '' && $mynodename_title != "**All**" && $mynodename_title != "OR") {
          $mynodename_nid = db_result(db_query("SELECT term_data.tid AS tid FROM term_data term_data WHERE (term_data.vid in ('13')) AND (term_data.name = '%s')",$mynodename_title));
         $query->where[0][args][$index_value] = $mynodename_nid;
        }
      }
    }
  }
}


?>
weekbeforenext’s picture

I think I figured out an easier way to avoid the 'An illegal choice has been detected. Please contact the site administrator.' error message. Instead of generating the allowed options, remove #options using unset(). Without #options, the validation doesn't require that the user selected a valid option.

If they entered text that will not match a node id, I set the node id to 0 in the query. This makes it work properly, returning no results without throwing errors.

Here's my .module code for Drupal 6.x:

// $Id: MODULE_NAME.module

/**
*@file
* Module Name
* Turns a select into an autocomplete textfield.
*
* Specific for 'NODE_NAME' nodes and the 'VIEW_NAME' view 
* 
*/

/**
* Implementation of hook_menu().
*/
function MODULE_NAME_menu(){
	
//Initialize the $items array.
    $items = array();
	
//Add menu for the node autocomplete textfield's #autocomplete_path.
    $items['NODE_NAME/autocomplete'] = array(
//Required. The untranslated title of the menu item.
        'title' => t('Node Title'),
//The function to call to display a web page when the user visits the path. If omitted, the parent menu item's callback will be used instead.
        'page callback' => 'NODE_NAME_autocomplete',
//An array of arguments to pass to the access callback function. Integer values pass the corresponding URL component.
        'access arguments' => array('access content'),
//Callbacks simply register a path so that the correct function is fired when the URL is accessed.
        'type' => MENU_CALLBACK
    );
	
//Return the $items array with the new menu item.
    return $items;
}

/**
* Implementation of hook_form_alter().
*/
function MODULE_NAME_form_alter(&$form, $form_state, $form_id){

//Only affect a views exposed form.
    if($form_id == 'views_exposed_form'){
		
//If a view exists, retreive form_state information.
        if (array_key_exists('view', $form_state)){
			
//If the view has a name,
            if (array_key_exists('name', $form_state['view'])){
				
//Only affect the specified view.
                if ($form_state['view']->name == 'VIEW_NAME'){
					
//Turn the node select into a textfield.
                    $form['SELECT_FIELD']['#type'] ='textfield';
//Turns the node textfield into an autocomplete textfield.					
                    $form['SELECT_FIELD']['#autocomplete_path'] ='NODE_NAME/autocomplete';
//Remove options from the node autocomplete textfield so errors aren't thrown if the user enters something that doesn't exist.
					unset($form['SELECT_FIELD']['#options']);
                }
            }
        }
    }
}

/**
* Autocomplete Function for nodes.
*/
function NODE_NAME_autocomplete($string = ''){
	
//Initialize the $matches array.
    $matches = array();
	
//Ensure that a string has been passed.	
    if ($string){
		
//Remove any extra spaces before or after the meaningful string.		
        $string = trim($string);
		
//Set the query to select all nodes that are published and match what the user entered. - use '%%%s%%' to match from anywhere in the title
		$query = "SELECT title FROM {node} WHERE type='NODE_NAME' and title like '%s%'";
//Run the query and restrict the results to the first 10.
		$result = db_query_range($query,$string,0,10);
		
//Ensure there are results returned from the query.		
		if ($result != false){
			
//Loop through the all of the results.
			while ($NODE_NAME = db_fetch_object($result)){
				
//Add the plain text select field to the $matches array with the $key set as the same.			
				$matches[$NODE_NAME->title] = check_plain($NODE_NAME->title);
			}
		}
    }

//Converts the $matches array into its Javascript equivalent and prints it to the page output.		
    print drupal_to_js($matches);
//Exit the function.	
    exit;
}

/**
* Implementation of hook_views_query_alter().
*/
function MODULE_NAME_views_query_alter(&$view, &$query){
	
//Ensure that the WHERE information exists as an array in the query which is not empty.
    if (is_array($query->where) && !empty($query->where)){

//Search the where clauses for the select field clause.
		$index_value = array_search("node_data_SELECT_FIELD.SELECT_FIELD = '%s'", $query->where[0][clauses]);

//If the select field clause was found,		
		if ($index_value != '' && $index_value != false){

//If the cooresponding argument is not blank or all,
			if($query->where[0][args][$index_value] == '' || strtolower($NODE_NAME_title) == "all"){

//remove the clause and argument because the user isn't searching by the select field.
				unset($query->where[0][clauses][$index_value]);	
				unset($query->where[0][args][$index_value]);	
			}
			else{
//Get the argument that cooresponds to the select field where clause.
				$NODE_NAME_title = strtoupper($query->where[0][args][$index_value]);
				
//Select the node id of the select field argument.
				$NODE_NAME_nid = db_result(db_query("SELECT nid FROM {node} WHERE title = '%s'",$NODE_NAME_title));
				
//If a node id was found for the select field argument,
				if($NODE_NAME_nid){

//replace the select field with the cooresponding node id in the argument.
					$query->where[0][args][$index_value] = $NODE_NAME_nid;
				}
				else{
//If the node id wasn't found, replace the select field with 0 as a nonexistent node id that will not return results.				
					$query->where[0][args][$index_value] = 0;
				}
			}
		}
	}
}
hkvd’s picture

I have implemented an auto-complete text field for usernames using the Drupal forms API as below -

    $form['users'] = array(
        '#type' => 'textfield',
        '#title' => t('Select Users'),
	'#maxlength' => 50,
	'#autocomplete_path' => 'user/autocomplete',
    );

This field only works for selecting a single username. How can I implement a similar auto-complete field which allows selection of multiple users in a comma seperated list?

syakely’s picture

I have done this recently by using taxonomy code example. Mine was for Node Title Auto complete, but I think you'll be able to get it from here.

function _node_title_autocomplete($string) {
  // The user enters a comma-separated list of Titles. We only autocomplete the last title.
  // This code was altered from core taxonomy autocomplete.
  $array = drupal_explode_tags($string);
  // Fetch last Title
  $last_string = trim(array_pop($array));
  $matches = array();
  if ($last_string != '') {
    $result = db_query_range("SELECT node.nid, node.title FROM {node} WHERE LOWER(node.title) LIKE LOWER('%s%%') AND node.type in ('competitor')", $last_string, 0, 10);
    $prefix = count($array) ? implode(', ', $array) .', ' : '';
    while ($node = db_fetch_object($result)) {
      $n = $node->title;
      // Commas and quotes in terms are special cases, so encode 'em.
      if (strpos($node->title, ',') !== FALSE || strpos($node->title, '"') !== FALSE) {
        $n = '"'. str_replace('"', '""', $node->title) .'"';
      }
      $matches[$prefix . $n] = check_plain($node->title);
    }
  }
  drupal_json($matches);
}

kevinsiji’s picture

This feature is badly required. My site have a reference node count of 6000+ (and growing) and having that all loaded in a dropdown list proves to be a suicide attempt.

Kevin & Siji

technikh’s picture

Cheers,
TechNikh

In 30 seconds set up Automated Visual testing of your website. Zero coding. https://drupal.org/project/drulenium
Ever dreamed of styling your view, We have a solution for you. https://drupal.org/project/views_stylizer

pepe84’s picture

Hi there!

I found a simply solution using form hooks inspired by weekbeforenext comment:


define('VIEWS_ALL_WILDCARD', 'All');
define('NODE_AUTOCOMPLETE_PATH', 'node/%/autocomplete');

/**
 * Implementation of hook_menu().
 */
function mymodule_menu() {
    $items = array();
    
    $items[NODE_AUTOCOMPLETE_PATH] = array(
        'title'             => t('Node Title'),
        'page callback'     => 'mymodule_filters_content_type_autocomplete',
        'page arguments'    => array(1),
        'access arguments'  => array('access content'),
        'type'              => MENU_CALLBACK
    );

    return $items;
}

/**
 * Autocomplete function for nodes.
 */
function mymodule_content_type_autocomplete($type, $string = '') {
    // Init matches
    $matches = array();

    if (!empty($string)) {
        $string = trim($string);
        // Set the query to select all nodes that are published and match
        $query = "SELECT nid, title
                  FROM {node}
                  WHERE status = 1 AND type='%s' AND LOWER(title) LIKE LOWER('%s%')";
        // Run the query and restrict the results to the first 5.
        $result = db_query_range($query, $type, $string, 0, 5);
        // Ensure there are results returned from the query.
        if ($result != false) {
            while ($node = db_fetch_object($result)){
                // Add the plain text select field to the $matches array
                $matches[$node->title] = check_plain($node->title);
            }
        }
    }
    // Render as JSON response
    drupal_json($matches);
}

/**
 * Implementation of hook_form_alter()
 */
function mymodule_form_alter(&$form, &$form_state, $form_id) {
    if ($form_id === 'views_exposed_form') {
        $viewName = $form_state['view']->name;
        // Common
        switch($viewName) {
            case "my_view":
                    // Autocomplete form field
                    mymodule_exposed_autocomplete(
                        $form,
                        $form_state,
                        'my_fieldname',
                        'my_fieldname_content_type'
                    );
        }
    }
}

/* GLOBAL var */
$autos = array();

function mymodule_exposed_autocomplete(&$form, &$form_state, $fieldname, $type) {
    // Add fieldname to list
    global $autos;
    $autos[$fieldname] = $fieldname;
    // Transform original filter
    $path = str_replace('%', $type, NODE_AUTOCOMPLETE_PATH);
    $form[$fieldname]['#type'] = 'textfield';
    $form[$fieldname]['#autocomplete_path'] = $path;
    unset($form[$fieldname]['#options']);
    // If value is a nid transform it to title
    $value = $form_state['view']->exposed_input[$fieldname];
    if (is_numeric($value)) {
        $title = db_result(db_query("SELECT title FROM {node} WHERE nid = %d AND type = '%s'", $value, $type));
        $form[$fieldname]['#value'] = $title;
    }
    // Save node content type for title search
    $form_state['noderef'][$fieldname] = $type;
    // Fill original filter using fake one through form validate hook
    array_unshift($form['#validate'], 'mymodule_exposed_autocomplete_validate');
}

function mymodule_filters_exposed_autocomplete_validate($form, &$form_state) {
    global $autos;
    // Validate autocomplete searches
    foreach($autos as $auto) {
        if (!empty($form_state['values'][$auto]) ) {
            // Retrieve nid using title
            $title = $form_state['values'][$auto];
            // Wildcard?
            if ($title == VIEWS_ALL_WILDCARD) break;
            // Search node
            $type = isset($form_state['noderef'][$auto]) 
                    ? $form_state['noderef'][$auto] : null;
            $query = "SELECT nid FROM {node} WHERE LOWER(title) = LOWER('%s')" .
                (empty($type) ? "" : " AND type = '%s'");
            $nid = db_result(db_query($query, $title, $type));
            if ($nid) {
                $form_state['values'][$auto] =
                $form_state['clicked_button']['#post'][$auto] =
                $form_state['view']->exposed_input[$auto] = $nid;
            } else {
                form_set_error(
                    $auto,
                    t('@name not found', array('@name' => '"' . $title . '"'))
                );
            }
        } else {
            // Reset to show all results
            unset($form_state['values'][$auto]);
            unset($form_state['clicked_button']['#post'][$auto]);
            unset($form_state['view']->exposed_input[$auto]);
        }
    }
}

I hope this would be useful and as TechNikh says, maybe this code has to be moved to Views Autocomplete Filters project.

Regards,

SchwebDesign’s picture

i'm hoping to implement this for a Views 3.x exposed filter allowed values (or not?) select list (transformed to autocomplete textfield of course). I've been researching and there are lots of bits of code around for how to do this with Views 2.x as well as Views Autocomplete Filters module... but i cannot find anything related to how to do adjust the code like the above example in Views 3.x for a cck field. Could anyone help me out? Am i missing something?

I'd also be more than happy to pay someone $$ to help me get a working Views exposed filter cck autocomplete widget for views 3.x

-Cameron
Web Designer & Developer
Website Design and Development