FAQ #51
Question: How do I add a search engine on my Cakephp site?
Answer: Use fulltext index (presuming you use Mysql as the database) and by coaxing Cake's paginate method, you can create a decent search engine fairly quickly. Here's a full example...
//Your model search.php
<?php
class Search extends AppModel {
var $useTable = false;
var $validate = array(
'q' => array(
'notempty' => array(
'rule' => 'notempty',
'message' => 'Query cannot be empty',
),
'maxlength' => array(
'rule' => array('maxlength',100),
'message' => 'Query cannot be longer than 100 characters',
),
'minlength' => array(
'rule' => array('minlength',2),
'message' => 'Query cannot be shorter than 2 characters',
),
),
);
} //end of class
/*EOF*/
//Your controller search_controller.php
<?php
class SearchController extends AppController {
var $uses=array('Link');
function index(){
$this->set('title_for_layout',"Search results");
$query=(isset($this->data['Search']['q']))?$this->data['Search']['q']:'';
if(!$query) $query=(isset($this->params['url']['q']))?$this->params['url']['q']:'';
if(!$query){
$this->Session->setFlash(__("Invalid or empty search string",true));
}
else{
App::import('Sanitize');
$this->set('query',$query);
$query=Sanitize::escape($query);
$this->paginate = array(
'conditions' => array('Link.status'=>1,
"MATCH(Link.title,Link.description) AGAINST('$query' IN BOOLEAN MODE)"
),
'recursive'=>-1,
'limit'=>10,
);
$this->set('links', $this->paginate('Link'));
}
}
} //end of class
/*EOF*/
//Your search form which you can put anywhere in your layout:
<?php
echo $this->Form->create('Search',array('url'=>'/search'));
echo $this->Form->input('q',array('label'=>'Search'));
echo $this->Form->end();
?>
//Your search/index.ctp view, to display search results
<div id="bigtitle">Search Results</div>
<?php if($query):?>
<p>
Search for: <strong><? echo $query; ?></strong>
</p>
<?php
foreach($links as $link){
?>
<div id="result">
<div id="title"><?php echo $link['Link']['title'];?></div>
<div id="description"><?php echo $link['Link']['description'];?></div>
<div id="url">
<?php echo $this->Html->link($link['Link']['url'],$link['Link']['url']);?>
</div>
</div>
<?php
}
?>
<p>
<?php
//here's where we preserve the query for the next page of results
$paginator->options(array('url' => array_merge($this->passedArgs,
array('?' => "&q=".urlencode($query)))));
echo $this->Paginator->counter(array(
'format' => __('Page %page% of %pages%', true)
));
?> </p>
<div class="paging">
<?php echo $this->Paginator->prev('<< ' . __('previous', true), array(), null, array('class'=>'disabled'));?>
| <?php echo $this->Paginator->numbers();?>
|
<?php echo $this->Paginator->next(__('next', true) . ' >>', array(), null, array('class' => 'disabled'));?>
</div>
<?php endif; ?>
Example:
//Your links table includes these fields
url VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT,
status TINYINT NOT NULL DEFAULT 0,
FULLTEXT KEY `fulltext` (`title`,`description`)
url VARCHAR(255) NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT,
status TINYINT NOT NULL DEFAULT 0,
FULLTEXT KEY `fulltext` (`title`,`description`)
//Your model search.php
<?php
class Search extends AppModel {
var $useTable = false;
var $validate = array(
'q' => array(
'notempty' => array(
'rule' => 'notempty',
'message' => 'Query cannot be empty',
),
'maxlength' => array(
'rule' => array('maxlength',100),
'message' => 'Query cannot be longer than 100 characters',
),
'minlength' => array(
'rule' => array('minlength',2),
'message' => 'Query cannot be shorter than 2 characters',
),
),
);
} //end of class
/*EOF*/
//Your controller search_controller.php
<?php
class SearchController extends AppController {
var $uses=array('Link');
function index(){
$this->set('title_for_layout',"Search results");
$query=(isset($this->data['Search']['q']))?$this->data['Search']['q']:'';
if(!$query) $query=(isset($this->params['url']['q']))?$this->params['url']['q']:'';
if(!$query){
$this->Session->setFlash(__("Invalid or empty search string",true));
}
else{
App::import('Sanitize');
$this->set('query',$query);
$query=Sanitize::escape($query);
$this->paginate = array(
'conditions' => array('Link.status'=>1,
"MATCH(Link.title,Link.description) AGAINST('$query' IN BOOLEAN MODE)"
),
'recursive'=>-1,
'limit'=>10,
);
$this->set('links', $this->paginate('Link'));
}
}
} //end of class
/*EOF*/
//Your search form which you can put anywhere in your layout:
<?php
echo $this->Form->create('Search',array('url'=>'/search'));
echo $this->Form->input('q',array('label'=>'Search'));
echo $this->Form->end();
?>
//Your search/index.ctp view, to display search results
<div id="bigtitle">Search Results</div>
<?php if($query):?>
<p>
Search for: <strong><? echo $query; ?></strong>
</p>
<?php
foreach($links as $link){
?>
<div id="result">
<div id="title"><?php echo $link['Link']['title'];?></div>
<div id="description"><?php echo $link['Link']['description'];?></div>
<div id="url">
<?php echo $this->Html->link($link['Link']['url'],$link['Link']['url']);?>
</div>
</div>
<?php
}
?>
<p>
<?php
//here's where we preserve the query for the next page of results
$paginator->options(array('url' => array_merge($this->passedArgs,
array('?' => "&q=".urlencode($query)))));
echo $this->Paginator->counter(array(
'format' => __('Page %page% of %pages%', true)
));
?> </p>
<div class="paging">
<?php echo $this->Paginator->prev('<< ' . __('previous', true), array(), null, array('class'=>'disabled'));?>
| <?php echo $this->Paginator->numbers();?>
|
<?php echo $this->Paginator->next(__('next', true) . ' >>', array(), null, array('class' => 'disabled'));?>
</div>
<?php endif; ?>
Related Faq:How do I implement a fulltext search?
Category:Controller