Add a keyword search to an index view

This is a simple way to add a keyword search to an index view. I am a newcomer to Yii, so check out any comments for improvements on this approach.

A search for Web Apps will find all records where the content of the sepecified search columns includes either ‘Web’ or ‘Apps’.

A search for “Web Apps” will find all records where the content of the sepecified search columns includes the phrase ‘Web Apps’.

The example code is based on the ‘post’ entity in the blog tutorial. First create a search component. The widget, KeywordSearchComponent.php, goes in the /protected/components folder.




<?php

Yii::import('zii.widgets.CPortlet');


class KeywordSearchComponent extends CPortlet

{

	protected function renderContent()

	{

		$this->render('keywordSearch');

	}

}



The associated view, keywordSearch.php, goes in the /protected/components/views folder.




<div>

<?php echo CHtml::beginForm(array('post/index'), 'get'); ?>

<?php echo Chtml::label('Keyword Search ', 'searchbox'); ?>

<?php echo Chtml::textField('searchbox', $this->controller->currentSearchValue, array('size'=>20,'maxlength'=>128)); ?>

<?php echo CHtml::submitButton('Search'); ?>

<?php echo CHtml::endForm(); ?>

</div>



Now add the search widget to the index.php view above the CListView widget using the code:




<?php $this->widget('KeywordSearchComponent'); ?>



The controller (e.g. PostController.php) now needs to be amended. Add the following instance variables (amending the column names to those you wish to be included in the keyword search).




private $keywordSearchColumnArray = array('title', 'content','tags'); //Columns to search 

public $currentSearchValue; //Current keword search string



Now add a new search condition to the actionIndex function. The following example is an amended version of the post demo code.




public function actionIndex()

{

	//Original demo code to create a criteria object 	

	$criteria=new CDbCriteria(array(

		'condition'=>'status='.Post::STATUS_PUBLISHED,

		'order'=>'priority',

		'with'=>'commentCount',

	));

	

	//New code for the keyword search

	if(isset($_GET['searchbox']) and strlen(trim($_GET['searchbox'])) > 0)

	{

		$this->currentSearchValue = trim($_GET['searchbox']);

		$additionalCriteria = $this->makeKeywordSearchCondition($_GET['searchbox']);

		$criteria->addCondition($additionalCriteria);

	}	

	

	//Original demo code for the tag cloud search	

	if(isset($_GET['tag']))

		$criteria->addSearchCondition('tags',$_GET['tag']);			

	

	//Original demo code to create the data provider object

	$dataProvider=new CActiveDataProvider('Post', array(

		'pagination'=>array(

			'pageSize'=>Yii::app()->params['postsPerPage'],

		),

		'criteria'=>$criteria,

	));

	...



Finally, add the makeKeywordSearchCondition function:




/**

 * Make the keyword search SQL. 

 * @param 	String 	Search string input by user

 * @return 	String  SQL condition 

 */	

private function makeKeywordSearchCondition($keywordStr){

	$criteriaSql; 		//Search condition	

	

 	//Split the string into an array of words and phrases

 	//The string: Android "Web Apps" 

 	//will produce a two element array containing 'Android' and 'Web Apps' 

	$elementArray = array();

	$regX = "/[\s,]*\\\"([^\\\"]+)\\\"[\s,]*|[\s,]+/"; 

	$tempArray = preg_split  ($regX, trim($keywordStr),  0, PREG_SPLIT_DELIM_CAPTURE);

	foreach($tempArray as $ind => $str){

		if(trim($str)){

			array_push($elementArray, $str);

		}			

	}

	

	//Construct the search sql  

	foreach($this->keywordSearchColumnArray as $column)

	{	

		foreach($elementArray as $value){

			$value =  addSlashes($value);

			$value = '"%'.$value.'%"';	

			if($criteriaSql){


				$criteriaSql .= ' OR';

			}

			$criteriaSql .= " $column LIKE $value";

		}


	}			

	return $criteriaSql;

}			



A correction to my code. In keywordSearch.php I have hard coded the route to ‘post/index’. To make the widget portable it needs to use $this->controller->getRoute(), so the keywordSearch view becomes:




<div>

<?php echo CHtml::beginForm(array($this->controller->getRoute()), 'get'); ?>

<?php echo Chtml::label('Keyword Search ', 'searchbox'); ?>

<?php echo Chtml::textField('searchbox', $this->controller->currentSearchValue, array('size'=>20,'maxlength'=>128)); ?>

<?php echo CHtml::submitButton('Search'); ?>

<?php echo CHtml::endForm(); ?>

</div>



Notes for Postgresql users:

In Postgres, unlike MySQL, the keywords are case sensitive.

Becuase Postgres does not like double quotes the following line in the makeKeywordSearchCondition function:


$value = '"%'.$value.'%"';  

must be changed to:


$value = "'%".$value."%'";

This change will work OK with MySQL.

thank you very much for you sharing.

I try your code, but there is a problem with this variable “$criteriaSql;” in beginning of “makeKeywordSearchCondition” function. I don’t know which default value for this variable, so I put “$criteriaSql = “”;”.

If you have any idea please tell me.

Thanks again.

The $criteriaSql = ""; is fine

thx it’s work and helpfull

regards