Yahoo! Search BOSS Integration with CakePHP
Tags: CakePHP, Yahoo, API | Written on 17/7/08

I implemented full text search with the Yahoo! Search BOSS API on top of CakePHP in one night, ending at 5AM - lol. It has auto-suggest for spelling errors, and I added paging as well. The BOSS API impressed me, it was very clean and I was able to implement seamless full text search into my website.
The full source is at the bottom of the article, but let me walk you through the basic parts.
Start by gathering the results. I'm sure there is a better way to do this in CakePHP, but for now I'm using straight PHP5. I got most of this off Webmaster-Source, but I added a few things including: setting the site search context site:domain.com as well as getting the total results found totalhits. You can see all the xml data fields that you get back on the search response fields page.
Here is the code for gathering the search results, make sure you get $search_term from the params, then switch out the BOSS API key your_API_key and domain.com with yours. You get the API key by registering on the Yahoo! BOSS website.
PHP:
//Gather data and prepare query $yhost = 'http://boss.yahooapis.com'; $apikey = 'your_API_key'; $site = 'site:yourdomain.com'; $url = "$yhost/ysearch/web/v1/$thequery+$site?appid=$apikey&format=xml"; // if (!empty($start)) $url .= "&start=$start"; Add this to provide paging //Get the search results $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $data = curl_exec($ch); curl_close($ch); $results = new SimpleXmlElement($data, LIBXML_NOCDATA); $total = $results->resultset_web->attributes(); $total = $total['totalhits']; $results = $results->resultset_web->result; $yhost = 'http://boss.yahooapis.com'; $url = "$yhost/ysearch/spelling/v1/$thequery?appid=$apikey&format=xml";
We can loop through our results in our view. $results will contain title, dispurl and abstract. I use the shortcut, <? which is shorthand for <?php echo.
PHP:
<li> <span class="count">#<?= $i++; ?>:</span> <a class="search_result" title="<?= $title; ?>" href="<?= $result->clickurl; ?>"> <?= $title; ?> </a><br /> <span class="abstract"> $result->abstract ); ?> </span><br /> </li> <? endif; endforeach; ?>
Next, we want to get any suggestions in case they spelled something wrong. This is obviously optional, but I think it is a key to search. People are going to misspell things (including me). Using the spelling API, look up the suggested search term.
PHP:
//Get the suggested term $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $data = curl_exec($ch); curl_close($ch); $suggest = new SimpleXmlElement($data, LIBXML_NOCDATA); $suggest_term = $suggest->resultset_spell->result->suggestion;
Then it is easy to display the suggestion in our view:
PHP:
<strong>Did you mean: <?= $html->link($suggestTerm, '/searches?q=' . $suggestTerm); ?></strong> <? endif; ?>
I also implemented paging in the view, this is probably pretty crappy code... written very close to 5AM so you may want to do this your own way. It basically uses the $start and $total variables in the URL to calculate the pages, and what page you are on.
PHP:
<?php if ($start > 0): if ($start == 10) $startParam = ''; else $startParam = "&start=" . ($start - 10); endif; $page = 0; for ($i=0; $i<$total; $i=$i+10) { $page++; if ($i > ($start-50) && $i < ($start+50)) { if ($i == $start) { } else if ($i === 0) { } else { } } else if ($i == ($start-60)) { } else if ($i == ($start+60)) { } } endif; endif; ?>
Ok, putting it all together here is the full source.
Full source of controllers/searches_controller.php
PHP:
<?php class SearchesController extends AppController { var $name = 'Searches'; public function index() { params['url']['q']) ? $this->params['url']['q'] : null; //Gather data and prepare query $yhost = 'http://boss.yahooapis.com'; $apikey = 'your_API_key'; $site = 'site:yourdomain.com'; $url = "$yhost/ysearch/web/v1/$thequery+$site?appid=$apikey&format=xml"; //Get the search results $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $data = curl_exec($ch); curl_close($ch); $results = new SimpleXmlElement($data, LIBXML_NOCDATA); $total = $results->resultset_web->attributes(); $total = $total['totalhits']; $results = $results->resultset_web->result; $yhost = 'http://boss.yahooapis.com'; $url = "$yhost/ysearch/spelling/v1/$thequery?appid=$apikey&format=xml"; //Get the suggested term $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $data = curl_exec($ch); curl_close($ch); $suggest = new SimpleXmlElement($data, LIBXML_NOCDATA); $suggest_term = $suggest->resultset_spell->result->suggestion; $next = $start + 10; } } } ?>
Full source of views/searches/index.ctp
PHP:
<?php $this->pageTitle = "Search results for, \"$searchTerm\""; ?> <h2>Search results for, "<?= $searchTerm; ?>"</h2> <strong>Did you mean: <?= $html->link($suggestTerm, '/searches?q=' . $suggestTerm); ?></strong> <? endif; ?> <div class="container search_results"> No results found for "<?= $searchTerm; ?>". <? else: ?> <ul> <li> <span class="count">#<?= $i++; ?>:</span> <a class="search_result" title="<?= $title; ?>" href="<?= $result->clickurl; ?>"> <?= $title; ?> </a><br /> <span class="abstract"> $result->abstract ); ?> </span><br /> </li> <? endif; endforeach; ?> </ul> <? if ($total > 10): if ($start > 0): if ($start == 10) $startParam = ''; else $startParam = "&start=" . ($start - 10); endif; $page = 0; for ($i=0; $i<$total; $i=$i+10) { $page++; if ($i > ($start-50) && $i < ($start+50)) { if ($i == $start) { } else if ($i === 0) { } else { } } else if ($i == ($start-60)) { } else if ($i == ($start+60)) { } } endif; endif; ?> <? endif; ?> </div> <?php endif; ?>
Comments
Great example though! Thanks