Completed
Push — master ( 13c87d...699abc )
by Mark
06:46 queued 02:05
created
code/adapters/ShopSearchMysql.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -19,7 +19,7 @@
 block discarded – undo
19 19
      * @param string $sort [optional]
20 20
      * @return ArrayData
21 21
      */
22
-    public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='')
22
+    public function searchFromVars($keywords, array $filters = array(), array $facetSpec = array(), $start = -1, $limit = -1, $sort = '')
23 23
     {
24 24
         $searchable = ShopSearch::get_searchable_classes();
25 25
         $matches = new ArrayList;
Please login to merge, or discard this patch.
Indentation   +65 added lines, -65 removed lines patch added patch discarded remove patch
@@ -9,81 +9,81 @@
 block discarded – undo
9 9
  */
10 10
 class ShopSearchMysql extends Object implements ShopSearchAdapter
11 11
 {
12
-    /**
13
-     * @param string $keywords
14
-     * @param array $filters [optional]
15
-     * @param array $facetSpec [optional]
16
-     * @param int $start [optional]
17
-     * @param int $limit [optional]
18
-     * @param string $sort [optional]
19
-     * @return ArrayData
20
-     */
21
-    public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='')
22
-    {
23
-        $searchable = ShopSearch::get_searchable_classes();
24
-        $matches = new ArrayList;
12
+	/**
13
+	 * @param string $keywords
14
+	 * @param array $filters [optional]
15
+	 * @param array $facetSpec [optional]
16
+	 * @param int $start [optional]
17
+	 * @param int $limit [optional]
18
+	 * @param string $sort [optional]
19
+	 * @return ArrayData
20
+	 */
21
+	public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='')
22
+	{
23
+		$searchable = ShopSearch::get_searchable_classes();
24
+		$matches = new ArrayList;
25 25
 
26
-        foreach ($searchable as $className) {
27
-            $list = DataObject::get($className);
26
+		foreach ($searchable as $className) {
27
+			$list = DataObject::get($className);
28 28
 
29
-            // get searchable fields
30
-            $keywordFields = $this->getSearchFields($className);
29
+			// get searchable fields
30
+			$keywordFields = $this->getSearchFields($className);
31 31
 
32
-            // build the filter
33
-            $filter = array();
32
+			// build the filter
33
+			$filter = array();
34 34
 
35
-            // Use parametrized query if SilverStripe >= 3.2
36
-            if (SHOP_SEARCH_IS_SS32) {
37
-                foreach ($keywordFields as $indexFields) {
38
-                    $filter[] = array("MATCH ($indexFields) AGAINST (?)" => $keywords);
39
-                }
40
-                $list = $list->whereAny($filter);
41
-            } else {
42
-                foreach ($keywordFields as $indexFields) {
43
-                    $filter[] = sprintf("MATCH ($indexFields) AGAINST ('%s')", Convert::raw2sql($keywords));
44
-                }
45
-                // join all the filters with an "OR" statement
46
-                $list = $list->where(implode(' OR ', $filter));
47
-            }
35
+			// Use parametrized query if SilverStripe >= 3.2
36
+			if (SHOP_SEARCH_IS_SS32) {
37
+				foreach ($keywordFields as $indexFields) {
38
+					$filter[] = array("MATCH ($indexFields) AGAINST (?)" => $keywords);
39
+				}
40
+				$list = $list->whereAny($filter);
41
+			} else {
42
+				foreach ($keywordFields as $indexFields) {
43
+					$filter[] = sprintf("MATCH ($indexFields) AGAINST ('%s')", Convert::raw2sql($keywords));
44
+				}
45
+				// join all the filters with an "OR" statement
46
+				$list = $list->where(implode(' OR ', $filter));
47
+			}
48 48
 
49
-            // add in any other filters
50
-            $list = FacetHelper::inst()->addFiltersToDataList($list, $filters);
49
+			// add in any other filters
50
+			$list = FacetHelper::inst()->addFiltersToDataList($list, $filters);
51 51
 
52
-            // add any matches to the big list
53
-            $matches->merge($list);
54
-        }
52
+			// add any matches to the big list
53
+			$matches->merge($list);
54
+		}
55 55
 
56
-        return new ArrayData(array(
57
-            'Matches'   => $matches,
58
-            'Facets'    => FacetHelper::inst()->buildFacets($matches, $facetSpec, (bool)Config::inst()->get('ShopSearch', 'auto_facet_attributes')),
59
-        ));
60
-    }
56
+		return new ArrayData(array(
57
+			'Matches'   => $matches,
58
+			'Facets'    => FacetHelper::inst()->buildFacets($matches, $facetSpec, (bool)Config::inst()->get('ShopSearch', 'auto_facet_attributes')),
59
+		));
60
+	}
61 61
 
62 62
 
63
-    /**
64
-     * @param $className
65
-     * @return array an array containing fields per index
66
-     * @throws Exception
67
-     */
68
-    protected function getSearchFields($className)
69
-    {
70
-        $indexes = Config::inst()->get($className, 'indexes');
63
+	/**
64
+	 * @param $className
65
+	 * @return array an array containing fields per index
66
+	 * @throws Exception
67
+	 */
68
+	protected function getSearchFields($className)
69
+	{
70
+		$indexes = Config::inst()->get($className, 'indexes');
71 71
 
72
-        $indexList = array();
73
-        foreach ($indexes as $name => $index) {
74
-            if (is_array($index)) {
75
-                if (!empty($index['type']) && $index['type'] == 'fulltext' && !empty($index['value'])) {
76
-                    $indexList[] = trim($index['value']);
77
-                }
78
-            } elseif (preg_match('/fulltext\((.+)\)/', $index, $m)) {
79
-                $indexList[] = trim($m[1]);
80
-            }
81
-        }
72
+		$indexList = array();
73
+		foreach ($indexes as $name => $index) {
74
+			if (is_array($index)) {
75
+				if (!empty($index['type']) && $index['type'] == 'fulltext' && !empty($index['value'])) {
76
+					$indexList[] = trim($index['value']);
77
+				}
78
+			} elseif (preg_match('/fulltext\((.+)\)/', $index, $m)) {
79
+				$indexList[] = trim($m[1]);
80
+			}
81
+		}
82 82
 
83
-        if (count($indexList) === 0) {
84
-            throw new Exception("Class $className does not appear to have any fulltext indexes");
85
-        }
83
+		if (count($indexList) === 0) {
84
+			throw new Exception("Class $className does not appear to have any fulltext indexes");
85
+		}
86 86
 
87
-        return $indexList;
88
-    }
87
+		return $indexList;
88
+	}
89 89
 }
Please login to merge, or discard this patch.
code/ShopSearchAjax.php 1 patch
Indentation   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -9,22 +9,22 @@
 block discarded – undo
9 9
  */
10 10
 class ShopSearchAjax extends Extension
11 11
 {
12
-    /**
13
-     * @param SS_HTTPRequest $request
14
-     * @param SS_HTTPResponse $response
15
-     * @param ArrayData $results
16
-     * @param array $data
17
-     */
18
-    public function updateSearchResultsResponse(&$request, &$response, $results, $data)
19
-    {
20
-        if ($request->isAjax() && $this->owner->hasExtension('AjaxControllerExtension')) {
21
-            if (!$response) {
22
-                $response = $this->owner->getAjaxResponse();
23
-            }
24
-            $response->addRenderContext('RESULTS', $results);
25
-            $response->pushRegion('SearchResults', $results);
26
-            $response->pushRegion('SearchHeader', $results);
27
-            $response->triggerEvent('searchresults');
28
-        }
29
-    }
12
+	/**
13
+	 * @param SS_HTTPRequest $request
14
+	 * @param SS_HTTPResponse $response
15
+	 * @param ArrayData $results
16
+	 * @param array $data
17
+	 */
18
+	public function updateSearchResultsResponse(&$request, &$response, $results, $data)
19
+	{
20
+		if ($request->isAjax() && $this->owner->hasExtension('AjaxControllerExtension')) {
21
+			if (!$response) {
22
+				$response = $this->owner->getAjaxResponse();
23
+			}
24
+			$response->addRenderContext('RESULTS', $results);
25
+			$response->pushRegion('SearchResults', $results);
26
+			$response->pushRegion('SearchHeader', $results);
27
+			$response->triggerEvent('searchresults');
28
+		}
29
+	}
30 30
 }
Please login to merge, or discard this patch.
code/reports/SearchTermsReport.php 1 patch
Indentation   +29 added lines, -29 removed lines patch added patch discarded remove patch
@@ -9,35 +9,35 @@
 block discarded – undo
9 9
  */
10 10
 class SearchTermsReport extends ShopPeriodReport
11 11
 {
12
-    protected $title = "Search Terms";
13
-    protected $description = "Understand what users are searching for.";
14
-    protected $dataClass = "SearchLog";
15
-    protected $periodfield = "SearchLog.Created";
12
+	protected $title = "Search Terms";
13
+	protected $description = "Understand what users are searching for.";
14
+	protected $dataClass = "SearchLog";
15
+	protected $periodfield = "SearchLog.Created";
16 16
 
17
-    public function columns()
18
-    {
19
-        return array(
20
-            "Query" => array(
21
-                "title" => "Query",
22
-                "formatting" => '<a href=\"home/SearchForm?q=$ATT_val($Query)\" target=\"_new\">$Query</a>'
23
-            ),
24
-            'NumResults' => 'Results',
25
-            'Quantity' => 'Searches',
26
-            'MostRecent' => 'Most Recent',
27
-        );
28
-    }
17
+	public function columns()
18
+	{
19
+		return array(
20
+			"Query" => array(
21
+				"title" => "Query",
22
+				"formatting" => '<a href=\"home/SearchForm?q=$ATT_val($Query)\" target=\"_new\">$Query</a>'
23
+			),
24
+			'NumResults' => 'Results',
25
+			'Quantity' => 'Searches',
26
+			'MostRecent' => 'Most Recent',
27
+		);
28
+	}
29 29
 
30
-    public function query($params)
31
-    {
32
-        $query = parent::query($params);
33
-        $query->selectField($this->periodfield, "FilterPeriod")
34
-            ->addSelect("SearchLog.Query")
35
-            ->selectField("Count(SearchLog.ID)", "Quantity")
36
-            ->selectField("Max(SearchLog.Created)", "MostRecent")
37
-            ->selectField("Max(SearchLog.NumResults)", "NumResults");
38
-        $query->addGroupby("SearchLog.Query");
39
-        $query->addWhere("\"SearchLog\".\"Filters\" is null AND \"SearchLog\".\"ParentSearchID\" = '0'");
40
-        $query->setOrderBy("Quantity", "DESC");
41
-        return $query;
42
-    }
30
+	public function query($params)
31
+	{
32
+		$query = parent::query($params);
33
+		$query->selectField($this->periodfield, "FilterPeriod")
34
+			->addSelect("SearchLog.Query")
35
+			->selectField("Count(SearchLog.ID)", "Quantity")
36
+			->selectField("Max(SearchLog.Created)", "MostRecent")
37
+			->selectField("Max(SearchLog.NumResults)", "NumResults");
38
+		$query->addGroupby("SearchLog.Query");
39
+		$query->addWhere("\"SearchLog\".\"Filters\" is null AND \"SearchLog\".\"ParentSearchID\" = '0'");
40
+		$query->setOrderBy("Quantity", "DESC");
41
+		return $query;
42
+	}
43 43
 }
Please login to merge, or discard this patch.
code/ShopSearchForm.php 2 patches
Indentation   +141 added lines, -141 removed lines patch added patch discarded remove patch
@@ -8,145 +8,145 @@
 block discarded – undo
8 8
  */
9 9
 class ShopSearchForm extends Form
10 10
 {
11
-    /** @var bool - setting this to true will remove the category dropdwon from the form */
12
-    private static $disable_category_dropdown = false;
13
-
14
-    /** @var string - this probably ought to be overridden with a vfi or solr facet unless you're products are only in one category */
15
-    private static $category_field = 'f[ParentID]';
16
-
17
-    /** @var string - setting to 'NONE' will mean the category dropdwon will have no empty option */
18
-    private static $category_empty_string = 'All Products';
19
-
20
-    /** @var int - how deep to list the categories for the dropdown */
21
-    private static $category_max_depth = 10;
22
-
23
-
24
-    /**
25
-     * @param Controller $controller
26
-     * @param String     $method
27
-     * @param string     $suggestURL
28
-     */
29
-    public function __construct($controller, $method, $suggestURL = '')
30
-    {
31
-        $searchField = TextField::create('q', '');
32
-        $searchField->setAttribute('placeholder', _t('ShopSearch.SEARCH', 'Search'));
33
-        if ($suggestURL) {
34
-            $searchField->setAttribute('data-suggest-url', $suggestURL);
35
-        }
36
-
37
-        $fields = FieldList::create($searchField);
38
-        if (!self::config()->disable_category_dropdown) {
39
-            $cats     = ShopSearch::get_category_hierarchy(0, '', self::config()->category_max_depth);
40
-            $catField = DropdownField::create(self::get_category_field(), '', $cats, Session::get('LastSearchCatID'));
41
-
42
-            $emptyString = self::config()->category_empty_string;
43
-            if ($emptyString !== 'NONE') {
44
-                $catField->setEmptyString(_t('ShopSearch.'.$emptyString, $emptyString));
45
-            }
46
-
47
-            $fields->push($catField);
48
-        }
49
-
50
-        parent::__construct($controller, $method, $fields, FieldList::create(array(FormAction::create('results', _t('ShopSearch.GO', 'Go')))));
51
-
52
-        $this->setFormMethod('GET');
53
-        $this->disableSecurityToken();
54
-        if ($c = self::config()->css_classes) {
55
-            $this->addExtraClass($c);
56
-        }
57
-
58
-        Requirements::css(SHOP_SEARCH_FOLDER . '/css/ShopSearch.css');
59
-
60
-        if (Config::inst()->get('ShopSearch', 'suggest_enabled')) {
61
-            Requirements::javascript(THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js');
62
-            Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
63
-            Requirements::javascript(SHOP_SEARCH_FOLDER . '/javascript/search.suggest.js');
64
-            Requirements::javascript(SHOP_SEARCH_FOLDER . '/javascript/search.js');
65
-        }
66
-    }
67
-
68
-
69
-    /**
70
-     * @return string
71
-     */
72
-    public static function get_category_field()
73
-    {
74
-        return Config::inst()->get('ShopSearchForm', 'category_field');
75
-    }
76
-
77
-
78
-    /**
79
-     * @param array $data
80
-     * @return mixed
81
-     */
82
-    public function results(array $data)
83
-    {
84
-        // do the search
85
-        $results  = ShopSearch::inst()->search($data);
86
-        $request  = $this->controller->getRequest();
87
-        $baseLink = $request->getURL(false);
88
-
89
-        // if there was only one category filter, remember it for the category dropdown to retain it's value
90
-        if (!ShopSearchForm::config()->disable_category_dropdown) {
91
-            $qs_filters  = (string)Config::inst()->get('ShopSearch', 'qs_filters');
92
-            $categoryKey = (string)ShopSearchForm::config()->category_field;
93
-
94
-            if (preg_match('/\[(.+)\]/', $categoryKey, $matches)) {
95
-                // get right of the f[] around the actual key if present
96
-                $categoryKey = $matches[1];
97
-            }
98
-
99
-            if (!empty($data[$qs_filters][$categoryKey])) {
100
-                $categoryID = $data[$qs_filters][$categoryKey];
101
-                if (is_numeric($categoryID)) {
102
-                    // If it's set in the dropdown it will just be a number
103
-                    // If it's set from the checkboxes it will be something like LIST~1,2,3,4
104
-                    // We only want to remember the value in the former case
105
-                    Session::set('LastSearchCatID', $categoryID);
106
-                }
107
-            } else {
108
-                // If they unchecked every value, then clear the dropdown as well
109
-                Session::clear('LastSearchCatID');
110
-            }
111
-        }
112
-
113
-        // add links for any facets
114
-        if ($results->Facets && $results->Facets->count()) {
115
-            $qs_ps      = (string)Config::inst()->get('ShopSearch', 'qs_parent_search');
116
-            $baseParams = array_merge($data, array($qs_ps => $results->SearchLogID));
117
-            unset($baseParams['url']);
118
-            $results->Facets = FacetHelper::inst()->insertFacetLinks($results->Facets, $baseParams, $baseLink);
119
-        }
120
-
121
-        // add a dropdown for sorting
122
-        $qs_sort    = (string)Config::inst()->get('ShopSearch', 'qs_sort');
123
-        $options    = Config::inst()->get('ShopSearch', 'sort_options');
124
-        $sortParams = array_merge($data, array($qs_sort => 'NEWSORTVALUE'));
125
-        unset($sortParams['url']);
126
-        $results->SortControl = DropdownField::create($qs_sort, ShopSearch::config()->sort_label, $options, $results->Sort)
127
-            ->setAttribute('data-url', $baseLink . '?' . http_build_query($sortParams));
128
-
129
-        // a little more output management
130
-        $results->Title = "Search Results";
131
-        $results->Results = $results->Matches; // this makes us compatible with the default search template
132
-
133
-        // Give a hook for the parent controller to format the results, for example,
134
-        // interpreting filters in a specific way to affect the title or content
135
-        // when no results are returned. Since this is domain-specific we just leave
136
-        // it up to the host app.
137
-        if ($this->controller->hasMethod('onBeforeSearchDisplay')) {
138
-            $this->controller->onBeforeSearchDisplay($results);
139
-        }
140
-
141
-        // give a hook for processing ajax requests through a different template (i.e. for returning only fragments)
142
-        $tpl = Config::inst()->get('ShopSearch', 'ajax_results_template');
143
-        if (!empty($tpl) && Director::is_ajax()) {
144
-            return $this->controller->customise($results)->renderWith($tpl);
145
-        }
146
-
147
-        // Give a hook for modifying the search responses
148
-        $this->controller->extend('updateSearchResultsResponse', $request, $response, $results, $data);
149
-
150
-        return $response ?: $this->controller->customise($results)->renderWith(array('ShopSearch_results', 'Page_results', 'Page'));
151
-    }
11
+	/** @var bool - setting this to true will remove the category dropdwon from the form */
12
+	private static $disable_category_dropdown = false;
13
+
14
+	/** @var string - this probably ought to be overridden with a vfi or solr facet unless you're products are only in one category */
15
+	private static $category_field = 'f[ParentID]';
16
+
17
+	/** @var string - setting to 'NONE' will mean the category dropdwon will have no empty option */
18
+	private static $category_empty_string = 'All Products';
19
+
20
+	/** @var int - how deep to list the categories for the dropdown */
21
+	private static $category_max_depth = 10;
22
+
23
+
24
+	/**
25
+	 * @param Controller $controller
26
+	 * @param String     $method
27
+	 * @param string     $suggestURL
28
+	 */
29
+	public function __construct($controller, $method, $suggestURL = '')
30
+	{
31
+		$searchField = TextField::create('q', '');
32
+		$searchField->setAttribute('placeholder', _t('ShopSearch.SEARCH', 'Search'));
33
+		if ($suggestURL) {
34
+			$searchField->setAttribute('data-suggest-url', $suggestURL);
35
+		}
36
+
37
+		$fields = FieldList::create($searchField);
38
+		if (!self::config()->disable_category_dropdown) {
39
+			$cats     = ShopSearch::get_category_hierarchy(0, '', self::config()->category_max_depth);
40
+			$catField = DropdownField::create(self::get_category_field(), '', $cats, Session::get('LastSearchCatID'));
41
+
42
+			$emptyString = self::config()->category_empty_string;
43
+			if ($emptyString !== 'NONE') {
44
+				$catField->setEmptyString(_t('ShopSearch.'.$emptyString, $emptyString));
45
+			}
46
+
47
+			$fields->push($catField);
48
+		}
49
+
50
+		parent::__construct($controller, $method, $fields, FieldList::create(array(FormAction::create('results', _t('ShopSearch.GO', 'Go')))));
51
+
52
+		$this->setFormMethod('GET');
53
+		$this->disableSecurityToken();
54
+		if ($c = self::config()->css_classes) {
55
+			$this->addExtraClass($c);
56
+		}
57
+
58
+		Requirements::css(SHOP_SEARCH_FOLDER . '/css/ShopSearch.css');
59
+
60
+		if (Config::inst()->get('ShopSearch', 'suggest_enabled')) {
61
+			Requirements::javascript(THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js');
62
+			Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
63
+			Requirements::javascript(SHOP_SEARCH_FOLDER . '/javascript/search.suggest.js');
64
+			Requirements::javascript(SHOP_SEARCH_FOLDER . '/javascript/search.js');
65
+		}
66
+	}
67
+
68
+
69
+	/**
70
+	 * @return string
71
+	 */
72
+	public static function get_category_field()
73
+	{
74
+		return Config::inst()->get('ShopSearchForm', 'category_field');
75
+	}
76
+
77
+
78
+	/**
79
+	 * @param array $data
80
+	 * @return mixed
81
+	 */
82
+	public function results(array $data)
83
+	{
84
+		// do the search
85
+		$results  = ShopSearch::inst()->search($data);
86
+		$request  = $this->controller->getRequest();
87
+		$baseLink = $request->getURL(false);
88
+
89
+		// if there was only one category filter, remember it for the category dropdown to retain it's value
90
+		if (!ShopSearchForm::config()->disable_category_dropdown) {
91
+			$qs_filters  = (string)Config::inst()->get('ShopSearch', 'qs_filters');
92
+			$categoryKey = (string)ShopSearchForm::config()->category_field;
93
+
94
+			if (preg_match('/\[(.+)\]/', $categoryKey, $matches)) {
95
+				// get right of the f[] around the actual key if present
96
+				$categoryKey = $matches[1];
97
+			}
98
+
99
+			if (!empty($data[$qs_filters][$categoryKey])) {
100
+				$categoryID = $data[$qs_filters][$categoryKey];
101
+				if (is_numeric($categoryID)) {
102
+					// If it's set in the dropdown it will just be a number
103
+					// If it's set from the checkboxes it will be something like LIST~1,2,3,4
104
+					// We only want to remember the value in the former case
105
+					Session::set('LastSearchCatID', $categoryID);
106
+				}
107
+			} else {
108
+				// If they unchecked every value, then clear the dropdown as well
109
+				Session::clear('LastSearchCatID');
110
+			}
111
+		}
112
+
113
+		// add links for any facets
114
+		if ($results->Facets && $results->Facets->count()) {
115
+			$qs_ps      = (string)Config::inst()->get('ShopSearch', 'qs_parent_search');
116
+			$baseParams = array_merge($data, array($qs_ps => $results->SearchLogID));
117
+			unset($baseParams['url']);
118
+			$results->Facets = FacetHelper::inst()->insertFacetLinks($results->Facets, $baseParams, $baseLink);
119
+		}
120
+
121
+		// add a dropdown for sorting
122
+		$qs_sort    = (string)Config::inst()->get('ShopSearch', 'qs_sort');
123
+		$options    = Config::inst()->get('ShopSearch', 'sort_options');
124
+		$sortParams = array_merge($data, array($qs_sort => 'NEWSORTVALUE'));
125
+		unset($sortParams['url']);
126
+		$results->SortControl = DropdownField::create($qs_sort, ShopSearch::config()->sort_label, $options, $results->Sort)
127
+			->setAttribute('data-url', $baseLink . '?' . http_build_query($sortParams));
128
+
129
+		// a little more output management
130
+		$results->Title = "Search Results";
131
+		$results->Results = $results->Matches; // this makes us compatible with the default search template
132
+
133
+		// Give a hook for the parent controller to format the results, for example,
134
+		// interpreting filters in a specific way to affect the title or content
135
+		// when no results are returned. Since this is domain-specific we just leave
136
+		// it up to the host app.
137
+		if ($this->controller->hasMethod('onBeforeSearchDisplay')) {
138
+			$this->controller->onBeforeSearchDisplay($results);
139
+		}
140
+
141
+		// give a hook for processing ajax requests through a different template (i.e. for returning only fragments)
142
+		$tpl = Config::inst()->get('ShopSearch', 'ajax_results_template');
143
+		if (!empty($tpl) && Director::is_ajax()) {
144
+			return $this->controller->customise($results)->renderWith($tpl);
145
+		}
146
+
147
+		// Give a hook for modifying the search responses
148
+		$this->controller->extend('updateSearchResultsResponse', $request, $response, $results, $data);
149
+
150
+		return $response ?: $this->controller->customise($results)->renderWith(array('ShopSearch_results', 'Page_results', 'Page'));
151
+	}
152 152
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -55,13 +55,13 @@  discard block
 block discarded – undo
55 55
             $this->addExtraClass($c);
56 56
         }
57 57
 
58
-        Requirements::css(SHOP_SEARCH_FOLDER . '/css/ShopSearch.css');
58
+        Requirements::css(SHOP_SEARCH_FOLDER.'/css/ShopSearch.css');
59 59
 
60 60
         if (Config::inst()->get('ShopSearch', 'suggest_enabled')) {
61
-            Requirements::javascript(THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js');
62
-            Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
63
-            Requirements::javascript(SHOP_SEARCH_FOLDER . '/javascript/search.suggest.js');
64
-            Requirements::javascript(SHOP_SEARCH_FOLDER . '/javascript/search.js');
61
+            Requirements::javascript(THIRDPARTY_DIR.'/jquery-ui/jquery-ui.js');
62
+            Requirements::css(THIRDPARTY_DIR.'/jquery-ui-themes/smoothness/jquery-ui.css');
63
+            Requirements::javascript(SHOP_SEARCH_FOLDER.'/javascript/search.suggest.js');
64
+            Requirements::javascript(SHOP_SEARCH_FOLDER.'/javascript/search.js');
65 65
         }
66 66
     }
67 67
 
@@ -124,7 +124,7 @@  discard block
 block discarded – undo
124 124
         $sortParams = array_merge($data, array($qs_sort => 'NEWSORTVALUE'));
125 125
         unset($sortParams['url']);
126 126
         $results->SortControl = DropdownField::create($qs_sort, ShopSearch::config()->sort_label, $options, $results->Sort)
127
-            ->setAttribute('data-url', $baseLink . '?' . http_build_query($sortParams));
127
+            ->setAttribute('data-url', $baseLink.'?'.http_build_query($sortParams));
128 128
 
129 129
         // a little more output management
130 130
         $results->Title = "Search Results";
Please login to merge, or discard this patch.
code/helpers/VirtualFieldIndexQueuedJob.php 2 patches
Indentation   +46 added lines, -46 removed lines patch added patch discarded remove patch
@@ -9,64 +9,64 @@
 block discarded – undo
9 9
  * @subpackage helpers
10 10
  */
11 11
 if (!interface_exists('QueuedJob')) {
12
-    return;
12
+	return;
13 13
 }
14 14
 
15 15
 class VirtualFieldIndexQueuedJob extends AbstractQueuedJob implements QueuedJob
16 16
 {
17
-    /**
18
-     * The QueuedJob queue to use when processing updates
19
-     * @config
20
-     * @var int
21
-     */
22
-    private static $reindex_queue = 2; // QueuedJob::QUEUED;
17
+	/**
18
+	 * The QueuedJob queue to use when processing updates
19
+	 * @config
20
+	 * @var int
21
+	 */
22
+	private static $reindex_queue = 2; // QueuedJob::QUEUED;
23 23
 
24 24
 
25
-    /**
26
-     * @param DataObject $object
27
-     * @param array $fields
28
-     */
29
-    public function __construct($object = null, $fields = array())
30
-    {
31
-        if ($object) {
32
-            $this->setObject($object);
33
-        }
34
-        $this->rebuildFields = $fields;
35
-    }
25
+	/**
26
+	 * @param DataObject $object
27
+	 * @param array $fields
28
+	 */
29
+	public function __construct($object = null, $fields = array())
30
+	{
31
+		if ($object) {
32
+			$this->setObject($object);
33
+		}
34
+		$this->rebuildFields = $fields;
35
+	}
36 36
 
37 37
 
38
-    /**
39
-     * Helper method
40
-     */
41
-    public function triggerProcessing()
42
-    {
43
-        singleton('QueuedJobService')->queueJob($this);
44
-    }
38
+	/**
39
+	 * Helper method
40
+	 */
41
+	public function triggerProcessing()
42
+	{
43
+		singleton('QueuedJobService')->queueJob($this);
44
+	}
45 45
 
46 46
 
47
-    /**
48
-     * @return string
49
-     */
50
-    public function getTitle()
51
-    {
52
-        $obj = $this->getObject();
53
-        return "Update Virtual Field Indexes: " . ($obj ? $obj->getTitle() : '???');
54
-    }
47
+	/**
48
+	 * @return string
49
+	 */
50
+	public function getTitle()
51
+	{
52
+		$obj = $this->getObject();
53
+		return "Update Virtual Field Indexes: " . ($obj ? $obj->getTitle() : '???');
54
+	}
55 55
 
56 56
 
57
-    /**
58
-     * Reprocess any needed fields
59
-     */
60
-    public function process()
61
-    {
62
-        Versioned::reading_stage('Stage');
63
-        /** @var DataObject|VirtualFieldIndex $obj */
64
-        $obj = $this->getObject();
57
+	/**
58
+	 * Reprocess any needed fields
59
+	 */
60
+	public function process()
61
+	{
62
+		Versioned::reading_stage('Stage');
63
+		/** @var DataObject|VirtualFieldIndex $obj */
64
+		$obj = $this->getObject();
65 65
 
66
-        if ($obj) {
67
-            $obj->rebuildVFI();
68
-        }
66
+		if ($obj) {
67
+			$obj->rebuildVFI();
68
+		}
69 69
 
70
-        $this->isComplete = true;
71
-    }
70
+		$this->isComplete = true;
71
+	}
72 72
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -50,7 +50,7 @@
 block discarded – undo
50 50
     public function getTitle()
51 51
     {
52 52
         $obj = $this->getObject();
53
-        return "Update Virtual Field Indexes: " . ($obj ? $obj->getTitle() : '???');
53
+        return "Update Virtual Field Indexes: ".($obj ? $obj->getTitle() : '???');
54 54
     }
55 55
 
56 56
 
Please login to merge, or discard this patch.
code/helpers/BetweenFilter.php 1 patch
Indentation   +73 added lines, -73 removed lines patch added patch discarded remove patch
@@ -10,89 +10,89 @@
 block discarded – undo
10 10
  */
11 11
 class BetweenFilter extends SearchFilter
12 12
 {
13
-    /**
14
-     * @param DataQuery $query
15
-     * @return DataQuery
16
-     */
17
-    protected function applyOne(DataQuery $query)
18
-    {
19
-        $this->model = $query->applyRelation($this->relation);
20
-        $value = $this->getDbFormattedValue();
13
+	/**
14
+	 * @param DataQuery $query
15
+	 * @return DataQuery
16
+	 */
17
+	protected function applyOne(DataQuery $query)
18
+	{
19
+		$this->model = $query->applyRelation($this->relation);
20
+		$value = $this->getDbFormattedValue();
21 21
 
22
-        if (is_numeric($value)) {
23
-            $filter = sprintf("%s > %s", $this->getDbName(), Convert::raw2sql($value));
24
-        } else {
25
-            $filter = sprintf("%s > '%s'", $this->getDbName(), Convert::raw2sql($value));
26
-        }
22
+		if (is_numeric($value)) {
23
+			$filter = sprintf("%s > %s", $this->getDbName(), Convert::raw2sql($value));
24
+		} else {
25
+			$filter = sprintf("%s > '%s'", $this->getDbName(), Convert::raw2sql($value));
26
+		}
27 27
 
28
-        return $query->where($filter);
29
-    }
28
+		return $query->where($filter);
29
+	}
30 30
 
31
-    /**
32
-     * @param DataQuery $query
33
-     * @return DataQuery
34
-     */
35
-    protected function excludeOne(DataQuery $query)
36
-    {
37
-        $this->model = $query->applyRelation($this->relation);
38
-        $value = $this->getDbFormattedValue();
31
+	/**
32
+	 * @param DataQuery $query
33
+	 * @return DataQuery
34
+	 */
35
+	protected function excludeOne(DataQuery $query)
36
+	{
37
+		$this->model = $query->applyRelation($this->relation);
38
+		$value = $this->getDbFormattedValue();
39 39
 
40
-        if (is_numeric($value)) {
41
-            $filter = sprintf("%s <= %s", $this->getDbName(), Convert::raw2sql($value));
42
-        } else {
43
-            $filter = sprintf("%s <= '%s'", $this->getDbName(), Convert::raw2sql($value));
44
-        }
40
+		if (is_numeric($value)) {
41
+			$filter = sprintf("%s <= %s", $this->getDbName(), Convert::raw2sql($value));
42
+		} else {
43
+			$filter = sprintf("%s <= '%s'", $this->getDbName(), Convert::raw2sql($value));
44
+		}
45 45
 
46
-        return $query->where($filter);
47
-    }
46
+		return $query->where($filter);
47
+	}
48 48
 
49
-    /**
50
-     * @param DataQuery $query
51
-     * @return DataQuery
52
-     */
53
-    protected function applyMany(DataQuery $query)
54
-    {
55
-        $this->model = $query->applyRelation($this->relation);
49
+	/**
50
+	 * @param DataQuery $query
51
+	 * @return DataQuery
52
+	 */
53
+	protected function applyMany(DataQuery $query)
54
+	{
55
+		$this->model = $query->applyRelation($this->relation);
56 56
 
57
-        $filters = array();
58
-        $ops = array('>=', '<=');
59
-        foreach ($this->getValue() as $i => $value) {
60
-            if (is_numeric($value)) {
61
-                $filters[] = sprintf("%s %s %s", $this->getDbName(), $ops[$i], Convert::raw2sql($value));
62
-            } else {
63
-                $filters[] = sprintf("%s %s '%s'", $this->getDbName(), $ops[$i], Convert::raw2sql($value));
64
-            }
65
-        }
57
+		$filters = array();
58
+		$ops = array('>=', '<=');
59
+		foreach ($this->getValue() as $i => $value) {
60
+			if (is_numeric($value)) {
61
+				$filters[] = sprintf("%s %s %s", $this->getDbName(), $ops[$i], Convert::raw2sql($value));
62
+			} else {
63
+				$filters[] = sprintf("%s %s '%s'", $this->getDbName(), $ops[$i], Convert::raw2sql($value));
64
+			}
65
+		}
66 66
 
67
-        return $query->where(implode(' AND ', $filters));
68
-    }
67
+		return $query->where(implode(' AND ', $filters));
68
+	}
69 69
 
70
-    /**
71
-     * @param DataQuery $query
72
-     * @return DataQuery
73
-     */
74
-    protected function excludeMany(DataQuery $query)
75
-    {
76
-        $this->model = $query->applyRelation($this->relation);
70
+	/**
71
+	 * @param DataQuery $query
72
+	 * @return DataQuery
73
+	 */
74
+	protected function excludeMany(DataQuery $query)
75
+	{
76
+		$this->model = $query->applyRelation($this->relation);
77 77
 
78
-        $filters = array();
79
-        $ops = array('<', '>');
80
-        foreach ($this->getValue() as $i => $value) {
81
-            if (is_numeric($value)) {
82
-                $filters[] = sprintf("%s %s %s", $this->getDbName(), $ops[$i], Convert::raw2sql($value));
83
-            } else {
84
-                $filters[] = sprintf("%s %s '%s'", $this->getDbName(), $ops[$i], Convert::raw2sql($value));
85
-            }
86
-        }
78
+		$filters = array();
79
+		$ops = array('<', '>');
80
+		foreach ($this->getValue() as $i => $value) {
81
+			if (is_numeric($value)) {
82
+				$filters[] = sprintf("%s %s %s", $this->getDbName(), $ops[$i], Convert::raw2sql($value));
83
+			} else {
84
+				$filters[] = sprintf("%s %s '%s'", $this->getDbName(), $ops[$i], Convert::raw2sql($value));
85
+			}
86
+		}
87 87
 
88
-        return $query->where(implode(' OR ', $filters));
89
-    }
88
+		return $query->where(implode(' OR ', $filters));
89
+	}
90 90
 
91
-    /**
92
-     * @return bool
93
-     */
94
-    public function isEmpty()
95
-    {
96
-        return $this->getValue() === array() || $this->getValue() === null || $this->getValue() === '';
97
-    }
91
+	/**
92
+	 * @return bool
93
+	 */
94
+	public function isEmpty()
95
+	{
96
+		return $this->getValue() === array() || $this->getValue() === null || $this->getValue() === '';
97
+	}
98 98
 }
Please login to merge, or discard this patch.
code/adapters/ShopSearchSimple.php 2 patches
Indentation   +81 added lines, -81 removed lines patch added patch discarded remove patch
@@ -10,95 +10,95 @@
 block discarded – undo
10 10
  */
11 11
 class ShopSearchSimple extends Object implements ShopSearchAdapter
12 12
 {
13
-    /**
14
-     * @param string $keywords
15
-     * @param array $filters [optional]
16
-     * @param array $facetSpec [optional]
17
-     * @param int $start [optional]
18
-     * @param int $limit [optional]
19
-     * @param string $sort [optional]
20
-     * @return ArrayData
21
-     */
22
-    public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='')
23
-    {
24
-        $searchable = ShopSearch::get_searchable_classes();
25
-        $matches = new ArrayList;
13
+	/**
14
+	 * @param string $keywords
15
+	 * @param array $filters [optional]
16
+	 * @param array $facetSpec [optional]
17
+	 * @param int $start [optional]
18
+	 * @param int $limit [optional]
19
+	 * @param string $sort [optional]
20
+	 * @return ArrayData
21
+	 */
22
+	public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='')
23
+	{
24
+		$searchable = ShopSearch::get_searchable_classes();
25
+		$matches = new ArrayList;
26 26
 
27
-        foreach ($searchable as $className) {
28
-            $list = DataObject::get($className);
27
+		foreach ($searchable as $className) {
28
+			$list = DataObject::get($className);
29 29
 
30
-            // get searchable fields
31
-            $keywordFields = $this->scaffoldSearchFields($className);
30
+			// get searchable fields
31
+			$keywordFields = $this->scaffoldSearchFields($className);
32 32
 
33
-            // convert that list into something we can pass to Datalist::filter
34
-            $keywordFilter = array();
35
-            if (!empty($keywords)) {
36
-                foreach ($keywordFields as $searchField) {
37
-                    $name = (strpos($searchField, ':') !== false) ? $searchField : "$searchField:PartialMatch";
38
-                    $keywordFilter[$name] = $keywords;
39
-                }
40
-            }
41
-            if (count($keywordFilter) > 0) {
42
-                $list = $list->filterAny($keywordFilter);
43
-            }
33
+			// convert that list into something we can pass to Datalist::filter
34
+			$keywordFilter = array();
35
+			if (!empty($keywords)) {
36
+				foreach ($keywordFields as $searchField) {
37
+					$name = (strpos($searchField, ':') !== false) ? $searchField : "$searchField:PartialMatch";
38
+					$keywordFilter[$name] = $keywords;
39
+				}
40
+			}
41
+			if (count($keywordFilter) > 0) {
42
+				$list = $list->filterAny($keywordFilter);
43
+			}
44 44
 
45
-            // add in any other filters
46
-            $list = FacetHelper::inst()->addFiltersToDataList($list, $filters);
45
+			// add in any other filters
46
+			$list = FacetHelper::inst()->addFiltersToDataList($list, $filters);
47 47
 
48
-            // add any matches to the big list
49
-            $matches->merge($list);
50
-        }
48
+			// add any matches to the big list
49
+			$matches->merge($list);
50
+		}
51 51
 
52
-        return new ArrayData(array(
53
-            'Matches'   => $matches,
54
-            'Facets'    => FacetHelper::inst()->buildFacets($matches, $facetSpec, (bool)Config::inst()->get('ShopSearch', 'auto_facet_attributes')),
55
-        ));
56
-    }
52
+		return new ArrayData(array(
53
+			'Matches'   => $matches,
54
+			'Facets'    => FacetHelper::inst()->buildFacets($matches, $facetSpec, (bool)Config::inst()->get('ShopSearch', 'auto_facet_attributes')),
55
+		));
56
+	}
57 57
 
58 58
 
59
-    /**
60
-     * This is verbatim copied from GridFieldAddExistingAutocompleter, with the exception
61
-     * that the default is 'PartialMatch' instead of 'StartsWith'
62
-     *
63
-     * @param String $dataClass - the class name
64
-     * @return Array|null - names of the searchable fields, with filters if appropriate
65
-     */
66
-    protected function scaffoldSearchFields($dataClass)
67
-    {
68
-        $obj = singleton($dataClass);
69
-        $fields = null;
70
-        if ($fieldSpecs = $obj->searchableFields()) {
71
-            $customSearchableFields = $obj->stat('searchable_fields');
72
-            foreach ($fieldSpecs as $name => $spec) {
73
-                if (is_array($spec) && array_key_exists('filter', $spec)) {
74
-                    // The searchableFields() spec defaults to PartialMatch,
75
-                    // so we need to check the original setting.
76
-                    // If the field is defined $searchable_fields = array('MyField'),
77
-                    // then default to StartsWith filter, which makes more sense in this context.
78
-                    if (!$customSearchableFields || array_search($name, $customSearchableFields)) {
79
-                        $filter = 'PartialMatch';
80
-                    } else {
81
-                        $filter = preg_replace('/Filter$/', '', $spec['filter']);
82
-                    }
59
+	/**
60
+	 * This is verbatim copied from GridFieldAddExistingAutocompleter, with the exception
61
+	 * that the default is 'PartialMatch' instead of 'StartsWith'
62
+	 *
63
+	 * @param String $dataClass - the class name
64
+	 * @return Array|null - names of the searchable fields, with filters if appropriate
65
+	 */
66
+	protected function scaffoldSearchFields($dataClass)
67
+	{
68
+		$obj = singleton($dataClass);
69
+		$fields = null;
70
+		if ($fieldSpecs = $obj->searchableFields()) {
71
+			$customSearchableFields = $obj->stat('searchable_fields');
72
+			foreach ($fieldSpecs as $name => $spec) {
73
+				if (is_array($spec) && array_key_exists('filter', $spec)) {
74
+					// The searchableFields() spec defaults to PartialMatch,
75
+					// so we need to check the original setting.
76
+					// If the field is defined $searchable_fields = array('MyField'),
77
+					// then default to StartsWith filter, which makes more sense in this context.
78
+					if (!$customSearchableFields || array_search($name, $customSearchableFields)) {
79
+						$filter = 'PartialMatch';
80
+					} else {
81
+						$filter = preg_replace('/Filter$/', '', $spec['filter']);
82
+					}
83 83
 
84
-                    if (class_exists($filter . 'Filter')) {
85
-                        $fields[] = "{$name}:{$filter}";
86
-                    } else {
87
-                        $fields[] = $name;
88
-                    }
89
-                } else {
90
-                    $fields[] = $name;
91
-                }
92
-            }
93
-        }
94
-        if (is_null($fields)) {
95
-            if ($obj->hasDatabaseField('Title')) {
96
-                $fields = array('Title');
97
-            } elseif ($obj->hasDatabaseField('Name')) {
98
-                $fields = array('Name');
99
-            }
100
-        }
84
+					if (class_exists($filter . 'Filter')) {
85
+						$fields[] = "{$name}:{$filter}";
86
+					} else {
87
+						$fields[] = $name;
88
+					}
89
+				} else {
90
+					$fields[] = $name;
91
+				}
92
+			}
93
+		}
94
+		if (is_null($fields)) {
95
+			if ($obj->hasDatabaseField('Title')) {
96
+				$fields = array('Title');
97
+			} elseif ($obj->hasDatabaseField('Name')) {
98
+				$fields = array('Name');
99
+			}
100
+		}
101 101
 
102
-        return $fields;
103
-    }
102
+		return $fields;
103
+	}
104 104
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -19,7 +19,7 @@  discard block
 block discarded – undo
19 19
      * @param string $sort [optional]
20 20
      * @return ArrayData
21 21
      */
22
-    public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='')
22
+    public function searchFromVars($keywords, array $filters = array(), array $facetSpec = array(), $start = -1, $limit = -1, $sort = '')
23 23
     {
24 24
         $searchable = ShopSearch::get_searchable_classes();
25 25
         $matches = new ArrayList;
@@ -81,7 +81,7 @@  discard block
 block discarded – undo
81 81
                         $filter = preg_replace('/Filter$/', '', $spec['filter']);
82 82
                     }
83 83
 
84
-                    if (class_exists($filter . 'Filter')) {
84
+                    if (class_exists($filter.'Filter')) {
85 85
                         $fields[] = "{$name}:{$filter}";
86 86
                     } else {
87 87
                         $fields[] = $name;
Please login to merge, or discard this patch.
code/adapters/ShopSearchSolr.php 2 patches
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -173,8 +173,8 @@  discard block
 block discarded – undo
173 173
         // create a sorting column
174 174
         if (isset($this->fieldMap['Title']) || ShopSearch::config()->solr_title_sort_field) {
175 175
             $f = empty(ShopSearch::config()->title_sort_field) ? '_titleSort' : ShopSearch::config()->solr_title_sort_field;
176
-            $xml .= "\n\t\t" . '<field name="' . $f . '" type="alphaOnlySort" indexed="true" stored="false" required="false" multiValued="false" />';
177
-            $xml .= "\n\t\t" . '<copyField source="SiteTree_Title" dest="' . $f . '"/>';
176
+            $xml .= "\n\t\t".'<field name="'.$f.'" type="alphaOnlySort" indexed="true" stored="false" required="false" multiValued="false" />';
177
+            $xml .= "\n\t\t".'<copyField source="SiteTree_Title" dest="'.$f.'"/>';
178 178
         }
179 179
 
180 180
         // create an autocomplete column
@@ -251,7 +251,7 @@  discard block
 block discarded – undo
251 251
      * @param string $sort [optional]
252 252
      * @return ArrayData
253 253
      */
254
-    public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='score desc')
254
+    public function searchFromVars($keywords, array $filters = array(), array $facetSpec = array(), $start = -1, $limit = -1, $sort = 'score desc')
255 255
     {
256 256
         $query = new SearchQuery();
257 257
         $params = array(
@@ -302,13 +302,13 @@  discard block
 block discarded – undo
302 302
         // process the keywords a bit
303 303
         $terms      = preg_split('/\s+/', trim(strtolower($keywords)));
304 304
         $lastTerm   = count($terms) > 0 ? array_pop($terms) : '';
305
-        $prefix     = count($terms) > 0 ? implode(' ', $terms) . ' ' : '';
305
+        $prefix     = count($terms) > 0 ? implode(' ', $terms).' ' : '';
306 306
         //$terms[]    = $lastTerm;
307
-        $terms[]    = $lastTerm . '*'; // this allows for partial words to still match
307
+        $terms[]    = $lastTerm.'*'; // this allows for partial words to still match
308 308
 
309 309
         // convert that to something solr adapater can handle
310 310
         $query = new SearchQuery();
311
-        $query->search('+' . implode(' +', $terms));
311
+        $query->search('+'.implode(' +', $terms));
312 312
 
313 313
         $params = array(
314 314
             'sort'          => 'score desc',
@@ -432,7 +432,7 @@  discard block
 block discarded – undo
432 432
                 }
433 433
             }
434 434
 
435
-            $fq[] = ($missing ? "+{$field}:[* TO *] " : '') . '-('.implode(' ', $excludeq).')';
435
+            $fq[] = ($missing ? "+{$field}:[* TO *] " : '').'-('.implode(' ', $excludeq).')';
436 436
         }
437 437
 
438 438
 //		if(!headers_sent()) {
@@ -474,7 +474,7 @@  discard block
 block discarded – undo
474 474
         $ret['suggestions'] = array();
475 475
         if (isset($res->facet_counts->facet_fields->_autocomplete)) {
476 476
             foreach ($res->facet_counts->facet_fields->_autocomplete as $term => $count) {
477
-                $ret['suggestions'][] = $prefix . $term;
477
+                $ret['suggestions'][] = $prefix.$term;
478 478
             }
479 479
         }
480 480
 
@@ -549,7 +549,7 @@  discard block
 block discarded – undo
549 549
                 if ($fields) {
550 550
                     $searchq = array();
551 551
                     foreach ($fields as $field) {
552
-                        $boost = (isset($search['boost'][$field])) ? '^' . $search['boost'][$field] : '';
552
+                        $boost = (isset($search['boost'][$field])) ? '^'.$search['boost'][$field] : '';
553 553
                         $searchq[] = "{$field}:".$part.$fuzzy.$boost;
554 554
                     }
555 555
                     $q[] = '+('.implode(' OR ', $searchq).')';
@@ -627,7 +627,7 @@  discard block
 block discarded – undo
627 627
                 }
628 628
             }
629 629
 
630
-            $fq[] = ($missing ? "+{$field}:[* TO *] " : '') . '-('.implode(' ', $excludeq).')';
630
+            $fq[] = ($missing ? "+{$field}:[* TO *] " : '').'-('.implode(' ', $excludeq).')';
631 631
         }
632 632
 
633 633
 //		if(!headers_sent()) {
Please login to merge, or discard this patch.
Indentation   +643 added lines, -643 removed lines patch added patch discarded remove patch
@@ -1,6 +1,6 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 if (!class_exists('SolrIndex')) {
3
-    return;
3
+	return;
4 4
 }
5 5
 
6 6
 /**
@@ -12,46 +12,46 @@  discard block
 block discarded – undo
12 12
  */
13 13
 class ShopSearchSolr extends SolrIndex implements ShopSearchAdapter
14 14
 {
15
-    /** @var array - maps our names for fields to Solr's names (i.e. Title => SiteTree_Title) */
16
-    protected $fieldMap = array();
17
-
18
-    /**
19
-     * Sets up the index
20
-     */
21
-    public function init()
22
-    {
23
-        $searchables = ShopSearch::get_searchable_classes();
24
-
25
-        // Add each class to the index
26
-        foreach ($searchables as $class) {
27
-            $this->addClass($class);
28
-        }
29
-
30
-        // add the fields they've specifically asked for
31
-        $fields = $this->getFulltextSpec();
32
-        foreach ($fields as $def) {
33
-            $this->addFulltextField($def['field'], $def['type'], $def['params']);
34
-        }
35
-
36
-        // add the filters they've asked for
37
-        $filters = $this->getFilterSpec();
38
-        foreach ($filters as $filterName => $def) {
39
-            // NOTE: I'm pulling the guts out of this function so we can access Solr's full name
40
-            // for the field (SiteTree_Title for Title) and build the fieldMap in one step instead
41
-            // of two.
42
-            //$this->addFilterField($def['field'], $def['type'], $def['params']);
43
-            $singleFilter = $this->fieldData($def['field'], $def['type'], $def['params']);
44
-            $this->filterFields = array_merge($this->filterFields, $singleFilter);
45
-            foreach ($singleFilter as $solrName => $solrDef) {
46
-                if ($def['field'] == $solrDef['field']) {
47
-                    $this->fieldMap[$filterName] = $solrName;
48
-                }
49
-            }
50
-        }
15
+	/** @var array - maps our names for fields to Solr's names (i.e. Title => SiteTree_Title) */
16
+	protected $fieldMap = array();
17
+
18
+	/**
19
+	 * Sets up the index
20
+	 */
21
+	public function init()
22
+	{
23
+		$searchables = ShopSearch::get_searchable_classes();
24
+
25
+		// Add each class to the index
26
+		foreach ($searchables as $class) {
27
+			$this->addClass($class);
28
+		}
29
+
30
+		// add the fields they've specifically asked for
31
+		$fields = $this->getFulltextSpec();
32
+		foreach ($fields as $def) {
33
+			$this->addFulltextField($def['field'], $def['type'], $def['params']);
34
+		}
35
+
36
+		// add the filters they've asked for
37
+		$filters = $this->getFilterSpec();
38
+		foreach ($filters as $filterName => $def) {
39
+			// NOTE: I'm pulling the guts out of this function so we can access Solr's full name
40
+			// for the field (SiteTree_Title for Title) and build the fieldMap in one step instead
41
+			// of two.
42
+			//$this->addFilterField($def['field'], $def['type'], $def['params']);
43
+			$singleFilter = $this->fieldData($def['field'], $def['type'], $def['params']);
44
+			$this->filterFields = array_merge($this->filterFields, $singleFilter);
45
+			foreach ($singleFilter as $solrName => $solrDef) {
46
+				if ($def['field'] == $solrDef['field']) {
47
+					$this->fieldMap[$filterName] = $solrName;
48
+				}
49
+			}
50
+		}
51 51
 
52 52
 //		Debug::dump($this->filterFields);
53 53
 
54
-        // Add spellcheck fields
54
+		// Add spellcheck fields
55 55
 //		$spellFields = $cfg->get('ShopSearch', 'spellcheck_dictionary_source');
56 56
 //		if (empty($spellFields) || !is_array($spellFields)) {
57 57
 //			$spellFields = array();
@@ -65,11 +65,11 @@  discard block
 block discarded – undo
65 65
 //			$this->addCopyField($f, '_spellcheckContent');
66 66
 //		}
67 67
 
68
-        // Technically, filter and sort fields are the same in Solr/Lucene
68
+		// Technically, filter and sort fields are the same in Solr/Lucene
69 69
 //		$this->addSortField('ViewCount');
70 70
 //		$this->addSortField('LastEdited', 'SSDatetime');
71 71
 
72
-        // Aggregate fields for spelling checks
72
+		// Aggregate fields for spelling checks
73 73
 //		$this->addCopyField('Title', 'spellcheckData');
74 74
 //		$this->addCopyField('Content', 'spellcheckData');
75 75
 
@@ -82,144 +82,144 @@  discard block
 block discarded – undo
82 82
 //			)
83 83
 //		));
84 84
 
85
-        // I can't get this to work. Need a way to create the Category field that get used
85
+		// I can't get this to work. Need a way to create the Category field that get used
86 86
 //		$this->addFilterField('Category', 'Int');
87 87
 //		$this->addFilterField('Parent.ID');
88 88
 //		$this->addFilterField('ProductCategories.ID');
89 89
 //		$this->addCopyField('SiteTree_Parent_ID', 'Category');
90 90
 //		$this->addCopyField('Product_ProductCategories_ID', 'Category');
91 91
 
92
-        // These will be added in a pull request to shop module. If they're not present they'll be ignored
92
+		// These will be added in a pull request to shop module. If they're not present they'll be ignored
93 93
 //		$this->addFilterField('AllCategoryIDs', 'Int', array('multiValued' => 'true'));
94 94
 //		$this->addFilterField('AllRecursiveCategoryIDs', 'Int', array('multiValued' => 'true'));
95 95
 
96
-        // This will cause only live pages to be indexed. There are two ways to do
97
-        // this. See fulltextsearch/docs/en/index.md for more information.
98
-        // Not sure if this is really the way to go or not, but for now this is it.
99
-        $this->excludeVariantState(array('SearchVariantVersioned' => 'Stage'));
100
-    }
101
-
102
-
103
-    /**
104
-     * Transforms different formats of field list into something we can pass to solr
105
-     * @param array $in
106
-     * @return array
107
-     */
108
-    protected function scrubFieldList($in)
109
-    {
110
-        $out = array();
111
-        if (empty($in) || !is_array($in)) {
112
-            return $out;
113
-        }
114
-
115
-        foreach ($in as $name => $val) {
116
-            // supports an indexed array format of simple field names
117
-            if (is_numeric($name)) {
118
-                $name = $val;
119
-                $val = true;
120
-            }
121
-
122
-            // supports a boolean value meaning "use the default setup"
123
-            $params = !is_array($val) ? array() : array_slice($val, 0);
124
-
125
-            // build a normalized structur
126
-            $def = array(
127
-                'field'     => isset($params['field']) ? $params['field'] : $name,
128
-                'type'      => isset($params['type']) ? $params['type'] : null,
129
-                'params'    => $params,
130
-            );
131
-
132
-            if (isset($def['params']['field'])) {
133
-                unset($def['params']['field']);
134
-            }
135
-            if (isset($def['params']['type'])) {
136
-                unset($def['params']['type']);
137
-            }
138
-
139
-            $out[$name] = $def;
140
-        }
141
-
142
-        return $out;
143
-    }
144
-
145
-
146
-    /**
147
-     * @return array
148
-     */
149
-    protected function getFulltextSpec()
150
-    {
151
-        $fields = Config::inst()->get('ShopSearch', 'solr_fulltext_fields');
152
-        if (empty($fields)) {
153
-            $fields = array('Title', 'Content');
154
-        }
155
-        return $this->scrubFieldList($fields);
156
-    }
157
-
158
-
159
-    /**
160
-     *
161
-     */
162
-    protected function getFilterSpec()
163
-    {
164
-        $fields = Config::inst()->get('ShopSearch', 'solr_filter_fields');
165
-        return $this->scrubFieldList($fields);
166
-    }
167
-
168
-
169
-    /**
170
-     * @return string
171
-     */
172
-    public function getFieldDefinitions()
173
-    {
174
-        $xml = parent::getFieldDefinitions();
96
+		// This will cause only live pages to be indexed. There are two ways to do
97
+		// this. See fulltextsearch/docs/en/index.md for more information.
98
+		// Not sure if this is really the way to go or not, but for now this is it.
99
+		$this->excludeVariantState(array('SearchVariantVersioned' => 'Stage'));
100
+	}
101
+
102
+
103
+	/**
104
+	 * Transforms different formats of field list into something we can pass to solr
105
+	 * @param array $in
106
+	 * @return array
107
+	 */
108
+	protected function scrubFieldList($in)
109
+	{
110
+		$out = array();
111
+		if (empty($in) || !is_array($in)) {
112
+			return $out;
113
+		}
114
+
115
+		foreach ($in as $name => $val) {
116
+			// supports an indexed array format of simple field names
117
+			if (is_numeric($name)) {
118
+				$name = $val;
119
+				$val = true;
120
+			}
121
+
122
+			// supports a boolean value meaning "use the default setup"
123
+			$params = !is_array($val) ? array() : array_slice($val, 0);
124
+
125
+			// build a normalized structur
126
+			$def = array(
127
+				'field'     => isset($params['field']) ? $params['field'] : $name,
128
+				'type'      => isset($params['type']) ? $params['type'] : null,
129
+				'params'    => $params,
130
+			);
131
+
132
+			if (isset($def['params']['field'])) {
133
+				unset($def['params']['field']);
134
+			}
135
+			if (isset($def['params']['type'])) {
136
+				unset($def['params']['type']);
137
+			}
138
+
139
+			$out[$name] = $def;
140
+		}
141
+
142
+		return $out;
143
+	}
144
+
145
+
146
+	/**
147
+	 * @return array
148
+	 */
149
+	protected function getFulltextSpec()
150
+	{
151
+		$fields = Config::inst()->get('ShopSearch', 'solr_fulltext_fields');
152
+		if (empty($fields)) {
153
+			$fields = array('Title', 'Content');
154
+		}
155
+		return $this->scrubFieldList($fields);
156
+	}
157
+
158
+
159
+	/**
160
+	 *
161
+	 */
162
+	protected function getFilterSpec()
163
+	{
164
+		$fields = Config::inst()->get('ShopSearch', 'solr_filter_fields');
165
+		return $this->scrubFieldList($fields);
166
+	}
167
+
168
+
169
+	/**
170
+	 * @return string
171
+	 */
172
+	public function getFieldDefinitions()
173
+	{
174
+		$xml = parent::getFieldDefinitions();
175 175
 //		$xml .= "\n\t\t<field name='_spellcheckContent' type='htmltext' indexed='true' stored='false' multiValued='true' />";
176 176
 
177
-        // create a sorting column
178
-        if (isset($this->fieldMap['Title']) || ShopSearch::config()->solr_title_sort_field) {
179
-            $f = empty(ShopSearch::config()->title_sort_field) ? '_titleSort' : ShopSearch::config()->solr_title_sort_field;
180
-            $xml .= "\n\t\t" . '<field name="' . $f . '" type="alphaOnlySort" indexed="true" stored="false" required="false" multiValued="false" />';
181
-            $xml .= "\n\t\t" . '<copyField source="SiteTree_Title" dest="' . $f . '"/>';
182
-        }
183
-
184
-        // create an autocomplete column
185
-        if (ShopSearch::config()->suggest_enabled) {
186
-            $xml .= "\n\t\t<field name='_autocomplete' type='autosuggest_text' indexed='true' stored='false' multiValued='true'/>";
187
-        }
188
-
189
-        return $xml;
190
-    }
191
-
192
-
193
-    /**
194
-     * @return string
195
-     */
196
-    public function getCopyFieldDefinitions()
197
-    {
198
-        $xml = parent::getCopyFieldDefinitions();
199
-
200
-        if (ShopSearch::config()->suggest_enabled) {
201
-            foreach ($this->fulltextFields as $name => $field) {
202
-                $xml .= "\n\t<copyField source='{$name}' dest='_autocomplete' />";
203
-                //$xml .= "\n\t<copyField source='{$name}' dest='_spellcheckContent' />";
204
-            }
205
-        }
206
-
207
-        return $xml;
208
-    }
209
-
210
-
211
-        /**
212
-     * Overrides the parent to add a field for autocomplete
213
-     * @return HTMLText
214
-     */
215
-    public function getTypes()
216
-    {
217
-        $val = parent::getTypes();
218
-        if (!$val || !is_object($val)) {
219
-            return $val;
220
-        }
221
-        $xml = $val->getValue();
222
-        $xml .= <<<XML
177
+		// create a sorting column
178
+		if (isset($this->fieldMap['Title']) || ShopSearch::config()->solr_title_sort_field) {
179
+			$f = empty(ShopSearch::config()->title_sort_field) ? '_titleSort' : ShopSearch::config()->solr_title_sort_field;
180
+			$xml .= "\n\t\t" . '<field name="' . $f . '" type="alphaOnlySort" indexed="true" stored="false" required="false" multiValued="false" />';
181
+			$xml .= "\n\t\t" . '<copyField source="SiteTree_Title" dest="' . $f . '"/>';
182
+		}
183
+
184
+		// create an autocomplete column
185
+		if (ShopSearch::config()->suggest_enabled) {
186
+			$xml .= "\n\t\t<field name='_autocomplete' type='autosuggest_text' indexed='true' stored='false' multiValued='true'/>";
187
+		}
188
+
189
+		return $xml;
190
+	}
191
+
192
+
193
+	/**
194
+	 * @return string
195
+	 */
196
+	public function getCopyFieldDefinitions()
197
+	{
198
+		$xml = parent::getCopyFieldDefinitions();
199
+
200
+		if (ShopSearch::config()->suggest_enabled) {
201
+			foreach ($this->fulltextFields as $name => $field) {
202
+				$xml .= "\n\t<copyField source='{$name}' dest='_autocomplete' />";
203
+				//$xml .= "\n\t<copyField source='{$name}' dest='_spellcheckContent' />";
204
+			}
205
+		}
206
+
207
+		return $xml;
208
+	}
209
+
210
+
211
+		/**
212
+		 * Overrides the parent to add a field for autocomplete
213
+		 * @return HTMLText
214
+		 */
215
+	public function getTypes()
216
+	{
217
+		$val = parent::getTypes();
218
+		if (!$val || !is_object($val)) {
219
+			return $val;
220
+		}
221
+		$xml = $val->getValue();
222
+		$xml .= <<<XML
223 223
 
224 224
 	        <fieldType name="autosuggest_text" class="solr.TextField"
225 225
 	                   positionIncrementGap="100">
@@ -238,89 +238,89 @@  discard block
 block discarded – undo
238 238
 	        </fieldType>
239 239
 
240 240
 XML;
241
-        $val->setValue($xml);
242
-        return $val;
243
-    }
244
-
245
-    /**
246
-     * This is an intermediary to bridge the search form input
247
-     * and the SearchQuery class. It allows us to have other
248
-     * drivers that may not use the FullTextSearch module.
249
-     *
250
-     * @param string $keywords
251
-     * @param array $filters [optional]
252
-     * @param array $facetSpec [optional]
253
-     * @param int $start [optional]
254
-     * @param int $limit [optional]
255
-     * @param string $sort [optional]
256
-     * @return ArrayData
257
-     */
258
-    public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='score desc')
259
-    {
260
-        $query = new SearchQuery();
261
-        $params = array(
262
-            'sort'  => $sort,
263
-        );
264
-
265
-        // swap out title search
266
-        if ($params['sort'] == 'SiteTree_Title') {
267
-            $params['sort'] = '_titleSort';
268
-        }
269
-
270
-        // search by keywords
271
-        $query->search(empty($keywords) ? '*:*' : $keywords);
272
-
273
-        // search by filter
274
-        foreach ($filters as $k => $v) {
275
-            if (isset($this->fieldMap[$k])) {
276
-                if (is_string($v) && preg_match('/^RANGE\~(.+)\~(.+)$/', $v, $m)) {
277
-                    // Is it a range value?
278
-                    $range = new SearchQuery_Range($m[1], $m[2]);
279
-                    $query->filter($this->fieldMap[$k], $range);
280
-                } else {
281
-                    // Or a normal scalar value
282
-                    $query->filter($this->fieldMap[$k], $v);
283
-                }
284
-            }
285
-        }
286
-
287
-        // add facets
288
-        $facetSpec = FacetHelper::inst()->expandFacetSpec($facetSpec);
289
-        $params += $this->buildFacetParams($facetSpec);
290
-
291
-        // TODO: add spellcheck
292
-
293
-        return $this->search($query, $start, $limit, $params, $facetSpec);
294
-    }
295
-
296
-
297
-    /**
298
-     * @param string $keywords
299
-     * @param array  $filters
300
-     * @return array
301
-     */
302
-    public function suggestWithResults($keywords, array $filters = array())
303
-    {
304
-        $limit      = (int)ShopSearch::config()->sayt_limit;
305
-
306
-        // process the keywords a bit
307
-        $terms      = preg_split('/\s+/', trim(strtolower($keywords)));
308
-        $lastTerm   = count($terms) > 0 ? array_pop($terms) : '';
309
-        $prefix     = count($terms) > 0 ? implode(' ', $terms) . ' ' : '';
310
-        //$terms[]    = $lastTerm;
311
-        $terms[]    = $lastTerm . '*'; // this allows for partial words to still match
312
-
313
-        // convert that to something solr adapater can handle
314
-        $query = new SearchQuery();
315
-        $query->search('+' . implode(' +', $terms));
316
-
317
-        $params = array(
318
-            'sort'          => 'score desc',
319
-            'facet'         => 'true',
320
-            'facet.field'   => '_autocomplete',
321
-            'facet.limit'   => ShopSearch::config()->suggest_limit,
322
-            'facet.prefix'  => $lastTerm,
323
-        );
241
+		$val->setValue($xml);
242
+		return $val;
243
+	}
244
+
245
+	/**
246
+	 * This is an intermediary to bridge the search form input
247
+	 * and the SearchQuery class. It allows us to have other
248
+	 * drivers that may not use the FullTextSearch module.
249
+	 *
250
+	 * @param string $keywords
251
+	 * @param array $filters [optional]
252
+	 * @param array $facetSpec [optional]
253
+	 * @param int $start [optional]
254
+	 * @param int $limit [optional]
255
+	 * @param string $sort [optional]
256
+	 * @return ArrayData
257
+	 */
258
+	public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='score desc')
259
+	{
260
+		$query = new SearchQuery();
261
+		$params = array(
262
+			'sort'  => $sort,
263
+		);
264
+
265
+		// swap out title search
266
+		if ($params['sort'] == 'SiteTree_Title') {
267
+			$params['sort'] = '_titleSort';
268
+		}
269
+
270
+		// search by keywords
271
+		$query->search(empty($keywords) ? '*:*' : $keywords);
272
+
273
+		// search by filter
274
+		foreach ($filters as $k => $v) {
275
+			if (isset($this->fieldMap[$k])) {
276
+				if (is_string($v) && preg_match('/^RANGE\~(.+)\~(.+)$/', $v, $m)) {
277
+					// Is it a range value?
278
+					$range = new SearchQuery_Range($m[1], $m[2]);
279
+					$query->filter($this->fieldMap[$k], $range);
280
+				} else {
281
+					// Or a normal scalar value
282
+					$query->filter($this->fieldMap[$k], $v);
283
+				}
284
+			}
285
+		}
286
+
287
+		// add facets
288
+		$facetSpec = FacetHelper::inst()->expandFacetSpec($facetSpec);
289
+		$params += $this->buildFacetParams($facetSpec);
290
+
291
+		// TODO: add spellcheck
292
+
293
+		return $this->search($query, $start, $limit, $params, $facetSpec);
294
+	}
295
+
296
+
297
+	/**
298
+	 * @param string $keywords
299
+	 * @param array  $filters
300
+	 * @return array
301
+	 */
302
+	public function suggestWithResults($keywords, array $filters = array())
303
+	{
304
+		$limit      = (int)ShopSearch::config()->sayt_limit;
305
+
306
+		// process the keywords a bit
307
+		$terms      = preg_split('/\s+/', trim(strtolower($keywords)));
308
+		$lastTerm   = count($terms) > 0 ? array_pop($terms) : '';
309
+		$prefix     = count($terms) > 0 ? implode(' ', $terms) . ' ' : '';
310
+		//$terms[]    = $lastTerm;
311
+		$terms[]    = $lastTerm . '*'; // this allows for partial words to still match
312
+
313
+		// convert that to something solr adapater can handle
314
+		$query = new SearchQuery();
315
+		$query->search('+' . implode(' +', $terms));
316
+
317
+		$params = array(
318
+			'sort'          => 'score desc',
319
+			'facet'         => 'true',
320
+			'facet.field'   => '_autocomplete',
321
+			'facet.limit'   => ShopSearch::config()->suggest_limit,
322
+			'facet.prefix'  => $lastTerm,
323
+		);
324 324
 
325 325
 //		$facetSpec = array(
326 326
 //			'_autocomplete' => array(
@@ -339,14 +339,14 @@  discard block
 block discarded – undo
339 339
 //		$suggestsion = array();
340 340
 ////		if ($)
341 341
 
342
-        $service = $this->getService();
342
+		$service = $this->getService();
343 343
 
344
-        SearchVariant::with(count($query->classes) == 1 ? $query->classes[0]['class'] : null)->call('alterQuery', $query, $this);
344
+		SearchVariant::with(count($query->classes) == 1 ? $query->classes[0]['class'] : null)->call('alterQuery', $query, $this);
345 345
 
346
-        $q = $terms;
347
-        $fq = array();
346
+		$q = $terms;
347
+		$fq = array();
348 348
 
349
-        // Build the search itself
349
+		// Build the search itself
350 350
 //		foreach ($query->search as $search) {
351 351
 //			$text = $search['text'];
352 352
 //			preg_match_all('/"[^"]*"|\S+/', $text, $parts);
@@ -370,408 +370,408 @@  discard block
 block discarded – undo
370 370
 //			}
371 371
 //		}
372 372
 
373
-        // Filter by class if requested
374
-        $classq = array();
375
-
376
-        foreach ($query->classes as $class) {
377
-            if (!empty($class['includeSubclasses'])) {
378
-                $classq[] = 'ClassHierarchy:'.$class['class'];
379
-            } else {
380
-                $classq[] = 'ClassName:'.$class['class'];
381
-            }
382
-        }
383
-
384
-        if ($classq) {
385
-            $fq[] = '+('.implode(' ', $classq).')';
386
-        }
387
-
388
-        // Filter by filters
389
-        foreach ($query->require as $field => $values) {
390
-            $requireq = array();
391
-
392
-            foreach ($values as $value) {
393
-                if ($value === SearchQuery::$missing) {
394
-                    $requireq[] = "(*:* -{$field}:[* TO *])";
395
-                } elseif ($value === SearchQuery::$present) {
396
-                    $requireq[] = "{$field}:[* TO *]";
397
-                } elseif ($value instanceof SearchQuery_Range) {
398
-                    $start = $value->start;
399
-                    if ($start === null) {
400
-                        $start = '*';
401
-                    }
402
-                    $end = $value->end;
403
-                    if ($end === null) {
404
-                        $end = '*';
405
-                    }
406
-                    $requireq[] = "$field:[$start TO $end]";
407
-                } else {
408
-                    $requireq[] = $field.':"'.$value.'"';
409
-                }
410
-            }
411
-
412
-            $fq[] = '+('.implode(' ', $requireq).')';
413
-        }
414
-
415
-        foreach ($query->exclude as $field => $values) {
416
-            $excludeq = array();
417
-            $missing = false;
418
-
419
-            foreach ($values as $value) {
420
-                if ($value === SearchQuery::$missing) {
421
-                    $missing = true;
422
-                } elseif ($value === SearchQuery::$present) {
423
-                    $excludeq[] = "{$field}:[* TO *]";
424
-                } elseif ($value instanceof SearchQuery_Range) {
425
-                    $start = $value->start;
426
-                    if ($start === null) {
427
-                        $start = '*';
428
-                    }
429
-                    $end = $value->end;
430
-                    if ($end === null) {
431
-                        $end = '*';
432
-                    }
433
-                    $excludeq[] = "$field:[$start TO $end]";
434
-                } else {
435
-                    $excludeq[] = $field.':"'.$value.'"';
436
-                }
437
-            }
438
-
439
-            $fq[] = ($missing ? "+{$field}:[* TO *] " : '') . '-('.implode(' ', $excludeq).')';
440
-        }
373
+		// Filter by class if requested
374
+		$classq = array();
375
+
376
+		foreach ($query->classes as $class) {
377
+			if (!empty($class['includeSubclasses'])) {
378
+				$classq[] = 'ClassHierarchy:'.$class['class'];
379
+			} else {
380
+				$classq[] = 'ClassName:'.$class['class'];
381
+			}
382
+		}
383
+
384
+		if ($classq) {
385
+			$fq[] = '+('.implode(' ', $classq).')';
386
+		}
387
+
388
+		// Filter by filters
389
+		foreach ($query->require as $field => $values) {
390
+			$requireq = array();
391
+
392
+			foreach ($values as $value) {
393
+				if ($value === SearchQuery::$missing) {
394
+					$requireq[] = "(*:* -{$field}:[* TO *])";
395
+				} elseif ($value === SearchQuery::$present) {
396
+					$requireq[] = "{$field}:[* TO *]";
397
+				} elseif ($value instanceof SearchQuery_Range) {
398
+					$start = $value->start;
399
+					if ($start === null) {
400
+						$start = '*';
401
+					}
402
+					$end = $value->end;
403
+					if ($end === null) {
404
+						$end = '*';
405
+					}
406
+					$requireq[] = "$field:[$start TO $end]";
407
+				} else {
408
+					$requireq[] = $field.':"'.$value.'"';
409
+				}
410
+			}
411
+
412
+			$fq[] = '+('.implode(' ', $requireq).')';
413
+		}
414
+
415
+		foreach ($query->exclude as $field => $values) {
416
+			$excludeq = array();
417
+			$missing = false;
418
+
419
+			foreach ($values as $value) {
420
+				if ($value === SearchQuery::$missing) {
421
+					$missing = true;
422
+				} elseif ($value === SearchQuery::$present) {
423
+					$excludeq[] = "{$field}:[* TO *]";
424
+				} elseif ($value instanceof SearchQuery_Range) {
425
+					$start = $value->start;
426
+					if ($start === null) {
427
+						$start = '*';
428
+					}
429
+					$end = $value->end;
430
+					if ($end === null) {
431
+						$end = '*';
432
+					}
433
+					$excludeq[] = "$field:[$start TO $end]";
434
+				} else {
435
+					$excludeq[] = $field.':"'.$value.'"';
436
+				}
437
+			}
438
+
439
+			$fq[] = ($missing ? "+{$field}:[* TO *] " : '') . '-('.implode(' ', $excludeq).')';
440
+		}
441 441
 
442 442
 //		if(!headers_sent()) {
443 443
 //			if ($q) header('X-Query: '.implode(' ', $q));
444 444
 //			if ($fq) header('X-Filters: "'.implode('", "', $fq).'"');
445 445
 //		}
446 446
 
447
-        $params = array_merge($params, array('fq' => implode(' ', $fq)));
448
-
449
-        $res = $service->search(
450
-            implode(' ', $q),
451
-            0,
452
-            $limit,
453
-            $params,
454
-            Apache_Solr_Service::METHOD_POST
455
-        );
456
-
457
-        $results = new ArrayList();
458
-        if ($res->getHttpStatus() >= 200 && $res->getHttpStatus() < 300) {
459
-            foreach ($res->response->docs as $doc) {
460
-                $result = DataObject::get_by_id($doc->ClassName, $doc->ID);
461
-                if ($result) {
462
-                    $results->push($result);
463
-                }
464
-            }
465
-            $numFound = $res->response->numFound;
466
-        } else {
467
-            $numFound = 0;
468
-        }
469
-
470
-        $ret = array();
471
-        $ret['products'] = new PaginatedList($results);
472
-        $ret['products']->setLimitItems(false);
473
-        $ret['products']->setTotalItems($numFound);
474
-        $ret['products']->setPageStart(0);
475
-        $ret['products']->setPageLength($limit);
476
-
477
-        // Facets (this is how we're doing suggestions for now...
478
-        $ret['suggestions'] = array();
479
-        if (isset($res->facet_counts->facet_fields->_autocomplete)) {
480
-            foreach ($res->facet_counts->facet_fields->_autocomplete as $term => $count) {
481
-                $ret['suggestions'][] = $prefix . $term;
482
-            }
483
-        }
484
-
485
-        // Suggestions (requires custom setup, assumes spellcheck.collate=true)
447
+		$params = array_merge($params, array('fq' => implode(' ', $fq)));
448
+
449
+		$res = $service->search(
450
+			implode(' ', $q),
451
+			0,
452
+			$limit,
453
+			$params,
454
+			Apache_Solr_Service::METHOD_POST
455
+		);
456
+
457
+		$results = new ArrayList();
458
+		if ($res->getHttpStatus() >= 200 && $res->getHttpStatus() < 300) {
459
+			foreach ($res->response->docs as $doc) {
460
+				$result = DataObject::get_by_id($doc->ClassName, $doc->ID);
461
+				if ($result) {
462
+					$results->push($result);
463
+				}
464
+			}
465
+			$numFound = $res->response->numFound;
466
+		} else {
467
+			$numFound = 0;
468
+		}
469
+
470
+		$ret = array();
471
+		$ret['products'] = new PaginatedList($results);
472
+		$ret['products']->setLimitItems(false);
473
+		$ret['products']->setTotalItems($numFound);
474
+		$ret['products']->setPageStart(0);
475
+		$ret['products']->setPageLength($limit);
476
+
477
+		// Facets (this is how we're doing suggestions for now...
478
+		$ret['suggestions'] = array();
479
+		if (isset($res->facet_counts->facet_fields->_autocomplete)) {
480
+			foreach ($res->facet_counts->facet_fields->_autocomplete as $term => $count) {
481
+				$ret['suggestions'][] = $prefix . $term;
482
+			}
483
+		}
484
+
485
+		// Suggestions (requires custom setup, assumes spellcheck.collate=true)
486 486
 //		if(isset($res->spellcheck->suggestions->collation)) {
487 487
 //			$ret['Suggestion'] = $res->spellcheck->suggestions->collation;
488 488
 //		}
489 489
 
490
-        return $ret;
491
-    }
492
-
493
-    /**
494
-     * @param $facets
495
-     * @return array
496
-     */
497
-    protected function buildFacetParams(array $facets)
498
-    {
499
-        $params = array();
500
-
501
-        if (!empty($facets)) {
502
-            $params['facet'] = 'true';
503
-
504
-            foreach ($facets as $name => $spec) {
505
-                // With our current implementation, "range" facets aren't true facets in solr terms.
506
-                // They're just a type of filter which can be handled elsewhere.
507
-                // For the other types we just ignore the rest of the spec and let Solr do its thing
508
-                if ($spec['Type'] != ShopSearch::FACET_TYPE_RANGE && isset($this->fieldMap[$name])) {
509
-                    $params['facet.field'] = $this->fieldMap[$name];
510
-                }
511
-            }
512
-        }
513
-
514
-        return $params;
515
-    }
516
-
517
-
518
-    /**
519
-     * Fulltextsearch module doesn't yet support facets very well, so I've just copied this function here so
520
-     * we have access to the results. I'd prefer to modify it minimally so we can eventually get rid of it
521
-     * once they add faceting or hooks to get directly at the returned response.
522
-     *
523
-     * @param SearchQuery $query
524
-     * @param integer $offset
525
-     * @param integer $limit
526
-     * @param  Array $params Extra request parameters passed through to Solr
527
-     * @param array $facetSpec - Added for ShopSearch so we can process the facets
528
-     * @return ArrayData Map with the following keys:
529
-     *  - 'Matches': ArrayList of the matched object instances
530
-     */
531
-    public function search(SearchQuery $query, $offset = -1, $limit = -1, $params = array(), $facetSpec = array())
532
-    {
533
-        $service = $this->getService();
534
-
535
-        SearchVariant::with(count($query->classes) == 1 ? $query->classes[0]['class'] : null)->call('alterQuery', $query, $this);
536
-
537
-        $q = array();
538
-        $fq = array();
539
-
540
-        // Build the search itself
541
-
542
-        foreach ($query->search as $search) {
543
-            $text = $search['text'];
544
-            preg_match_all('/"[^"]*"|\S+/', $text, $parts);
545
-
546
-            $fuzzy = $search['fuzzy'] ? '~' : '';
547
-
548
-            foreach ($parts[0] as $part) {
549
-                $fields = (isset($search['fields'])) ? $search['fields'] : array();
550
-                if (isset($search['boost'])) {
551
-                    $fields = array_merge($fields, array_keys($search['boost']));
552
-                }
553
-                if ($fields) {
554
-                    $searchq = array();
555
-                    foreach ($fields as $field) {
556
-                        $boost = (isset($search['boost'][$field])) ? '^' . $search['boost'][$field] : '';
557
-                        $searchq[] = "{$field}:".$part.$fuzzy.$boost;
558
-                    }
559
-                    $q[] = '+('.implode(' OR ', $searchq).')';
560
-                } else {
561
-                    $q[] = '+'.$part.$fuzzy;
562
-                }
563
-            }
564
-        }
565
-
566
-        // Filter by class if requested
567
-
568
-        $classq = array();
569
-
570
-        foreach ($query->classes as $class) {
571
-            if (!empty($class['includeSubclasses'])) {
572
-                $classq[] = 'ClassHierarchy:'.$class['class'];
573
-            } else {
574
-                $classq[] = 'ClassName:'.$class['class'];
575
-            }
576
-        }
577
-
578
-        if ($classq) {
579
-            $fq[] = '+('.implode(' ', $classq).')';
580
-        }
581
-
582
-        // Filter by filters
583
-
584
-        foreach ($query->require as $field => $values) {
585
-            $requireq = array();
586
-
587
-            foreach ($values as $value) {
588
-                if ($value === SearchQuery::$missing) {
589
-                    $requireq[] = "(*:* -{$field}:[* TO *])";
590
-                } elseif ($value === SearchQuery::$present) {
591
-                    $requireq[] = "{$field}:[* TO *]";
592
-                } elseif ($value instanceof SearchQuery_Range) {
593
-                    $start = $value->start;
594
-                    if ($start === null) {
595
-                        $start = '*';
596
-                    }
597
-                    $end = $value->end;
598
-                    if ($end === null) {
599
-                        $end = '*';
600
-                    }
601
-                    $requireq[] = "$field:[$start TO $end]";
602
-                } else {
603
-                    $requireq[] = $field.':"'.$value.'"';
604
-                }
605
-            }
606
-
607
-            $fq[] = '+('.implode(' ', $requireq).')';
608
-        }
609
-
610
-        foreach ($query->exclude as $field => $values) {
611
-            $excludeq = array();
612
-            $missing = false;
613
-
614
-            foreach ($values as $value) {
615
-                if ($value === SearchQuery::$missing) {
616
-                    $missing = true;
617
-                } elseif ($value === SearchQuery::$present) {
618
-                    $excludeq[] = "{$field}:[* TO *]";
619
-                } elseif ($value instanceof SearchQuery_Range) {
620
-                    $start = $value->start;
621
-                    if ($start === null) {
622
-                        $start = '*';
623
-                    }
624
-                    $end = $value->end;
625
-                    if ($end === null) {
626
-                        $end = '*';
627
-                    }
628
-                    $excludeq[] = "$field:[$start TO $end]";
629
-                } else {
630
-                    $excludeq[] = $field.':"'.$value.'"';
631
-                }
632
-            }
633
-
634
-            $fq[] = ($missing ? "+{$field}:[* TO *] " : '') . '-('.implode(' ', $excludeq).')';
635
-        }
490
+		return $ret;
491
+	}
492
+
493
+	/**
494
+	 * @param $facets
495
+	 * @return array
496
+	 */
497
+	protected function buildFacetParams(array $facets)
498
+	{
499
+		$params = array();
500
+
501
+		if (!empty($facets)) {
502
+			$params['facet'] = 'true';
503
+
504
+			foreach ($facets as $name => $spec) {
505
+				// With our current implementation, "range" facets aren't true facets in solr terms.
506
+				// They're just a type of filter which can be handled elsewhere.
507
+				// For the other types we just ignore the rest of the spec and let Solr do its thing
508
+				if ($spec['Type'] != ShopSearch::FACET_TYPE_RANGE && isset($this->fieldMap[$name])) {
509
+					$params['facet.field'] = $this->fieldMap[$name];
510
+				}
511
+			}
512
+		}
513
+
514
+		return $params;
515
+	}
516
+
517
+
518
+	/**
519
+	 * Fulltextsearch module doesn't yet support facets very well, so I've just copied this function here so
520
+	 * we have access to the results. I'd prefer to modify it minimally so we can eventually get rid of it
521
+	 * once they add faceting or hooks to get directly at the returned response.
522
+	 *
523
+	 * @param SearchQuery $query
524
+	 * @param integer $offset
525
+	 * @param integer $limit
526
+	 * @param  Array $params Extra request parameters passed through to Solr
527
+	 * @param array $facetSpec - Added for ShopSearch so we can process the facets
528
+	 * @return ArrayData Map with the following keys:
529
+	 *  - 'Matches': ArrayList of the matched object instances
530
+	 */
531
+	public function search(SearchQuery $query, $offset = -1, $limit = -1, $params = array(), $facetSpec = array())
532
+	{
533
+		$service = $this->getService();
534
+
535
+		SearchVariant::with(count($query->classes) == 1 ? $query->classes[0]['class'] : null)->call('alterQuery', $query, $this);
536
+
537
+		$q = array();
538
+		$fq = array();
539
+
540
+		// Build the search itself
541
+
542
+		foreach ($query->search as $search) {
543
+			$text = $search['text'];
544
+			preg_match_all('/"[^"]*"|\S+/', $text, $parts);
545
+
546
+			$fuzzy = $search['fuzzy'] ? '~' : '';
547
+
548
+			foreach ($parts[0] as $part) {
549
+				$fields = (isset($search['fields'])) ? $search['fields'] : array();
550
+				if (isset($search['boost'])) {
551
+					$fields = array_merge($fields, array_keys($search['boost']));
552
+				}
553
+				if ($fields) {
554
+					$searchq = array();
555
+					foreach ($fields as $field) {
556
+						$boost = (isset($search['boost'][$field])) ? '^' . $search['boost'][$field] : '';
557
+						$searchq[] = "{$field}:".$part.$fuzzy.$boost;
558
+					}
559
+					$q[] = '+('.implode(' OR ', $searchq).')';
560
+				} else {
561
+					$q[] = '+'.$part.$fuzzy;
562
+				}
563
+			}
564
+		}
565
+
566
+		// Filter by class if requested
567
+
568
+		$classq = array();
569
+
570
+		foreach ($query->classes as $class) {
571
+			if (!empty($class['includeSubclasses'])) {
572
+				$classq[] = 'ClassHierarchy:'.$class['class'];
573
+			} else {
574
+				$classq[] = 'ClassName:'.$class['class'];
575
+			}
576
+		}
577
+
578
+		if ($classq) {
579
+			$fq[] = '+('.implode(' ', $classq).')';
580
+		}
581
+
582
+		// Filter by filters
583
+
584
+		foreach ($query->require as $field => $values) {
585
+			$requireq = array();
586
+
587
+			foreach ($values as $value) {
588
+				if ($value === SearchQuery::$missing) {
589
+					$requireq[] = "(*:* -{$field}:[* TO *])";
590
+				} elseif ($value === SearchQuery::$present) {
591
+					$requireq[] = "{$field}:[* TO *]";
592
+				} elseif ($value instanceof SearchQuery_Range) {
593
+					$start = $value->start;
594
+					if ($start === null) {
595
+						$start = '*';
596
+					}
597
+					$end = $value->end;
598
+					if ($end === null) {
599
+						$end = '*';
600
+					}
601
+					$requireq[] = "$field:[$start TO $end]";
602
+				} else {
603
+					$requireq[] = $field.':"'.$value.'"';
604
+				}
605
+			}
606
+
607
+			$fq[] = '+('.implode(' ', $requireq).')';
608
+		}
609
+
610
+		foreach ($query->exclude as $field => $values) {
611
+			$excludeq = array();
612
+			$missing = false;
613
+
614
+			foreach ($values as $value) {
615
+				if ($value === SearchQuery::$missing) {
616
+					$missing = true;
617
+				} elseif ($value === SearchQuery::$present) {
618
+					$excludeq[] = "{$field}:[* TO *]";
619
+				} elseif ($value instanceof SearchQuery_Range) {
620
+					$start = $value->start;
621
+					if ($start === null) {
622
+						$start = '*';
623
+					}
624
+					$end = $value->end;
625
+					if ($end === null) {
626
+						$end = '*';
627
+					}
628
+					$excludeq[] = "$field:[$start TO $end]";
629
+				} else {
630
+					$excludeq[] = $field.':"'.$value.'"';
631
+				}
632
+			}
633
+
634
+			$fq[] = ($missing ? "+{$field}:[* TO *] " : '') . '-('.implode(' ', $excludeq).')';
635
+		}
636 636
 
637 637
 //		if(!headers_sent()) {
638 638
 //			if ($q) header('X-Query: '.implode(' ', $q));
639 639
 //			if ($fq) header('X-Filters: "'.implode('", "', $fq).'"');
640 640
 //		}
641 641
 
642
-        if ($offset == -1) {
643
-            $offset = $query->start;
644
-        }
645
-        if ($limit == -1) {
646
-            $limit = $query->limit;
647
-        }
648
-        if ($limit == -1) {
649
-            $limit = SearchQuery::$default_page_size;
650
-        }
651
-
652
-        $params = array_merge($params, array('fq' => implode(' ', $fq)));
653
-
654
-        $res = $service->search(
655
-            $q ? implode(' ', $q) : '*:*',
656
-            $offset,
657
-            $limit,
658
-            $params,
659
-            Apache_Solr_Service::METHOD_POST
660
-        );
661
-        //Debug::dump($res);
662
-
663
-        $results = new ArrayList();
664
-        if ($res->getHttpStatus() >= 200 && $res->getHttpStatus() < 300) {
665
-            foreach ($res->response->docs as $doc) {
666
-                $result = DataObject::get_by_id($doc->ClassName, $doc->ID);
667
-                if ($result) {
668
-                    $results->push($result);
669
-
670
-                    // Add highlighting (optional)
671
-                    $docId = $doc->_documentid;
672
-                    if ($res->highlighting && $res->highlighting->$docId) {
673
-                        // TODO Create decorator class for search results rather than adding arbitrary object properties
674
-                        // TODO Allow specifying highlighted field, and lazy loading
675
-                        // in case the search API needs another query (similar to SphinxSearchable->buildExcerpt()).
676
-                        $combinedHighlights = array();
677
-                        foreach ($res->highlighting->$docId as $field => $highlights) {
678
-                            $combinedHighlights = array_merge($combinedHighlights, $highlights);
679
-                        }
680
-
681
-                        // Remove entity-encoded U+FFFD replacement character. It signifies non-displayable characters,
682
-                        // and shows up as an encoding error in browsers.
683
-                        $result->Excerpt = DBField::create_field(
684
-                            'HTMLText',
685
-                            str_replace(
686
-                                '&#65533;',
687
-                                '',
688
-                                implode(' ... ', $combinedHighlights)
689
-                            )
690
-                        );
691
-                    }
692
-                }
693
-            }
694
-            $numFound = $res->response->numFound;
695
-        } else {
696
-            $numFound = 0;
697
-        }
698
-
699
-        $ret = array();
700
-        $ret['Matches'] = new PaginatedList($results);
701
-        $ret['Matches']->setLimitItems(false);
702
-        // Tell PaginatedList how many results there are
703
-        $ret['Matches']->setTotalItems($numFound);
704
-        // Results for current page start at $offset
705
-        $ret['Matches']->setPageStart($offset);
706
-        // Results per page
707
-        $ret['Matches']->setPageLength($limit);
708
-
709
-        // Facets
710
-        //Debug::dump($res);
711
-        if (isset($res->facet_counts->facet_fields)) {
712
-            $ret['Facets'] = $this->buildFacetResults($res->facet_counts->facet_fields, $facetSpec);
713
-        }
714
-
715
-        // Suggestions (requires custom setup, assumes spellcheck.collate=true)
716
-        if (isset($res->spellcheck->suggestions->collation)) {
717
-            $ret['Suggestion'] = $res->spellcheck->suggestions->collation;
718
-        }
719
-
720
-        return new ArrayData($ret);
721
-    }
722
-
723
-
724
-    /**
725
-     * @param stdClass $facetFields
726
-     * @param array $facetSpec
727
-     * @return ArrayList
728
-     */
729
-    protected function buildFacetResults($facetFields, array $facetSpec)
730
-    {
731
-        $out = new ArrayList;
732
-
733
-        foreach ($facetSpec as $field => $facet) {
734
-            if ($facet['Type'] == ShopSearch::FACET_TYPE_RANGE) {
735
-                // If it's a range facet, set up the min/max
736
-                // TODO: we could probably get the real min and max with solr's range faceting if we tried
737
-                if (isset($facet['RangeMin'])) {
738
-                    $facet['MinValue'] = $facet['RangeMin'];
739
-                }
740
-                if (isset($facet['RangeMax'])) {
741
-                    $facet['MaxValue'] = $facet['RangeMax'];
742
-                }
743
-                $out->push(new ArrayData($facet));
744
-            } elseif (isset($this->fieldMap[$field])) {
745
-                // Otherwise, look through Solr's results
746
-                $mySolrName = $this->fieldMap[$field];
747
-                foreach ($facetFields as $solrName => $values) {
748
-                    if ($solrName == $mySolrName) {
749
-                        // we found a match, look through the values we were given
750
-                        foreach ($values as $val => $count) {
751
-                            if (!isset($facet['Values'][$val])) {
752
-                                // for link type facets we want to add anything
753
-                                // for checkboxes, if it's not in the provided list we leave it out
754
-                                if ($facet['Type'] != ShopSearch::FACET_TYPE_CHECKBOX && $count > 0) {
755
-                                    $facet['Values'][$val] = new ArrayData(array(
756
-                                        'Label'     => $val,
757
-                                        'Value'     => $val,
758
-                                        'Count'     => $count,
759
-                                    ));
760
-                                }
761
-                            } elseif ($facet['Values'][$val]) {
762
-                                $facet['Values'][$val]->Count = $count;
763
-                            }
764
-                        }
765
-                    }
766
-                }
767
-
768
-                // then add that to the stack
769
-                $facet['Values'] = new ArrayList($facet['Values']);
770
-                $out->push(new ArrayData($facet));
771
-            }
772
-        }
773
-
774
-        //Debug::dump($out);
775
-        return $out;
776
-    }
642
+		if ($offset == -1) {
643
+			$offset = $query->start;
644
+		}
645
+		if ($limit == -1) {
646
+			$limit = $query->limit;
647
+		}
648
+		if ($limit == -1) {
649
+			$limit = SearchQuery::$default_page_size;
650
+		}
651
+
652
+		$params = array_merge($params, array('fq' => implode(' ', $fq)));
653
+
654
+		$res = $service->search(
655
+			$q ? implode(' ', $q) : '*:*',
656
+			$offset,
657
+			$limit,
658
+			$params,
659
+			Apache_Solr_Service::METHOD_POST
660
+		);
661
+		//Debug::dump($res);
662
+
663
+		$results = new ArrayList();
664
+		if ($res->getHttpStatus() >= 200 && $res->getHttpStatus() < 300) {
665
+			foreach ($res->response->docs as $doc) {
666
+				$result = DataObject::get_by_id($doc->ClassName, $doc->ID);
667
+				if ($result) {
668
+					$results->push($result);
669
+
670
+					// Add highlighting (optional)
671
+					$docId = $doc->_documentid;
672
+					if ($res->highlighting && $res->highlighting->$docId) {
673
+						// TODO Create decorator class for search results rather than adding arbitrary object properties
674
+						// TODO Allow specifying highlighted field, and lazy loading
675
+						// in case the search API needs another query (similar to SphinxSearchable->buildExcerpt()).
676
+						$combinedHighlights = array();
677
+						foreach ($res->highlighting->$docId as $field => $highlights) {
678
+							$combinedHighlights = array_merge($combinedHighlights, $highlights);
679
+						}
680
+
681
+						// Remove entity-encoded U+FFFD replacement character. It signifies non-displayable characters,
682
+						// and shows up as an encoding error in browsers.
683
+						$result->Excerpt = DBField::create_field(
684
+							'HTMLText',
685
+							str_replace(
686
+								'&#65533;',
687
+								'',
688
+								implode(' ... ', $combinedHighlights)
689
+							)
690
+						);
691
+					}
692
+				}
693
+			}
694
+			$numFound = $res->response->numFound;
695
+		} else {
696
+			$numFound = 0;
697
+		}
698
+
699
+		$ret = array();
700
+		$ret['Matches'] = new PaginatedList($results);
701
+		$ret['Matches']->setLimitItems(false);
702
+		// Tell PaginatedList how many results there are
703
+		$ret['Matches']->setTotalItems($numFound);
704
+		// Results for current page start at $offset
705
+		$ret['Matches']->setPageStart($offset);
706
+		// Results per page
707
+		$ret['Matches']->setPageLength($limit);
708
+
709
+		// Facets
710
+		//Debug::dump($res);
711
+		if (isset($res->facet_counts->facet_fields)) {
712
+			$ret['Facets'] = $this->buildFacetResults($res->facet_counts->facet_fields, $facetSpec);
713
+		}
714
+
715
+		// Suggestions (requires custom setup, assumes spellcheck.collate=true)
716
+		if (isset($res->spellcheck->suggestions->collation)) {
717
+			$ret['Suggestion'] = $res->spellcheck->suggestions->collation;
718
+		}
719
+
720
+		return new ArrayData($ret);
721
+	}
722
+
723
+
724
+	/**
725
+	 * @param stdClass $facetFields
726
+	 * @param array $facetSpec
727
+	 * @return ArrayList
728
+	 */
729
+	protected function buildFacetResults($facetFields, array $facetSpec)
730
+	{
731
+		$out = new ArrayList;
732
+
733
+		foreach ($facetSpec as $field => $facet) {
734
+			if ($facet['Type'] == ShopSearch::FACET_TYPE_RANGE) {
735
+				// If it's a range facet, set up the min/max
736
+				// TODO: we could probably get the real min and max with solr's range faceting if we tried
737
+				if (isset($facet['RangeMin'])) {
738
+					$facet['MinValue'] = $facet['RangeMin'];
739
+				}
740
+				if (isset($facet['RangeMax'])) {
741
+					$facet['MaxValue'] = $facet['RangeMax'];
742
+				}
743
+				$out->push(new ArrayData($facet));
744
+			} elseif (isset($this->fieldMap[$field])) {
745
+				// Otherwise, look through Solr's results
746
+				$mySolrName = $this->fieldMap[$field];
747
+				foreach ($facetFields as $solrName => $values) {
748
+					if ($solrName == $mySolrName) {
749
+						// we found a match, look through the values we were given
750
+						foreach ($values as $val => $count) {
751
+							if (!isset($facet['Values'][$val])) {
752
+								// for link type facets we want to add anything
753
+								// for checkboxes, if it's not in the provided list we leave it out
754
+								if ($facet['Type'] != ShopSearch::FACET_TYPE_CHECKBOX && $count > 0) {
755
+									$facet['Values'][$val] = new ArrayData(array(
756
+										'Label'     => $val,
757
+										'Value'     => $val,
758
+										'Count'     => $count,
759
+									));
760
+								}
761
+							} elseif ($facet['Values'][$val]) {
762
+								$facet['Values'][$val]->Count = $count;
763
+							}
764
+						}
765
+					}
766
+				}
767
+
768
+				// then add that to the stack
769
+				$facet['Values'] = new ArrayList($facet['Values']);
770
+				$out->push(new ArrayData($facet));
771
+			}
772
+		}
773
+
774
+		//Debug::dump($out);
775
+		return $out;
776
+	}
777 777
 }
Please login to merge, or discard this patch.