This is now an outdated tutorial. It has been replaced by a more polished version designed for CakePHP 1.2. Whilst the tutorial you’re looking at now still works in 1.1, you’d be best of upgrading to CakePHP 1.2. It’s much cooler.
Want to get a AJAX-ified live search working in CakePHP? So did I, but struggled to track down the relevant tutorials in English. There are two prominent tutorials out there; one by Marcus Jaschen, and another by Nio. Unless you’re well versed in German or Chinese (respectively) you might struggle to decipher these. I’ve adapted ideas seen in both tutorials and present one in plain (ish) English.
If you want to see what this looks like before getting started, try the demo.
Demo is offline at the moment, waiting to move it over to the new host, sorry!
Livesearch?
If you’re not too sure what Livesearching is, you probably don’t need to be reading this tutorial. A quick overview of a livesearch would describe how search results are presented to you before you’ve finished typing - no need to click submit, just type and watch the results appear. Livesearch is part of the Web 2.0 thing, and has come around thanks to the popularisation of AJAX. To see an example of a livesearch in action head on over to Google Suggest.
Setting up a CakePHP environment
To demonstrate the Cake livesearch we’ll need to set up a very basic environment; it doesn’t really matter what project you use, it’s only one model/controller and a few views.
For demonstrative purposes I’m going to livesearch a table of “users” (original, I know) and have the names of the users returned back. Below is the SQL for the table I’m using:
CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name_first` varchar(20) DEFAULT NULL, `name_second` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`) );
And here are a few names to populate the table - they’re British Prime Ministers if you didn’t know:
INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('7','James','Callaghan'); INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('6','Margaret','Thatcher'); INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('1','Tony','Blair'); INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('5','John','Major'); INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('8','Edward','Heath'); INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('9','Harold','Wilson'); INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('10','Alec','Douglas-Home'); INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('11','Harold','Macmillan'); INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('12','Anthony','Eden'); INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('13','Clement','Attlee'); INSERT INTO `users` (`id`,`name_first`,`name_second`) VALUES ('14','Winston','Churchill');
The next step is to set up the model, this is very, very basic:
/app/models/user.php
Setting up views & layouts
The first thing you’ll need to do is download the prototype and scriptaculous libraries - you can get them from here. You’ll need to extract the scriptaculous.js and prototype.js to /app/webroot/js. So that these can be used, you will need to update the default layout to include these files. In the head tag of your layout, put the following:
link('prototype')); e ($javascript->link('scriptaculous')); ?>
This will ensure the needed Javascript files are included.
The first step here is to create an empty layout, this will just display the content passed to it (which will be the search results) - no >html> tags or anything, just the content:
/app/views/layouts/results.thtml
The next step is to create the search page - this will need a form with an input element as well as some CakePHP/Javascript code to establish the livesearch functionality:
/app/views/users/index.thtml
<h1>Livesearch</h1> <form enctype="application/x-www-form-urlencoded" method="get"> <input id="livesearch" name="livesearch" type="text" /> < ?php echo $html -> image("spinner.gif") ?> 'view', 'url' => '/users/search', 'frequency' => 1, 'loading' => "Element.hide('view');Element.show('loading')", 'complete' => "Element.hide('loading');Effect.Appear('view')" ); print $ajax -> observeField('livesearch', $options); ?> </form> <!-- Results will load here -->
Here we have a form text input called livesearch, a series of hidden div tags for showing/hiding the search spinner and the search results. The $options array outlines a series of settings for our Javascript observer function; specifically what element needs to be updated in the html document (view), the url to submit to (we will create this in the controller on the next page), the frequency in seconds to check the text input and query the controller, and what to do during the loading phase and when the results are passed back. The spinner can be found here - I’m not sure who initially created this or what the license is, but I don’t think you need to worry about using it.
The following view will have the results passed to it by our controller and is responsible for displaying the search results:
/app/views/users/search.thtml (naming must match controller action)
0) { foreach ($result as $user) { print ' '; print $user['users']['name_first'] . ' ' . $user['users']['name_second']; print ' '; } } else { print ' No results found '; } ?>
You should probably know what this does - it just loops through our results (assuming there are some) and prints out the first and second names for said users. If there are no results, print out a polite message.
Setting up the livesearch controller
If you’re reading this you probably have some knowledge of Cake architecture already, and I don’t need to delve into the workings of a controller.
Where we stand thus far is:
- We have a model linking to our user table
- We have all the views/layouts ready to go
- The default view for our controller, index.html, is needing a controller action to submit to, and get results back from
Because we named the search results page search.thtml the controller action needs to be called search - feel free to change this, but don’t forget to update the url value in the $options array in index.html. Let’s take a look at the controller action:
/app/controllers/users_controller.php
This shows the very start of our controller, set up for scaffolding (just for defaults’ sake) and opting to use lots of helpers - we don’t need them all, but it’s generally a safe bet to keep them. We are, of course, most interested in the ajax helper.
Next we need to add an index function to prevent scaffolding taking over:
/app/controllers/users_controller.php
/* .. snip .. */ FUNCTION INDEX() { $this->set('data',$this->User->findAll()); } /* .. snip .. */
The final step is to add a search function which will populate the search.thtml view created earlier.
This function will be called by the form observer in index.thtml - it’s not as complicated as you might think:
/app/controllers/users_controller.php
/* .. snip .. */ FUNCTION search() { $this -> RequestHandler -> setAjax($this); IF (!empty($this->params['form']['livesearch'])) { $word = $this->params['form']['livesearch']; $result = $this -> User -> query("SELECT * FROM users WHERE name_first LIKE '%".$word."%' OR name_second LIKE '%".$word."%'"); $this->set('result', $result); } } /* .. snip .. */
What’s happening here, then? Well, firstly we check to make sure the livesearch field is not blank - if it is CakePHP will knows to use the blank Ajax layout.
Moving on, we do a very basic query of the User model (table users created right at the beginning) - we’re querying for first name or second name which is LIKE the input text.
When we’ve got the results we pass them to a result array variable which will be accessed by the search.thtml view. Remember that CakePHP has a built in layout for dealing with displaying the results of Ajax requests, so we don’t need to worry about that.
Next steps
This has been a very rudimentary demonstration of how to accomplish a livesearch feature in CakePHP - it’s limited and probably not a good idea to use this version without any tweaks. Here’s a few things you might want to address:
- There’s no differentiation between no results and an empty search box, it would be good to clear all messages if the search input is empty.
- The MySQL query is very basic - if you type a complete name in “Firstname Secondname” the query won’t pick it up - there needs to be a few more LIKEs in there. Try looking into the CONCAT() MySQL function.
- It would be great if we could detect when the user has finished typing by setting some form of delay reset - this will prevent the function searching needlessly
- The search string is not checked before it’s sent to the query, opening up endless possibilities of SQL injection attacks.
- Highlight the search string in the results
Hope this has been useful for you - I’d appreciate a quick comment if it has, or if you feel there is room for improvement with this tutorial, in addition to typos and factual errors!
Revisions
13th March
* Modified tutorial to be single page
28nd November
+ Added a demo
* Modified the controller to use RequestHandler component
* Simplified the controller thanks to Daniel’s feedback.
22nd November
+ Updated to include javascript library instructions, thanks Daniel.
* Modified search.thtml to check for variable setting. Again, thanks Daniel.

This is awesome. Saw the link on tutorialicio.us, voted up. Thanks!
Good tutorial. Here some improvements:
It is not necessary to create a special layout, there is already an ajax layout in the core.
The controller can be simplified a bit by using the RequestHandler component: var $components = array(’RequestHandler’); . Then your search function will look like:
[php]
function search() {
$this->RequestHandler->setAjax($this);
if (!empty($this->params['form']['livesearch'])) {
$word = $this->params['form']['livesearch'];
$result = $this->User->query(”SELECT * FROM users WHERE username LIKE ‘%”.$word.”%’”);
$this->set(’result’, $result);
}
}
[/php]
There is a small bug in search.thtml: the first line should be: if (isset($result) && count($result) > 0)
And last but least I would mention that you have to download the prototype library and to include it in the default layout.
I have made an Live Search following your tutor, now I need to add a radio
to the form, so that users can specify the range of search.
I have used $html->radio() to generate a radio, and changed
observeField() to observeForm(), but, this Live Search doesn’t work
anymore, what can I do?
Thanks!
I don’t really understand what I’ve done wrong, since I did everything exactly as you mentoined, but somehow the spinner starts but no results appear. Any idea where I’d have to look?
Mine too. I did everything that was in the tutorial but nothing happen. The spinner is working. I already had a data in my Database User Table. Am I missing something?
Sorry i already know what happen. I think there’s a bug in the code?
‘loading’ => “Element.hide(’view’);Element.show(’loading’)”,
‘complete’ => “Element.hide(’loading’);Effect.Appear(’view’)”
i change the Element.show(’loading’) to Element.show(’view’)
then it work.
hi every body and the author.. i’m new to AJAX and this tutorial is the best i’ve seen so far but i do not understand how everything should be in folders.
And also, the connection to the database. does it need to connect to the database?
thanks
awesome tutorial. i was looking for a cool way to set up an automotive terms glossary for a website… exactly what i needed. thanks!
a couple other things:
i get this error at http://cake.afx.cc now that i added the includes for the JS:
[code]
Notice: Undefined variable: javascript in (PATH)/cake/app/views/layouts/default.thtml on line 34
Fatal error: Call to a member function link() on a non-object in (PATH)/cake/app/views/layouts/default.thtml on line 34[/code]
any ideas on how to fix this? i dunno what’s up because everything else is working…
also..i deleted the results.thtml page and it still works the same. what does that page do?
Where’s the live search input form suppose to show.
I have my table with all the edit/view/delete function ok but I can’t reach the livesearch. Not even the url users/search
??
Is it possible to get the source with their hierarchy(folders) so I can see where I did it wrong !?
Great tutorial, helped me alot as i’m new to cake. thanks
merci pour ce tutoriel,j’ai tout fait,ainsi que j’ai modifié Element.show(’loading’) par Element.show(’view’),mais toujours ça marche pas,:((
il n’ya aucune erreur sur la page,mais ien ne s’affiche,j’aimerais bien avoir une réponse