unchanged
Title
Adding search to Yii blog example (using Zend Lucene)
Introduction ------------ForIn this tutorial I will add a search to the blog demo. The search would be based on Zend Lucene. Requirements and Preparation --------------------------- * Yii framework -II will work with the current version [yii-1.1.8.r3324](yii.googlecode.com/files/yii-1.1.8.r3324.tar.gz "1.1.8") extract the archiveinon your server... * Now go to your **yii-1.1.8.r3324\demos\blog\protected** directory and create there a **vendors** directory In the vendors directory you should put the Zend Frameworklibrary, For thislibrary which youwill need tocan [downloadit](http://www.zendframework.com/download/latesthere](http://www.zendframework.com/download/latest "downloadit")here"). After downloading copy the Zend directorythat insidewith the library (with Zend directory inside)to vendors Shouldinto vendors. It should look like this now (I copied only the Search directory...): <center>  </center> * add under runtimedirectory,directory a new one and call it search, it will be used for the index files that Zend Lucene will create. Make sure it is writable! Step 1 - adding the search form ------------------------------- For this we will create a widget or if to be more specific a CPortlet! The most simple way is to go to your componentsdirectory,directory and create a SearchBlock.phpfilefile. And copy inside code from tag cloud. change class name to SearchBlock. Now lets insert it ! Go to views/layouts/column2.php add this,abouveabove the TagCloud widget ~~~ [php] <?php$this->widget('SearchBlock',$this->widget('SearchBlock', array());)); ?> ~~~ after saving you will see 2 tagcouldsclouds in your main blog page... lets now edit it and change it to some search form Now lets change the SearchBlog widget code... ~~~ [php] <?php Yii::import('zii.widgets.CPortlet'); class SearchBlock extends CPortlet {publicpublic $title='Search';protectedprotected function renderContent(){ echo{ echo CHtml::beginForm(array('search/search'), 'get', array('style'=> 'inline')) .CHtml::textField('q',CHtml::textField('q', '', array('placeholder'=> 'search...','style'=>'width:140px;')) .CHtml::submitButton('Go!',array('style'=>'width:30px;'))CHtml::submitButton('Go!',array('style'=>'width:30px;')) .CHtml::endForm(''); }CHtml::endForm(''); } } ~~~ It will look like this: <center>  </center> Creating SearchController ------------------------ Now lets add a SearchController ~~~ [php] <?php class SearchController extends Controller {/** */** * @var string index dir as alias path from <b>application.</b>,, default to <b>runtime.search</b>*/ private*/ private $_indexFiles = 'runtime.search';/** */** * (non-PHPdoc)** @see CController::init()*/ public*/ public function init(){Yii::import('application.vendors.*'); require_once('Zend/Search/Lucene.php'); parent::init(); } publicYii::import('application.vendors.*'); require_once('Zend/Search/Lucene.php'); parent::init(); } public function actionCreate(){ } public{ } public function actionSearch(){ }{ } } ~~~ As you can see in the init method we import Zend from vendors and we require the Search Lucene It has two actions Search and Create For this tutorial I don't need more... but you can add update for example to update the search index ( of course you should implement it) etc. It is a time to mention the documentation for Zend Lucene [Here](http://framework.zend.com/manual/en/zend.search.lucene.overview.html "") To do more advanced stuff, you will need to learn it Also the [api ](http://framework.zend.com/apidoc/core/db_Search_Lucene.html#%5CZend_Search_Lucene "") for the Lucene classCreating Search Index --------------------- This is an example of blog array after AR findAll ~~~ [php] 'id' => '2' 'title' => 'A Test Post' 'content' => 'a lot of text' 'tags' => 'test' 'status' => '2' 'create_time' => '1230952187' 'update_time' => '1230952187' 'author_id' => '1' ~~~ This is how we create the index: ~~~ [php] /**** Search index creation*/ public*/ public function actionCreate(){ $index{ $index = new Zend_Search_Lucene(Yii::getPathOfAlias('application.' . $this->_indexFiles), true);$posts$posts = Post::model()->findAll();foreach($postsforeach($posts as $post){$doc$doc = new Zend_Search_Lucene_Document();$doc->addField(Zend_Search_Lucene_Field::Text('title', CHtml::encode($post->title),$doc->addField(Zend_Search_Lucene_Field::Text('title', CHtml::encode($post->title), 'utf-8')); $doc->addField(Zend_Search_Lucene_Field::Text('link', CHtml::encode($post->url) ,); $doc->addField(Zend_Search_Lucene_Field::Text('link', CHtml::encode($post->url) , 'utf-8')); $doc->addField(Zend_Search_Lucene_Field::Text('content', CHtml::encode($post->content) ,); $doc->addField(Zend_Search_Lucene_Field::Text('content', CHtml::encode($post->content) , 'utf-8')); $index->addDocument($doc); } $index->commit(); echo); $index->addDocument($doc); } $index->commit(); echo 'Lucene index created';}} ~~~ First I found all the posts with **Post::model()->findAll();** and than I added post to search index one by one, his content title and link fields Now you should navigate to**http://localhost/yii-1.1.8.r3324/demos/blog/index.php/search/create** And you will see "Lucene index created" Doing the search ---------------- ~~~ [php] public function actionSearch(){ $this->layout='column2'; if{ $this->layout='column2'; if (($term = Yii::app()->getRequest()->getParam('q', null)) !== null) {$index$index = new Zend_Search_Lucene(Yii::getPathOfAlias('application.' . $this->_indexFiles));$results$results = $index->find($term);$query$query = Zend_Search_Lucene_Search_QueryParser::parse($term);$this->render('search',$this->render('search', compact('results', 'term', 'query'));} }} } ~~~ Creating the view ----------------- Create views/search/search.php add there ~~~ [php] <?php $this->pageTitle=Yii::app()->name . ' - Search results'; $this->breadcrumbs=array('Search'Search Results', ); ?> <h3>Search Results for: "<?php echo CHtml::encode($term); ?>"</h3> <?php if (!empty($results)): ?><?php<?php foreach($results as $result):?> <p>Title:?> <p>Title: <?php echo $query->highlightMatches(CHtml::encode($result->title)); ?></p><p>Link:<p>Link: <?php echo CHtml::link($query->highlightMatches(CHtml::encode($result->link)), CHtml::encode($result->link)); ?></p><p>Content:<p>Content: <?php echo $query->highlightMatches(CHtml::encode($result->content)); ?></p><hr/> <?php<hr/> <?php endforeach; ?><?php<?php else: ?><p<p class="error">No results matched your search terms.</p><?php<?php endif; ?> ~~~ As you can see because I passed to the view the $query, I can use Zend's highlightMatches ...<br>(if(if your search results not inenglish,English, you might have some encoding issues with highlightMatches, so you might consider using your own highlighter for greater flexibility and encoding issues free)<br>Also I created a real link from the linked I passed and created via **$doc->addField(Zend_Search_Lucene_Field::Text('link' ...** Andthatsthat's it try to search the word "it" And you will get:  Conclusion ---------- Now we are done, we have our search working... As you noticed I don't explained a lot about Zend Lucene itself... So you should look at the links I gave, and try to learn about it more! Additional Links ---------------- 1. [larry ulman's artice about Zend Lucene](http://www.larryullman.com/2009/12/05/integrating-zend_lucene-with-yii/ "") 2. [Zend Lucene documentation](http://framework.zend.com/manual/en/zend.search.lucene.overview.html "") 3. [Good article 1](http://www.talkincode.com/getting-started-with-zend_lucene-885.html "") 3. [Good article 2](http://devzone.zend.com/article/91 "") 5. [Good articles 3](http://ganeshhs.com/zend-framework/zend-lucene-search-part5-search-engine-results-page-formatting "") Bonus: Pagination with CPagination ----------------------------------- As some one here added in the comment, pagination can be a little tricky... You need to do some calculations by yourself... Actually it is always a dilemma if to give you find the trick yourself, or give it to you directly... There is a very big danger if you always get the answers for your problem from others... because in real time job, no one will do the job for you... So this is how it would look with [CPagination](http://www.yiiframework.com/doc/api/1.1/CPagination ""), after you figure out how to do the pagination of the result yourself: <center> (The page limit is 3 per page)  </center> Sodontdon't continue reading, until you spend some time thinking about it and trying your self! Add to search ~~~ [php] $pages = new CPagination(count($results)); $currentPage = Yii::app()->getRequest()->getQuery('page', 1); $pages->pageSize = 3; ~~~ In the view change the foreach view with for ~~~ [php]<?php<?php for($i = $currentPage * $pages->pageSize - $pages->pageSize, $end = $currentPage * $pages->pageSize; $i<$end;$i++):?> <p>Title:?> <p>Title: <?php echo $query->highlightMatches(CHtml::encode($results[$i]->title)); ?></p><p>Link:<p>Link: <?php echo CHtml::link($query->highlightMatches(CHtml::encode($results[$i]->link)), CHtml::encode($results[$i]->link)); ?></p><p>Content:<p>Content: <?php echo $query->highlightMatches(CHtml::encode($results[$i]->content)); ?></p><hr/> <?php<hr/> <?php endfor; ?> ~~~ and add after this~~~ [php] <?php $this->widget('CLinkPager', array('pages''pages' => $pages, )) ?> ~~~ It is not perfect, but it is the start. The problems you should think about are: what to do if some one tries to access page number 40 ? now it will throw error 500 What will happen if you have 2 pages, but only 4 records? You will getundefind ofssetundefined offset errors... Consider this as homework ;-) p.s. don't forget to add some blog posts or you will have nothing to paginate ;-) Another pagination solution --------------------------- Actually if you want it pass between pages via ajax, and don't handle the offset issues etc. ( it is easy but... we still mostliklylikely want ajax) So you can consider doing the pagination via CArrayDataProvider You have there example http://www.yiiframework.com/doc/api/1.1/CArrayDataProvider Just pass the result array than you can create CListView with some basic view for search result element, and you are done... CArrayDataProvider will do all the dirty work for you!