jQuery Autocomplete widget for CakePHP 2.2

By | December 20, 2012

A JavaScript autocomplete element that links through to your database isn’t built into CakePHP any more – here is a simple solution to add to your application using a jQuery plugin.
Why a plugin? jQuery doesn’t include this functionality, it is provided by jQuery UI but including that library may be a heavyweight solution for your application.

It should end up looking like the image below – as you type, relevant options from your database will appear in the list beneath.

CakePHP autocomplete widget

You can download the jQuery autocomplete plugin from https://github.com/dyve/jquery-autocomplete
Extract the files from the archive and move the .css and .js files from the src folder into the css and js folders of the CakePHP webroot directory.

Link to the CSS and JavaScript in your default layout:

echo $this->Html->css('jquery.autocomplete');
echo $this->Html->script('jquery.autocomplete.min.js');

Add a small form to your View, in this example there is a model called company and we want to use autocomplete on the name field.

<?php
echo $this->Form->create('Company', array('type' => 'post', 'action' => 'find'));
echo $this->Form->input('name');
echo $this->Form->submit();
echo $this->Form->end();
?>

Add this script block to the bottom of the document which activates the plugin and tells it where to submit AJAX requests to:

<script>
  $(document).ready(function(){  
    $("#CompanyName").autocomplete("/Companies/find.json", {
    minChars: 3
    });
  });
</script>

Modify jQuery.autocomplete.css to look nice with CakePHP default CSS:

.acResults ul li {
	margin: 0px;
	padding: 2px 5px;
	cursor: pointer;
	display: block;
	font: menu;
	overflow: hidden;
	color: #333;
}

Now back to some CakePHP – Your Controller needs a new action which the AJAX requests will be sending data to:

public function find() {
  $this->Company->recursive = -1;
  if ($this->request->is('ajax')) {
    $this->autoRender = false;
    $results = $this->Company->find('all', array(
      'fields' => array('Company.name'),
      //remove the leading '%' if you want to restrict the matches more
      'conditions' => array('Company.name LIKE ' => '%' . $this->request->query['q'] . '%')
    ));
    foreach($results as $result) {
      echo $result['Company']['name'] . "\n";
    }
 
  } else {
  	//if the form wasn't submitted with JavaScript
    //set a session variable with the search term in and redirect to index page
    $this->Session->write('companyName',$this->request->data['Company']['name']);
    $this->redirect(array('action' => 'index'));
  }
}

And finally add some code to the index action in the index action, which handles what happens if the form was submitted via a button press rather than AJAX.

//if there's a session with some data in, add a filter to the search conditions
if ($this->Session->check('companyName')) {
  $name = $this->Session->read('companyName');
  if ($name) {
    $this->paginate['conditions'][] = array('Company.name LIKE' => '%' . $name . '%');
    $this->request->data['Company']['name'] = $name;
  }
}

That’s it.

To to make submit buttons appear alongside form elements
here is a simple bit of jQuery to improve the appearance

$(document).ready(function(){
  $('div.input, div.submit').css({
    'float' : 'left',
    width : '200px',
    clear : 'none'
  });
  $('div.submit').css('margin-top', '14px');
  $('div.input.text').css('margin-top', '-4px');
});

Thanks to the answer on this question from StackOverflow for some inspiration
http://stackoverflow.com/questions/6071828/autocomplete-search-form-cakephp

13 thoughts on “jQuery Autocomplete widget for CakePHP 2.2

  1. Pingback: jQuery Autocomplete widget for CakePHP 2.2 | Richard Willis-Owen - Web 2.0 BLOG | Web 2.0 BLOG

  2. jon

    I could not get this to work on Cake 2.2.4. A few questions:
    1.Where is code converted to json?
    2.I could not find any access to the find action being called in my logs either.

    Reply
    1. Richard

      There is no JSON conversion taking place – the controller action is just echoing out plain text separated by new lines – the autocomplete plugin can deal with this just fine.
      Try using Firebug and look in the ‘Net’ tab, you should see any GET requests plus the responses in there.

      Reply
  3. Kelly

    $(document).ready(function(){
    $("#CompanyName").autocomplete("/Companies/find.json", {
    minChars: 3
    });
    });

    I’m running 2.1.1 atm, but had to change this. I needed to drop the .json off the end (otherwise the controller couldn’t find the function).

    Also, as I was using admin routing, and doing this in an admin page, the url was just the name of the function. ‘/Companies/find’ took it to the root of the application, losing the admin, ‘Companies/find’ meant it was duplicated like /admin/companies/Companies/find. Eventually I just changed it to ‘find’ and it worked!

    Reply
    1. Kelly

      ooh, no. The trailing slash is dependent on if your url finishes with a /

      domain/admin/controller/ = domain/admin/controller/controller/action
      domain/admin/controller = domain/admin/controller/action

      any ideas? I suspect basically admin routing is ballsing it up and I’m not sure how to handle it properly.

      Reply
  4. Daniel

    hi,
    I’m getting this error in console:
    “Uncaught Error: cannot call methods on autocomplete prior to initialization; attempted to call method ‘/Venues/find.json’ ”

    Any ideas?

    Reply
  5. jeferson tadeu de souza

    Please, I’m a lammer that need finish my job with this kind of autocomplete plugin. Like you know, cakephp launch in dropdowns a array with `id` and `name`( for example) , but just shows to us the `name`.
    There is a way to use this plugin ( autocomplete widget cakephp) saving the `id` in the field but searching by the `name` ?
    Thanks to save my job

    Reply
  6. Aguiran

    Just activate the .json extension in routes.php and the output view will automatically convert your data in json

    Router::parseExtensions(‘json’);

    Reply
  7. Jari Kuokka

    When I add more fields to the “‘fields’ => array(‘Company.name’)” -clause like “‘fields’ => array(‘Company.name’, Company.address, Company.postalcode)”, how can I use those fields in form? After I select the company by it’s name, three more input fields would be populated due to selected company id? How to use more information field at the same time?

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *


9 × 2 =