I´m trying to have certain DataObjects (News) displayed in the default SearchResult Page. So the result should display normal Pages and News.
Is there an easy way to accomplish that in Silverstripe 3?
Or is it recommended to code it completely custom - I mean a custom controller/action which handles the search request and creates a result list, which I display then in a custom template?
I found this, but obviously search is disabled right now:
https://github.com/arambalakjian/DataObjects-as-Pages
Thx and regards,
Florian
I usually but together a custom search function after enabling FulltextSearchable
. So in _config.php
I would have
FulltextSearchable::enable();
Object::add_extension('NewsStory', "FulltextSearchable('Name,Content')");
replacing Name and Content with whatever DBField
you want to be searchable. And each searchable DataObject
have this in their class to enable search indexes (pretty sure this needs to be added and run dev/build before enabling the extension, and only works on MySQL DB).
static $create_table_options = array(
'MySQLDatabase' => 'ENGINE=MyISAM'
);
then in my PageController
I have my custom searchForm and results functions.
Here is the search
function that returns the search form, called with $search
in the template:
public function search()
{
if($this->request && $this->request->requestVar('Search')) {
$searchText = $this->request->requestVar('Search');
}else{
$searchText = 'Search';
}
$f = new TextField('Search', false, $searchText);
$fields = new FieldList(
$f
);
$actions = new FieldList(
new FormAction('results', 'Go')
);
$form = new Form(
$this,
'search',
$fields,
$actions
);
//$form->disableSecurityToken();
$form->setFormMethod('GET');
$form->setTemplate('SearchForm');
return $form;
}
and here the custom results
function to handle the queries...
function results($data, $form, $request)
{
$keyword = trim($request->requestVar('Search'));
$keyword = Convert::raw2sql($keyword);
$keywordHTML = htmlentities($keyword, ENT_NOQUOTES, 'UTF-8');
$pages = new ArrayList();
$news = new ArrayList();
$mode = ' IN BOOLEAN MODE';
//$mode = ' WITH QUERY EXPANSION';
//$mode = '';
$siteTreeClasses = array('Page');
$siteTreeMatch = "MATCH( Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords ) AGAINST ('$keyword'$mode)
+ MATCH( Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords ) AGAINST ('$keywordHTML'$mode)";
$newsItemMatch = "MATCH( Name, Content ) AGAINST ('$keyword'$mode)
+ MATCH( Name, Content ) AGAINST ('$keywordHTML'$mode)";
//Standard pages
foreach ( $siteTreeClasses as $c )
{
$query = DataList::create($c)
->where($siteTreeMatch);
$query = $query->dataQuery()->query();
$query->addSelect(array('Relevance' => $siteTreeMatch));
$records = DB::query($query->sql());
$objects = array();
foreach( $records as $record )
{
if ( in_array($record['ClassName'], $siteTreeClasses) )
$objects[] = new $record['ClassName']($record);
}
$pages->merge($objects);
}
//news
$query = DataList::create('NewsStory')->where($newsItemMatch);
$query = $query->dataQuery()->query();
$query->addSelect(array('Relevance' => $newsItemMatch));
$records = DB::query($query->sql());
$objects = array();
foreach( $records as $record ) $objects[] = new $record['ClassName']($record);
$news->merge($objects);
//sorting results
$pages->sort(array(
'Relevance' => 'DESC',
'Title' => 'ASC'
));
$news->sort(array(
'Relevance' => 'DESC',
'Date' => 'DESC'
));
//output
$data = array(
'Pages' => $pages,
'News' => $news,
'Query' => $keyword
);
return $this->customise($data)->renderWith(array('Search','Page'));
}
I add all the Page classes I want to be searched and that extend SiteTree
in the $siteTreeClasses
array, and the News parts can be pretty much copied for any other DataObject
I need searchable.
I am not saying this is the best solution and this can definitely be improved on, but it works for me and this might be a good stating point.
I have adapted @colymba's solution into a silverstripe module: https://github.com/burnbright/silverstripe-pagesearch
It allows setting the pagetype in the url.
You'll need to substantially overwrite SearchForm->getResults()
.
It uses Database->searchEngine()
, but those are tailored towards SiteTree
and Page
classes.
The "proper" solution is to feed the data into a search engine like Solr or Sphinx.
We have the SS3-compatible "fulltextsearch" module for this purpose:
https://github.com/silverstripe-labs/silverstripe-fulltextsearch
It's going to take some upfront setup, and is only feasible if you can either host Solr yourself, or are prepared to pay for a SaaS provider. Once you've got it running though, the possibilities are endless, its a great tool!