Profile picture for user admin
Daniel Sipos
27 Mar 2013

Have you ever wondered how you can programatically create an autocomplete form element in Drupal? This tutorial will show you how to develop a form that contains a text field autocomplete widget that uses the Drupal Ajax Framework to dynamically retrieve content from your database.

To illustrate this in an example, I have created a table in my database that holds some colors (green, blue, red, etc). In your case this will probably be something more advanced, but you’ll get the point. Moreover, I created a custom page that displays a form with an autocomplete widget that queries the database as you start typing in it:

OK, so this simple example requires three functions: one to register some URL paths with Drupal, one to create the form that houses our autocomplete widget and one that retrieves the data from the database. This is actually the order I am going to write them in, explaining after each what the purpose of the code is.

function demo_menu() {
  $items['demo-autocomplete-test'] = array(
    'title' => 'Test autocomplete',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('demo_form'),
    'access arguments' => array('access content'),
    'type' => MENU_NORMAL_ITEM,
  );

  $items['demo-autocomplete-engine'] = array(
    'page callback' => 'demo_autocomplete',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

This first function implements the Drupal hook_menu(), one of the most useful ones out there. We use it to register with Drupal two URL paths: one for accessing our form that holds the autocomplete widget (demo-autcomplete-test) and one for the AJAX to use as a source for the autocompletion (demo-autocomplete-engine).

Without going too much into how to use this hook, I will make a couple of observations. The first menu item that is declared is given some attributes. We basically say that when a user navigates to www.example.com/demo-autocomplete-test, the respective page should have the title you see, and if the user has the appropriate access permissions, s/he should see the form called demo_form (that we will next create). The second menu item is not used for navigation but to provide the AJAX with a path to use for the autocompletion. You’ll see in a minute what I mean.

Let's see the second function:

function demo_form($form, &$form_state) {
  $form = array();

  $form['colors'] = array(
    '#title' => t('Colors'),
    '#type' => 'textfield',
    '#maxlength' => 60,
    '#autocomplete_path' => 'demo-autocomplete-engine',
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Submit',
  );

  return $form;
}

This function creates our testing form that contains an autocomplete widget. Two form items are declared: a textfield for the colors and a submit button. In order to turn a regular Drupal Form API textfield into an autocomplete widget, we need to give it a special attribute: #autocomplete_path -> the source for the autocompletion data to be run by the AJAX script. This is the path we registered in the previous function and gave it a callback function: demo_autocomplete.

So basically when the user starts typing into this textfield, an AJAX call will be made to that path, triggering this callback function that we now need to declare:

function demo_autocomplete($text) {
  $results = array();

  $query = db_select('colors', 'c');
  $query
      ->condition('c.color', '%' . db_like($text) . '%', 'LIKE')
      ->fields('c', array('color'))
      ->orderBy('color', 'ASC');
  $colors = $query->execute();

  foreach ($colors as $row) {
    $results[$row->color] = check_plain($row->color);
  }

  drupal_json_output($results);
}

I mentioned in the beginning that for this example I created a table to store color names in. Well this function does a simple thing: searches through that table using a wildcard and returns the results in a JSON format for the AJAX call. A parameter is passed to the function ($text) which represents the text string to be searched in the database. So to better understand what’s happening here, imagine the following:

As soon as the user types a letter into the texfield, an AJAX call is made and the search is performed in the table for all the colors that contain that letter. As the user types more letters, more searches are performed and the results displayed like in the image I showed you in the beginning. The cool thing is that this is being done dynamically without page refresh using AJAX.

Hope this was clear and helped you understand how you can use this technique in your development. If you have any questions, feel free to drop a line in the comments.

Profile picture for user admin

Daniel Sipos

CEO @ Web Omelette

Danny founded WEBOMELETTE in 2012 as a passion project, mostly writing about Drupal problems he faced day to day, as well as about new technologies and things that he thought other developers would find useful. Now he now manages a team of developers and designers, delivering quality products that make businesses successful.

Contact us

Comments

Chris 05 Apr 2013 03:34

can't access the first menu_hook page

Hi, I've been struggling to get a working autocomplete field on a form all day, and I've created a very simple example module using just the code you have above. It installs fine on my site, but won't give me access to the actual form... it says 'access denied'. I'm the admin (although not user1) with every access permitted... but still can't get the form to show up -- and wondering why? I've tried rebuilding permissions -- logging in/out -- but still
Access denied
You are not authorized to access this page.

is there something I need to set elsewhere?

thank,
c

Chris 05 Apr 2013 04:40

got it working - almost - by setting access to TRUE

not a great solution long term, but i'm just trying to get a sample one working and then will hammer out the problems. So, by setting the access callback in the menu_hook to TRUE -- it now at least begins to work...

meaning, when I type a letter in the form field -- the blue circle begins to spin... indicating to me that it's trying to run the query.. but then it spins and spins and spins... and eventually times out without retrieving any results....

I've been looking at my code and it matches yours -- but I can't figure out why the results aren't populating the field... any thoughts?

Daniel Sipos 05 Apr 2013 11:56

In reply to by Chris (not verified)

Hey there Chris,

Hey there Chris,

In this article i just mentioned that I created a table that holds the colors. Did you also do this? And did you do it in a way in which the query function I gave as an example here makes sense in your context?

Cheers!

Chris 05 Apr 2013 18:21

I didn't, but I tried

I didn't, but I tried replacing the references to that table with my node table -- selecting for title...

I'll try creating a simple table called colors with a single column called color with a few values ... I presume the table is simply located in the main Drupal database? so I can just use phpmyadmin to create this?

c

Chris 05 Apr 2013 23:08

fixed

thanks -- after reading this, I had a friend look at my code, and he noticed I had incorrectly referenced the callback function...and now it's working perfectly. Thanks for a great submission!

Daniel Sipos 06 Apr 2013 13:49

You are very welcome Chris.

You are very welcome Chris.

Glad it worked out!

James 30 Apr 2014 13:48

element_info() and auto_complete

Would you know how to create demo_element_info() hook to create a custom type:

'#type'=>'demo',

that auto_completes as per your demo?

phelix 08 May 2014 18:16

Search not returning correct results.

I have a quick questions. I was able to use your examples and integrate this into my code and it works and does return results. The only issue that I am having is it doesn't seem to return results that match what I am type in. No matter what I type it always displays all the results, not only the items that match the text. Any idea how or why this would return everything and not just what is matching what I type in the textfield?

Sachin 01 Aug 2016 09:52

Solution for Drupal *

Hi,

Thanks for the tutorial. But I was wondering how this will be achieved in drupal 8. I am new in module development so I am not able to make the above code work for Drupal 8. I am trying to create autocomplete field in my custom form which will fetch data for "City" from taxanomy which I created. Please help me out with this.

Thanks

Pankaj 17 Feb 2017 15:02

Autocomplete with link

Can you please explain how to show result with link and on click node title that go to node page.

function demo_autocomplete($text) {
  $results = array();
 
  $query = db_select('node', 'n');
  $query
      ->condition('n.title', '%' . db_like($text) . '%', 'LIKE')
        ->condition('status',1,'=');
  $nodes = $query->execute();
 
  foreach ($nodes as $row) {
$link  = l($row->title, 'node/'.$row->nid);    
    $results[$row->title] = check_plain($link);
  }
 
  drupal_json_output($results);
}

Is It Ok ?

pierre 23 Oct 2017 08:39

In reply to by Pankaj (not verified)

i did it like this ... not

i did it like this ... not really beautiful but you can improve it with JS

Implement the submit hook

/**
 * Implements hook_form_submit().
 */
function friendsearch_form_submit(&$form, &$form_state) {
    $form_state['redirect'] = '/node/' . trim($form_state['values']['names']);
}

And the foreach you can use this

  foreach ($nodes as $value) {
    $first_name = $value->field_first_name['und'][0]['value'];
    $name = $value->field_name['und'][0]['value'];
    $link  = $value->nid;

    $results[$link] = check_plain($first_name.' '.$name);
  }
Topi 12 Jun 2018 12:49

So Many Thanks!

Many many thanks for this kind article - helped me so much :)

Jerry 20 Sep 2018 15:24

How to add throbber in the textfield

How to add throbber in the textfield

Javi 06 Aug 2021 10:48

Multivalue

Hello and thanks a lot for the tutorial. It is possible to make the field multivalue? For example: to be able to add more than one color separated by commas, as we do it with a normal multivalue field.

Thanks! :D

Add new comment