Version: current

      1 <?php
      2 /**
      3  * Standard basic search form which conducts a fulltext search on all {@link SiteTree}
      4  * objects.
      5  *
      6  * If multilingual content is enabled through the {@link Translatable} extension,
      7  * only pages the currently set language on the holder for this searchform are found.
      8  * The language is set through a hidden field in the form, which is prepoluated
      9  * with {@link Translatable::get_current_locale()} when then form is constructed.
     10  *
     11  * @see Use ModelController and SearchContext for a more generic search implementation based around DataObject
     12  * @package sapphire
     13  * @subpackage search
     14  */
     15 class SearchForm extends Form {
     16
     17     private static $search_mode = 'morphy';
     18
     19     /**
     20      * устанавливает режим поиска
     21      *
     22      * @param string $val - режим
     23      *  * morphy - морфологический поиск (спец. символы не работают)
     24      *  * extended - поиск с выражениями: +слово -слово "как есть"
     25      *  * simple - поиск слов "как есть"
     26      */
     27     static function set_serch_mode($val) {
     28         self::$search_mode = $val;
     29     }
     30
     31     /**
     32      * @var int $pageLength How many results are shown per page.
     33      * Relies on pagination being implemented in the search results template.
     34      */
     35     protected $pageLength = 10;
     36
     37     /**
     38      * Classes to search
     39      */
     40     protected $classesToSearch = array(
     41         "SiteTree", "File"
     42     );
     43
     44     /**
     45      *
     46      * @param Controller $controller
     47      * @param string $name The name of the form (used in URL addressing)
     48      * @param FieldSet $fields Optional, defaults to a single field named "Search". Search logic needs to be customized
     49      *  if fields are added to the form.
     50      * @param FieldSet $actions Optional, defaults to a single field named "Go".
     51      */
     52     function __construct($controller, $name, $fields = null, $actions = null) {
     53         if(!$fields) {
     54             $fields = new FieldSet(
     55                 new TextField('Search', _t('SearchForm.SEARCH', 'Search')
     56             ));
     57         }
     58
     59         if(singleton('SiteTree')->hasExtension('Translatable')) {
     60             $fields->push(new HiddenField('locale', 'locale', Translatable::get_current_locale()));
     61         }
     62
     63         if(!$actions) {
     64             $actions = new FieldSet(
     65                 new FormAction("getResults", _t('SearchForm.GO', 'Go'))
     66             );
     67         }
     68
     69         parent::__construct($controller, $name, $fields, $actions);
     70
     71         $this->setFormMethod('get');
     72
     73         $this->disableSecurityToken();
     74         $this->getValidator()->setJavascriptValidationHandler('none');
     75     }
     76
     77     public function forTemplate() {
     78         return $this->renderWith(array(
     79             $this->template,
     80             'SearchForm',
     81             'Form'
     82         ));
     83     }
     84
     85     /**
     86      * Set the classes to search.
     87      * Currently you can only choose from "SiteTree" and "File", but a future version might improve this.
     88      */
     89     function classesToSearch($classes) {
     90         $illegalClasses = array_diff($classes, array('SiteTree', 'File'));
     91         if($illegalClasses) {
     92             user_error("SearchForm::classesToSearch() passed illegal classes '" . implode("', '", $illegalClasses) . "'.  At this stage, only File and SiteTree are allowed", E_USER_WARNING);
     93         }
     94         $legalClasses = array_intersect($classes, array('SiteTree', 'File'));
     95         $this->classesToSearch = $legalClasses;
     96     }
     97
     98     /**
     99      * Return dataObjectSet of the results using $_REQUEST to get info from form.
    100      * Wraps around {@link searchEngine()}.
    101      *
    102      * @param int $pageLength DEPRECATED 2.3 Use SearchForm->pageLength
    103      * @param array $data Request data as an associative array. Should contain at least a key 'Search' with all searched keywords.
    104      * @return DataObjectSet
    105      */
    106     public function getResults($pageLength = null, $data = null){
    107         // legacy usage: $data was defaulting to $_REQUEST, parameter not passed in doc.silverstripe.org tutorials
    108         if(!isset($data) || !is_array($data)) $data = $_REQUEST;
    109
    110         // set language (if present)
    111         if(singleton('SiteTree')->hasExtension('Translatable') && isset($data['locale'])) {
    112             $origLocale = Translatable::get_current_locale();
    113             Translatable::set_current_locale($data['locale']);
    114         }
    115
    116         if (self::$search_mode == 'morphy' && class_exists('SSMorphy')) {
    117             // TODO 'слово-слово' (например Санкт-Петербург) -> ???, пока: 'слово слово'
    118             $keywords = trim(preg_replace('/[^a-zA-Zа-яА-Я0-9\s]+/u', ' ', $data['Search']));
    119             $keywords = SSMorphy::createSearchCriteria($keywords);
    120         }
    121         elseif (self::$search_mode == 'extended') {
    122             // допускается +слово, -слово, "фраза как есть"
    123             $keywords = trim(preg_replace('/[^a-zA-Zа-яА-Я0-9\s"+-]+/u', ' ', $data['Search']));
    124             // 'слово+слово' -> 'слово +слово'
    125             $keywords = preg_replace('/(\S)\+/u', '$1 +', $keywords);
    126             // 'слово-слово' (например Санкт-Петербург) -> '"слово-слово"'
    127             $keywords = preg_replace('/(\w+)-(\w+)/u', '"$1-$2"', $keywords);
    128             // доводим кавычки до четного числа
    129             if (substr_count($keywords, '"') % 2 > 0) {
    130                 $keywords = $keywords . '"';
    131             }
    132             // любые окончания
    133             $keywords = $this->addStarsToKeywords($keywords);
    134         }
    135         else {
    136             // simple mode - режем все спецсимволы, для поиска в natural mode
    137             $keywords = trim(preg_replace('/[^a-zA-Zа-яА-Я0-9\s]+/u', ' ', $data['Search']));
    138         }
    139
    140         if(!$pageLength) $pageLength = $this->pageLength;
    141         $start = isset($_GET['start']) ? (int)$_GET['start'] : 0;
    142
    143         if(strpos($keywords, '"') !== false || strpos($keywords, '+') !== false || strpos($keywords, '-') !== false || strpos($keywords, '*') !== false) {
    144             $results = DB::getConn()->searchEngine($this->classesToSearch, $keywords, $start, $pageLength, "Relevance DESC", "", true);
    145         } else {
    146             $results = DB::getConn()->searchEngine($this->classesToSearch, $keywords, $start, $pageLength);
    147         }
    148
    149         // filter by permission
    150         if($results) foreach($results as $result) {
    151             if(!$result->canView()) {
    152                 $results->remove($result);
    153             }
    154             else {
    155                 // searchEngine() по-умолчанию вытаскивает только поля $ID, $Title, $Link и $Content
    156                 // если $Link определен как метод зависящий от других полей (например от $LinkType в анонсах)
    157                 // он будет определен неправильно - поэтому вытаскиваем объекты правильно
    158                 $results->replace($result, DataObject::get_by_id($result->ClassName, $result->ID));
    159             }
    160         }
    161
    162         // reset locale
    163         if(singleton('SiteTree')->hasExtension('Translatable') && isset($data['locale'])) {
    164             Translatable::set_current_locale($origLocale);
    165         }
    166
    167         return $results;
    168     }
    169
    170     /**
    171      * Добавляет * в конец слов с учетом других значений boolean mode
    172      *
    173      * @param string $keywords - строка для поиска
    174      *
    175      * @return string
    176      */
    177     protected function addStarsToKeywords($keywords) {
    178         if (!$keywords) return "";
    179         $splitWords = preg_split("/\s+/" , $keywords);
    180         while (list($i, $word) = each($splitWords)) {
    181             if ($word[0] == '"') {
    182                 // пропускаем до закрывающей кавычки
    183                 while (list($i, $subword) = each($splitWords)) {
    184                     $word .= ' ' . $subword;
    185                     if (substr($subword,-1) == '"') break;
    186                 }
    187             } elseif ($word[0] == '(') {
    188                 // пропускаем до закрывающей скобки
    189                 while (list($i, $subword) = each($splitWords)) {
    190                     $word .= ' ' . $subword;
    191                     if (substr($subword,-1) == ')') break;
    192                 }
    193             } elseif ($word[0] != '+' && $word[0] != '-' && substr($word,-1) != '*') {
    194                 $word .= '*';
    195             }
    196             $newWords[] = $word;
    197         }
    198         return implode(" ", $newWords);
    199     }
    200
    201     /**
    202      * Get the search query for display in a "You searched for ..." sentence.
    203      *
    204      * @param array $data
    205      * @return string
    206      */
    207     public function getSearchQuery($data = null) {
    208         // legacy usage: $data was defaulting to $_REQUEST, parameter not passed in doc.silverstripe.org tutorials
    209         if(!isset($data)) $data = $_REQUEST;
    210
    211         return Convert::raw2xml($data['Search']);
    212     }
    213
    214     /**
    215      * Set the maximum number of records shown on each page.
    216      *
    217      * @param int $length
    218      */
    219     public function setPageLength($length) {
    220         $this->pageLength = $length;
    221     }
    222
    223     /**
    224      * @return int
    225      */
    226     public function getPageLength() {
    227         return $this->pageLength;
    228     }
    229
    230 }
    231
    232 ?>
    233

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -