Autocompletion Mess - CJuiAutoComplete

I’ve spent quite some time looking at how autocompletion is meant to work with yii. Is there any consensus at all? As far as I can see there are three wiki entries and this seems to be the most popular.

However, there is a comment by the author pointing out a different much simpler way. Is that what is supposed to be done? If so, can I edit the entry to point that out? Rather than go through the whole dance to realise it’s not the best way?

There is also this frequently referenced thread where people kind of come to conclusions but there is no fully working example either.

Rather than just bitch, I’ll try to summarise my findings to see if what I’m doing makes sense and hopefully help other noobs see the light. The goal is to get autocompletion to work and also populate {(multiple) hidden} fields as a bonus. A lot of the “incomprehensible” stuff is explained at the JQuery UI site:

  1. In the view you need a field to write in. This triggers the autocompletion, asking the controller for results.

  2. The controller needs a function that can provide answers to the view when it’s asking.

Example code for the view


    

<?php echo $form->hiddenField($model, 'id'); // one way of creating a hidden field?>

    <?php

    // ext is a shortcut for application.extensions

    $this->widget('zii.widgets.jui.CJuiAutoComplete', array(

        'id'=>'fname',// set the "id" html attributes for the text field

        'name'=>'fname',// set the name attribute

        'source'=>$this->createUrl('autocomplete'), // calls the actionAutocomplete function 

         //in the controller

        'options'=>array(

            'delay'=>300, // wait 300ms after user stops typing

            'minLength'=>2, // length of entered characters before completing

            // CRUCIAL: when the user selects one of the predicted options,

            // this populates the hidden field with the "id" part of the returned json data

            'select'=>"js:function(event, ui) { 

            $('#SessionLog_id').val(ui.item.id); // ui.item.whatYouReturnInController

            // #SessionLog_id is the id of the hidden field 

        }"

        ),

        'htmlOptions'=>array(

            'size'=>'40' // set size of the input box, optional

        ),

    ));

    ?>

The controller code is very unpolished but I’ll try to explain:




    public function actionAutocomplete(){

        $res = array();

        // read the search "term" we've been asked about

        $term = Yii::app()->getRequest()->getParam('term', false);

        if ($term)

        {

            // tablename is user

            // fname is being formatted as "value" because that's what jQuery needs!

            // name seems to be what gets replaced by our search term

            $sql = 'SELECT id, fname AS "value" FROM `user` where LCASE(fname) LIKE :name';

            $cmd = Yii::app()->db->createCommand($sql);

            // use "name" as search term?

            $cmd->bindValue(":name","%".strtolower($term)."%", PDO::PARAM_STR);

            // get all results           

            $res = $cmd->queryAll();

        }

        echo CJSON::encode($res);

        Yii::app()->end();

    }

The last thing that had me stuck was returning the data as “value” because that’s what ends up in the box that the autocompletion runs in.

Does this all make sense? Am I right in considering the Wiki entries obsolete with overriding classes etc?

I just can’t see why is so hard to get autocomplete working, it’s trivial(you can throw your code away, is garbage, no offense) :

Controller action:




public function actionTag_autocomplete()

{

	if(!Yii::app()->request->isAjaxRequest)

	   throw new CHttpException(400,Yii::t('app','Invalid request. Please do not repeat this request again.'));

	

	$term=Yii::app()->input->get('term');//or $_GET['term'], i'm using the input extension

	

	$criteria=new CDbCriteria;

	$criteria->compare('name',$term,true);

	$criteria->order='tag_id DESC';

	$criteria->limit=10;

	

	$models=Tag::model()->findAll($criteria);

	$returnArray=array();

	foreach($models AS $model)

	{

		$returnArray[]=array(

			'label'=>CHtml::encode($model->name),

			'value'=>CHtml::encode($model->name),

			'id'=>(int)$model->tag_id,

		);

	}

	Yii::app()->messageStack->set($returnArray)->toJson();

}

//note, instead of Yii::app()->messageStack you can use :

//echo CJSON::encode($res);

//Yii::app()->end();




view:




<?php

$this->widget('zii.widgets.jui.CJuiAutoComplete', array(

	'name'=>'tag',

	'sourceUrl'=>Yii::app()->createUrl('post/tag_autocomplete'),

	'options'=>array(

		'minLength'=>'2',

		'select'=>'js:function(event, ui) {

                    alert(ui.item.id+' '+ui.item.label + ' '+ui.item.value);

		}',

		'close'=>'js:function(){

	           

		}',

	),

	'htmlOptions'=>array('class'=>'field select full','style'=>'height:20px;margin-top:5px')

));

?>



Thanks for your response! It’s trivial if you know how. I haven’t tested your code yet but it looks very neat. I’ll probably change what I’ve done to be closer to what you’ve posted. It’s easier to understand and maintain.