Skip to content

Instantly share code, notes, and snippets.

@colymba
Last active September 15, 2017 11:44
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save colymba/4657819 to your computer and use it in GitHub Desktop.
Save colymba/4657819 to your computer and use it in GitHub Desktop.
SilverStripe custom search results function. For Page and DataObject
/**
* Process and render search results.
*
* !! NOTE
* _config.php includes:
*
* FulltextSearchable::enable();
* Object::add_extension('ExtendedPage', "FulltextSearchable('HeadlineText')");
* Object::add_extension('NewsStory', "FulltextSearchable('Name,Content')");
* !!
*
* @param array $data The raw request data submitted by user
* @param Form $form The form instance that was submitted
* @param SS_HTTPRequest $request Request generated for this action
*/
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();
$files = new ArrayList();
$mode = ' IN BOOLEAN MODE';
//$mode = ' WITH QUERY EXPANSION';
//$mode = '';
$siteTreeClasses = array('Page', 'ExtendedPage'); //add in an classes that extend Page or SiteTree
$siteTreeMatch = "MATCH( Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords ) AGAINST ('$keyword'$mode)
+ MATCH( Title, MenuTitle, Content, MetaTitle, MetaDescription, MetaKeywords ) AGAINST ('$keywordHTML'$mode)";
//custom MATCH statement for any Object that extend Page or SiteTree and have additional fields/columns that need to be searchable
//create one block per Object
//i.e. "MATCH( column1, column2, column3 ) AGAINST ('$keyword'$mode) + MATCH( column1, column2, column3 ) AGAINST ('$keywordHTML'$mode)"
$extendedPageMatch = "MATCH( HeadlineText ) AGAINST ('$keyword'$mode)
+ MATCH( HeadlineText ) AGAINST ('$keywordHTML'$mode)";
//DataObject
$newsItemMatch = "MATCH( Name, Content ) AGAINST ('$keyword'$mode)
+ MATCH( Name, Content ) AGAINST ('$keywordHTML'$mode)";
$fileMatch = "MATCH( Filename, Title, Content ) AGAINST ('$keyword'$mode)
+ MATCH( Filename, Title, Content ) AGAINST ('$keywordHTML'$mode)";
/*
* Standard pages
* SiteTree Classes with the default search MATCH
*/
foreach ( $siteTreeClasses as $c )
{
$query = DataList::create($c)
->filter(array('RootLanguageParentID' => $this->RootLanguageParentID))
->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);
}
/*
* Pages with additional searchable fields
* copy/edit this block for each Object that extend Page/SiteTree and have other columns searchable
* see above for custom MATCH statement
*/
$query = DataList::create('ExtendedPage')
->where($siteTreeMatch . ' OR ' . $extendedPageMatch);
$query = $query->dataQuery()->query();
$query->addSelect(array('Relevance' => $siteTreeMatch . ' OR ' . $extendedPageMatch));
$records = DB::query($query->sql());
$objects = array();
foreach( $records as $record ) $objects[] = new $record['ClassName']($record);
$pages->merge($objects);
$pages->removeDuplicates();
/*
* news DataObject
*/
$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);
/*
* files
*/
$query = DataList::create('File')->where($fileMatch);
$query = $query->dataQuery()->query();
$query->addSelect(array('Relevance' => $fileMatch));
$records = DB::query($query->sql());
$objects = array();
foreach( $records as $record )
{
$extension = strtolower( pathinfo( $record['Name'], PATHINFO_EXTENSION ) );
//only return some file extensions
if ( in_array($extension, array('xls','xlsx','doc','docx','pdf','rtf','zip','7z','rar')) )
$objects[] = new $record['ClassName']($record);
}
$files->merge($objects);
/* ** */
$pages->sort(array(
'Relevance' => 'DESC',
'Title' => 'ASC'
));
$news->sort(array(
'Relevance' => 'DESC',
'Date' => 'DESC'
));
$files->sort(array(
'Relevance' => 'DESC',
'Name' => 'ASC'
));
$data = array(
'Pages' => $pages,
'News' => $news,
'Files' => $files,
'Query' => $keyword
);
if ( $pages->count() == 0
&& $news->count() == 0
&& $files->count() == 0 )
{
$data['NoResults'] = 1;
}
return $this->customise($data)->renderWith(array('Search','Page'));
}
@DiscoPixel
Copy link

Hi colymba,
have you gotten your great fulltext results (including DataObjects) working with SilverStripe 3.3 yet?
I upgraded a website and the search led to a DB error:
MATCH(Title,MenuTitle,Content,MetaDescription) AGAINST ('lorem*' IN BOOLEAN MODE)) AND ("SiteTree_Live"."ClassName" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)) ORDER BY "SiteTree_Live"."Sort" ASC You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use...

@devsekhar
Copy link

It is not working for me as well. @DiscoPixel Did you got any solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment