Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

silverstripe dataobject searchable

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

like image 495
spierala Avatar asked Dec 21 '22 10:12

spierala


1 Answers

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 DataObjectI 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.

like image 62
colymba Avatar answered Jan 04 '23 02:01

colymba