Autocomplete

By gwoo (gwoo)
The AutocompleteComponent originally submitted by Nate to CakeForge. With a nice intro at
http://cakebaker.42dh.com/2006/06/06/autocompletion-the-easy-way/
The full component and some sample controller and view code is here.

Controller Class:

Download code <?php 
var $components = array('Autocomplete');
var 
$helpers = array('Html''Javascript''Ajax');
?>


View Template:

Download code
<?php echo $ajax->autoComplete('Company/name'); ?>


-----------------------------------------------------------

Component Class:

Download code <?php 
<?php
/* SVN FILE: $Id: autocomplete.php 2932 2006-05-23 04:25:29Z nate $ */

/**
 * Automagically handles requests for autocomplete fields
 *
 * CakePHP :  Rapid Development Framework <http://www.cakephp.org/>
 * Copyright (c)    2006, Cake Software Foundation, Inc.
 *                                1785 E. Sahara Avenue, Suite 490-204
 *                                Las Vegas, Nevada 89104
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 * @filesource
 * @copyright        Copyright (c) 2006, Cake Software Foundation, Inc.
 * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP Project
 * @package            cake
 * @subpackage        cake.cake.libs.controller.components
 * @since            CakePHP v 0.10.4.1076
 * @version            $Revision: 2932 $
 * @modifiedby        $LastChangedBy: nate $
 * @lastmodified    $Date: 2006-05-23 00:25:29 -0400 (Tue, 23 May 2006) $
 * @license            http://www.opensource.org/licenses/mit-license.php The MIT License
 */

/**
 * Autocomplete Handler
 *
 * @package        cake
 * @subpackage    cake.cake.libs.controller.components
 *
 */
class AutocompleteComponent extends Object {

    var 
$layout 'ajax';

    var 
$enabled true;

    var 
$components = array('RequestHandler');

    var 
$handles = array();

/**
 * Startup
 *
 * @param object A reference to the controller
 * @return null
 */
    
function startup(&$controller) {

        if (!
$this->enabled || !$this->RequestHandler->isAjax() || !$this->RequestHandler->isPost()) {
            return 
true;
        }

        
$data $controller->data;
        if (empty(
$data) || count($data) != 1) {
            return 
false;
        }

        list(
$model) = array_keys($data);
        if (!
is_array($data[$model]) || count($data[$model]) != || !is_object($controller->{$model})) {
            return 
false;
        }

        list(
$field) = array_keys($data[$model]);
        
$conditions = array();

        if (!empty(
$this->handles)) {

            
$handled false;
            
$fields = array();

            foreach (
$this->handles as $key => $val) {
                if (
is_int($key)) {
                    
$key $val;
                    
$val = array();
                }
                if (
$key == $model.'.'.$field || $key == $field || $key == $model.'.*') {
                    
$handled true;
                    
$conditions $val;
                    break;
                }
            }
            if (!
$handled) {
                return 
true;
            }
        }

        
$base = array($model.'.'.$field => 'LIKE %'.$data[$model][$field].'%');
        if (!empty(
$conditions)) {
            
$conditions = array($base$conditions);
        } else {
            
$conditions $base;
        }

        
$results $controller->{$model}->findAll($conditions);

        if (
is_array($results) && !empty($results)) {
            
e("<ul>\n");
            foreach (
$results as $rec) {
                if (isset(
$rec[$model][$field])) {
                    
e("\t<li>".$rec[$model][$field]."</li>\n");
                }
            }
            
e("</ul>\n");
        }
        exit();
    }
}

?>
?>

 

Comments 77

CakePHP Team Comments Author Comments
 

Question

1 multiple fields

Hi gwoo,

can you also tell how I can show results from two or more fields in the autocomplete box. I want to display full name which consists of first_name and last_name

Regards,
Ritesh
Posted Sep 28, 2006 by Ritesh Agrawal
 

Comment

2 using two fields

You have a couple options.
  1. mysql: CONCAT('first_name','last_name') as name
  2. php:
    foreach ($users as $key=>$user) {
    $user[$key]['User']['fullname'] = $user[$key]['User']['first_name'] . ' ' $user[$key]['User']['last_name'];
    }


either of those should work.
Posted Sep 29, 2006 by gwoo
 

Comment

3 For the newbees

Don't forget to

1. put the js files from http://script.aculo.us in your webroot/js/ folder.

2. put the following (in this order) between the head tags:
<?php $html->charsetTag('UTF-8'); ?>
<?php $javascript->link('prototype'); ?>
<?php $javascript->link('scriptaculous.js?load=effects'); ?>
<?php $javascript->link('controls'); ?>

Very nice!
Posted Sep 29, 2006 by Welja
 

Question

4 multiple fields

hi,
where exactly do i have to add or replace the line in the above code if i want to have 2 or more fields -
in my (similar) case, i want to display lastname, firstname and the client's id e.g. smith, adam [007] - so to offer means to distinguish between two smith adams. -
thanx 4 your help,
tom
Posted Nov 28, 2006 by Othman ouahbi
 

Question

5 Follow up on Multiple FIelds

Was there ever an answer on where you put the code from reply #2? I have a search and I am trying to look for an ID or a Title? The component is great BTW...

Thanks,
The Flash Bum
http://www.flashbum.com
Posted Dec 29, 2006 by Jesse Freeman
 

Question

6 Ajax not defined

I am a total noob, and beg some help. I am trying to use this as described, I have the html, javascript, and ajax helpers in my controller. Actually I have them in my controller and in app_controller.php. I have a form in my view that contains only the setup and completion code for the form, a submit, and the snippet from here. The component is installed in the controllers/components dir. When I call the view, I get the form, but it fails silently. when I check the javascript console, I get an Ajax is not defined error. any clues that will help with my ignorance would be very helpful.
Al
Posted Dec 31, 1969 by Al Whorley
 

Comment

7 Ajax not defined

my problem turns out to be that the echo command from the code block above is ommitted:

<code>
<?php echo $html->charsetTag('UTF-8'); ?>
<?php echo $javascript->link('prototype'); ?>
<?php echo $javascript->link('scriptaculous.js?load=effects'); ?>
<?php echo $javascript->link('controls'); ?>
</code>

just thought that my error might help another Cake novice.
Al
Posted Dec 31, 1969 by Al Whorley
 

Comment

8 suggestion

It might be better to remove the first '%' on the SQL. So you get more relevant resutls as you type.

change line 90 to.

$base = array($model.'.'.$field => 'LIKE '.$data[$model][$field].'%');

Posted May 10, 2007 by Leigh Mackay
 

Comment

9 Help

It does not seem to work on CakePHP 1.2?

Is this right? The code it genreates is this:


  <input name="data[Article][title]" type="text" id="ArticleTitle" autocomplete="off" value="" />
  <div id="ArticleTitle_autoComplete" class="auto_complete"></div>
  <script type="text/javascript">new Ajax.Autocompleter('ArticleTitle', 'ArticleTitle_autoComplete', '/mech7/articles/add', {});</script>



I suppose it is because it says autocomplete=off ?
Posted Jun 18, 2007 by chris
 

Comment

10 Possible Security Issue

Please correct me if I am wrong, but isn't there a security issue with this component? A user could technically modify your page locally and point your auto-complete to any field in your database that is in any of the models that are used by the current controller. Therefore, extracting e-mail addresses, usernames, and possibly even passwords.

CakePHP makes this especially easy, since naming standards allow users to see exactly what your data model looks like.

Is there something here to prevent it that I am not seeing?
Posted Jun 29, 2007 by Allen
 

Comment

11 How do i..

How do i get for example the name from tags but let it submit the id?
Posted Jun 30, 2007 by chris
 

Comment

12 yes

Yes you are correct.. if i edit the form with firebug, i can enter different fields for the database 0_o this is pretty unsafe.

Please correct me if I am wrong, but isn't there a security issue with this component? A user could technically modify your page locally and point your auto-complete to any field in your database that is in any of the models that are used by the current controller. Therefore, extracting e-mail addresses, usernames, and possibly even passwords.

CakePHP makes this especially easy, since naming standards allow users to see exactly what your data model looks like.

Is there something here to prevent it that I am not seeing?
Posted Jun 30, 2007 by chris
 

Comment

13 Suggestion

Line 97 in the code should be changed from

$results = $controller->{$model}->findAll($conditions); 

to

$results = $controller->{$model}->findAll($conditions, $field); 

as this will limit only to the field that you want to query and not the all columns.

But as far as security breach with editing the value is concerned, it is there.. So to fix it, you may have customize the Autocomplete component and the beforeFilter method in the controller. In the Autocomplete, modify the code to match the query field with the parameter you may set in the beforeFilter. If it doesn't match then ignore the query, else continue.

Hope this helps someone,
Ketan
http://www.eclassifieds4u.com - Free Classifieds
Posted Jul 27, 2007 by Ketan
 

Comment

14 It doesn t work

This components has a lot of problems it give me always the same error ...

this.element.setAttribute is not a function
this.element.setAttribute('autocomplete','off');

controls.js (line 82)

Can someone explain me how to solve this problem ?
Posted Nov 13, 2007 by Antonello Mangone
 

Comment

15 Problems with script.acoul.us and JQuery

I always encounter serveral javascirpt errors as long as JQuery AND script.acoul.us are loaded.
These errors prevent autocomplete from working (as well as many other script.acoul.us functions).

I've solved this problem by just not using JQuery anymore, as sad as this is...

Posted Dec 3, 2007 by Nico Haberzettl
 

Bug

16 autocomplete off problem

I've managed to solve the "autocomplete=off" problem by editing the ajax helper,

line 450 of ajax.php had:

$htmlOptions['autocomplete'] = "off";

Which I changed to on, and it works fine now... I'm not sure this is the correct fix, but it works for me. I think this is possibly a bug?
Posted Jan 25, 2008 by Stickymaddness
 

Comment

17 I think you are misunderstanding

I've managed to solve the "autocomplete=off" problem by editing the ajax helper,

line 450 of ajax.php had:

$htmlOptions['autocomplete'] = "off";

Which I changed to on, and it works fine now... I'm not sure this is the correct fix, but it works for me. I think this is possibly a bug?


The autocomplete="off" attribute of the input field is actually turning off the browsers autocomplete abilities, not the websites. The reason that it is set to off is so that the browsers autocomplete doesn't interfere with the ajax autocomplete. When you say that you've fixed it by altering the framework code I believe all you've really done is turn on the browser autocomplete abilities. I've been playing around with this and find that it works great and I've checked the code and the autocomplete of the input is still set to off.

Here is the M$(yuck) explanation for reference:
http://msdn2.microsoft.com/en-us/library/ms533486(VS.85).aspx
Posted Feb 11, 2008 by Jeff Huelsbeck
 

Bug

18 I get two problems..

Error: Ajax.Autocompleter is not a constructor
Source File: http://localhost/index.php/products/add/
Line: 63

&amp;lt;script type="text/javascript">new Ajax.Autocompleter('ProductName', 'ProductName_autoComplete', '/index.php/products/add/', {});&amp;lt;/script>


And also nothing happens. I ahve all the helpers, components and manually link to prototype and scriptalicous in head tags.
Posted Apr 20, 2008 by Eddie