Completed
Push — master ( ec8fbc...686b54 )
by Mark
22:56
created
code/adapters/ShopSearchMysql.php 2 patches
Indentation   +72 added lines, -72 removed lines patch added patch discarded remove patch
@@ -1,89 +1,89 @@
 block discarded – undo
1 1
 <?php
2 2
 /**
3
- * Adapter that will use MySQL's full text search features.
4
- *
5
- * @author Mark Guinn <[email protected]>
6
- * @date 11.13.2013
7
- * @package shop_search
8
- * @subpackage adapters
9
- */
3
+	 * Adapter that will use MySQL's full text search features.
4
+	 *
5
+	 * @author Mark Guinn <[email protected]>
6
+	 * @date 11.13.2013
7
+	 * @package shop_search
8
+	 * @subpackage adapters
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.
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.
code/adapters/ShopSearchSimple.php 2 patches
Indentation   +89 added lines, -89 removed lines patch added patch discarded remove patch
@@ -1,104 +1,104 @@
 block discarded – undo
1 1
 <?php
2 2
 /**
3
- * VERY simple adapter to use DataList and :PartialMatch searches. Bare mininum
4
- * that will probably get terrible results but doesn't require any
5
- * additional setup.
6
- *
7
- * @author Mark Guinn <[email protected]>
8
- * @date 09.03.2013
9
- * @package shop_search
10
- */
3
+	 * VERY simple adapter to use DataList and :PartialMatch searches. Bare mininum
4
+	 * that will probably get terrible results but doesn't require any
5
+	 * additional setup.
6
+	 *
7
+	 * @author Mark Guinn <[email protected]>
8
+	 * @date 09.03.2013
9
+	 * @package shop_search
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   +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.
code/adapters/ShopSearchSolr.php 2 patches
Indentation   +648 added lines, -648 removed lines patch added patch discarded remove patch
@@ -1,53 +1,53 @@  discard block
 block discarded – undo
1 1
 <?php
2 2
 /**
3
- * Search driver for the fulltext module with solr backend.
4
- *
5
- * @author Mark Guinn <[email protected]>
6
- * @date 08.29.2013
7
- * @package shop_search
8
- */
3
+	 * Search driver for the fulltext module with solr backend.
4
+	 *
5
+	 * @author Mark Guinn <[email protected]>
6
+	 * @date 08.29.2013
7
+	 * @package shop_search
8
+	 */
9 9
 class ShopSearchSolr extends SolrIndex implements ShopSearchAdapter
10 10
 {
11
-    /** @var array - maps our names for fields to Solr's names (i.e. Title => SiteTree_Title) */
12
-    protected $fieldMap = array();
13
-
14
-    /**
15
-     * Sets up the index
16
-     */
17
-    public function init()
18
-    {
19
-        $searchables = ShopSearch::get_searchable_classes();
20
-
21
-        // Add each class to the index
22
-        foreach ($searchables as $class) {
23
-            $this->addClass($class);
24
-        }
25
-
26
-        // add the fields they've specifically asked for
27
-        $fields = $this->getFulltextSpec();
28
-        foreach ($fields as $def) {
29
-            $this->addFulltextField($def['field'], $def['type'], $def['params']);
30
-        }
31
-
32
-        // add the filters they've asked for
33
-        $filters = $this->getFilterSpec();
34
-        foreach ($filters as $filterName => $def) {
35
-            // NOTE: I'm pulling the guts out of this function so we can access Solr's full name
36
-            // for the field (SiteTree_Title for Title) and build the fieldMap in one step instead
37
-            // of two.
38
-            //$this->addFilterField($def['field'], $def['type'], $def['params']);
39
-            $singleFilter = $this->fieldData($def['field'], $def['type'], $def['params']);
40
-            $this->filterFields = array_merge($this->filterFields, $singleFilter);
41
-            foreach ($singleFilter as $solrName => $solrDef) {
42
-                if ($def['field'] == $solrDef['field']) {
43
-                    $this->fieldMap[$filterName] = $solrName;
44
-                }
45
-            }
46
-        }
11
+	/** @var array - maps our names for fields to Solr's names (i.e. Title => SiteTree_Title) */
12
+	protected $fieldMap = array();
13
+
14
+	/**
15
+	 * Sets up the index
16
+	 */
17
+	public function init()
18
+	{
19
+		$searchables = ShopSearch::get_searchable_classes();
20
+
21
+		// Add each class to the index
22
+		foreach ($searchables as $class) {
23
+			$this->addClass($class);
24
+		}
25
+
26
+		// add the fields they've specifically asked for
27
+		$fields = $this->getFulltextSpec();
28
+		foreach ($fields as $def) {
29
+			$this->addFulltextField($def['field'], $def['type'], $def['params']);
30
+		}
31
+
32
+		// add the filters they've asked for
33
+		$filters = $this->getFilterSpec();
34
+		foreach ($filters as $filterName => $def) {
35
+			// NOTE: I'm pulling the guts out of this function so we can access Solr's full name
36
+			// for the field (SiteTree_Title for Title) and build the fieldMap in one step instead
37
+			// of two.
38
+			//$this->addFilterField($def['field'], $def['type'], $def['params']);
39
+			$singleFilter = $this->fieldData($def['field'], $def['type'], $def['params']);
40
+			$this->filterFields = array_merge($this->filterFields, $singleFilter);
41
+			foreach ($singleFilter as $solrName => $solrDef) {
42
+				if ($def['field'] == $solrDef['field']) {
43
+					$this->fieldMap[$filterName] = $solrName;
44
+				}
45
+			}
46
+		}
47 47
 
48 48
 //		Debug::dump($this->filterFields);
49 49
 
50
-        // Add spellcheck fields
50
+		// Add spellcheck fields
51 51
 //		$spellFields = $cfg->get('ShopSearch', 'spellcheck_dictionary_source');
52 52
 //		if (empty($spellFields) || !is_array($spellFields)) {
53 53
 //			$spellFields = array();
@@ -61,11 +61,11 @@  discard block
 block discarded – undo
61 61
 //			$this->addCopyField($f, '_spellcheckContent');
62 62
 //		}
63 63
 
64
-        // Technically, filter and sort fields are the same in Solr/Lucene
64
+		// Technically, filter and sort fields are the same in Solr/Lucene
65 65
 //		$this->addSortField('ViewCount');
66 66
 //		$this->addSortField('LastEdited', 'SSDatetime');
67 67
 
68
-        // Aggregate fields for spelling checks
68
+		// Aggregate fields for spelling checks
69 69
 //		$this->addCopyField('Title', 'spellcheckData');
70 70
 //		$this->addCopyField('Content', 'spellcheckData');
71 71
 
@@ -78,144 +78,144 @@  discard block
 block discarded – undo
78 78
 //			)
79 79
 //		));
80 80
 
81
-        // I can't get this to work. Need a way to create the Category field that get used
81
+		// I can't get this to work. Need a way to create the Category field that get used
82 82
 //		$this->addFilterField('Category', 'Int');
83 83
 //		$this->addFilterField('Parent.ID');
84 84
 //		$this->addFilterField('ProductCategories.ID');
85 85
 //		$this->addCopyField('SiteTree_Parent_ID', 'Category');
86 86
 //		$this->addCopyField('Product_ProductCategories_ID', 'Category');
87 87
 
88
-        // These will be added in a pull request to shop module. If they're not present they'll be ignored
88
+		// These will be added in a pull request to shop module. If they're not present they'll be ignored
89 89
 //		$this->addFilterField('AllCategoryIDs', 'Int', array('multiValued' => 'true'));
90 90
 //		$this->addFilterField('AllRecursiveCategoryIDs', 'Int', array('multiValued' => 'true'));
91 91
 
92
-        // This will cause only live pages to be indexed. There are two ways to do
93
-        // this. See fulltextsearch/docs/en/index.md for more information.
94
-        // Not sure if this is really the way to go or not, but for now this is it.
95
-        $this->excludeVariantState(array('SearchVariantVersioned' => 'Stage'));
96
-    }
97
-
98
-
99
-    /**
100
-     * Transforms different formats of field list into something we can pass to solr
101
-     * @param array $in
102
-     * @return array
103
-     */
104
-    protected function scrubFieldList($in)
105
-    {
106
-        $out = array();
107
-        if (empty($in) || !is_array($in)) {
108
-            return $out;
109
-        }
110
-
111
-        foreach ($in as $name => $val) {
112
-            // supports an indexed array format of simple field names
113
-            if (is_numeric($name)) {
114
-                $name = $val;
115
-                $val = true;
116
-            }
117
-
118
-            // supports a boolean value meaning "use the default setup"
119
-            $params = !is_array($val) ? array() : array_slice($val, 0);
120
-
121
-            // build a normalized structur
122
-            $def = array(
123
-                'field'     => isset($params['field']) ? $params['field'] : $name,
124
-                'type'      => isset($params['type']) ? $params['type'] : null,
125
-                'params'    => $params,
126
-            );
127
-
128
-            if (isset($def['params']['field'])) {
129
-                unset($def['params']['field']);
130
-            }
131
-            if (isset($def['params']['type'])) {
132
-                unset($def['params']['type']);
133
-            }
134
-
135
-            $out[$name] = $def;
136
-        }
137
-
138
-        return $out;
139
-    }
140
-
141
-
142
-    /**
143
-     * @return array
144
-     */
145
-    protected function getFulltextSpec()
146
-    {
147
-        $fields = Config::inst()->get('ShopSearch', 'solr_fulltext_fields');
148
-        if (empty($fields)) {
149
-            $fields = array('Title', 'Content');
150
-        }
151
-        return $this->scrubFieldList($fields);
152
-    }
153
-
154
-
155
-    /**
156
-     *
157
-     */
158
-    protected function getFilterSpec()
159
-    {
160
-        $fields = Config::inst()->get('ShopSearch', 'solr_filter_fields');
161
-        return $this->scrubFieldList($fields);
162
-    }
163
-
164
-
165
-    /**
166
-     * @return string
167
-     */
168
-    public function getFieldDefinitions()
169
-    {
170
-        $xml = parent::getFieldDefinitions();
92
+		// This will cause only live pages to be indexed. There are two ways to do
93
+		// this. See fulltextsearch/docs/en/index.md for more information.
94
+		// Not sure if this is really the way to go or not, but for now this is it.
95
+		$this->excludeVariantState(array('SearchVariantVersioned' => 'Stage'));
96
+	}
97
+
98
+
99
+	/**
100
+	 * Transforms different formats of field list into something we can pass to solr
101
+	 * @param array $in
102
+	 * @return array
103
+	 */
104
+	protected function scrubFieldList($in)
105
+	{
106
+		$out = array();
107
+		if (empty($in) || !is_array($in)) {
108
+			return $out;
109
+		}
110
+
111
+		foreach ($in as $name => $val) {
112
+			// supports an indexed array format of simple field names
113
+			if (is_numeric($name)) {
114
+				$name = $val;
115
+				$val = true;
116
+			}
117
+
118
+			// supports a boolean value meaning "use the default setup"
119
+			$params = !is_array($val) ? array() : array_slice($val, 0);
120
+
121
+			// build a normalized structur
122
+			$def = array(
123
+				'field'     => isset($params['field']) ? $params['field'] : $name,
124
+				'type'      => isset($params['type']) ? $params['type'] : null,
125
+				'params'    => $params,
126
+			);
127
+
128
+			if (isset($def['params']['field'])) {
129
+				unset($def['params']['field']);
130
+			}
131
+			if (isset($def['params']['type'])) {
132
+				unset($def['params']['type']);
133
+			}
134
+
135
+			$out[$name] = $def;
136
+		}
137
+
138
+		return $out;
139
+	}
140
+
141
+
142
+	/**
143
+	 * @return array
144
+	 */
145
+	protected function getFulltextSpec()
146
+	{
147
+		$fields = Config::inst()->get('ShopSearch', 'solr_fulltext_fields');
148
+		if (empty($fields)) {
149
+			$fields = array('Title', 'Content');
150
+		}
151
+		return $this->scrubFieldList($fields);
152
+	}
153
+
154
+
155
+	/**
156
+	 *
157
+	 */
158
+	protected function getFilterSpec()
159
+	{
160
+		$fields = Config::inst()->get('ShopSearch', 'solr_filter_fields');
161
+		return $this->scrubFieldList($fields);
162
+	}
163
+
164
+
165
+	/**
166
+	 * @return string
167
+	 */
168
+	public function getFieldDefinitions()
169
+	{
170
+		$xml = parent::getFieldDefinitions();
171 171
 //		$xml .= "\n\t\t<field name='_spellcheckContent' type='htmltext' indexed='true' stored='false' multiValued='true' />";
172 172
 
173
-        // create a sorting column
174
-        if (isset($this->fieldMap['Title']) || ShopSearch::config()->solr_title_sort_field) {
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 . '"/>';
178
-        }
179
-
180
-        // create an autocomplete column
181
-        if (ShopSearch::config()->suggest_enabled) {
182
-            $xml .= "\n\t\t<field name='_autocomplete' type='autosuggest_text' indexed='true' stored='false' multiValued='true'/>";
183
-        }
184
-
185
-        return $xml;
186
-    }
187
-
188
-
189
-    /**
190
-     * @return string
191
-     */
192
-    public function getCopyFieldDefinitions()
193
-    {
194
-        $xml = parent::getCopyFieldDefinitions();
195
-
196
-        if (ShopSearch::config()->suggest_enabled) {
197
-            foreach ($this->fulltextFields as $name => $field) {
198
-                $xml .= "\n\t<copyField source='{$name}' dest='_autocomplete' />";
199
-                //$xml .= "\n\t<copyField source='{$name}' dest='_spellcheckContent' />";
200
-            }
201
-        }
202
-
203
-        return $xml;
204
-    }
205
-
206
-
207
-        /**
208
-     * Overrides the parent to add a field for autocomplete
209
-     * @return HTMLText
210
-     */
211
-    public function getTypes()
212
-    {
213
-        $val = parent::getTypes();
214
-        if (!$val || !is_object($val)) {
215
-            return $val;
216
-        }
217
-        $xml = $val->getValue();
218
-        $xml .= <<<XML
173
+		// create a sorting column
174
+		if (isset($this->fieldMap['Title']) || ShopSearch::config()->solr_title_sort_field) {
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 . '"/>';
178
+		}
179
+
180
+		// create an autocomplete column
181
+		if (ShopSearch::config()->suggest_enabled) {
182
+			$xml .= "\n\t\t<field name='_autocomplete' type='autosuggest_text' indexed='true' stored='false' multiValued='true'/>";
183
+		}
184
+
185
+		return $xml;
186
+	}
187
+
188
+
189
+	/**
190
+	 * @return string
191
+	 */
192
+	public function getCopyFieldDefinitions()
193
+	{
194
+		$xml = parent::getCopyFieldDefinitions();
195
+
196
+		if (ShopSearch::config()->suggest_enabled) {
197
+			foreach ($this->fulltextFields as $name => $field) {
198
+				$xml .= "\n\t<copyField source='{$name}' dest='_autocomplete' />";
199
+				//$xml .= "\n\t<copyField source='{$name}' dest='_spellcheckContent' />";
200
+			}
201
+		}
202
+
203
+		return $xml;
204
+	}
205
+
206
+
207
+		/**
208
+		 * Overrides the parent to add a field for autocomplete
209
+		 * @return HTMLText
210
+		 */
211
+	public function getTypes()
212
+	{
213
+		$val = parent::getTypes();
214
+		if (!$val || !is_object($val)) {
215
+			return $val;
216
+		}
217
+		$xml = $val->getValue();
218
+		$xml .= <<<XML
219 219
 
220 220
 	        <fieldType name="autosuggest_text" class="solr.TextField"
221 221
 	                   positionIncrementGap="100">
@@ -234,89 +234,89 @@  discard block
 block discarded – undo
234 234
 	        </fieldType>
235 235
 
236 236
 XML;
237
-        $val->setValue($xml);
238
-        return $val;
239
-    }
240
-
241
-    /**
242
-     * This is an intermediary to bridge the search form input
243
-     * and the SearchQuery class. It allows us to have other
244
-     * drivers that may not use the FullTextSearch module.
245
-     *
246
-     * @param string $keywords
247
-     * @param array $filters [optional]
248
-     * @param array $facetSpec [optional]
249
-     * @param int $start [optional]
250
-     * @param int $limit [optional]
251
-     * @param string $sort [optional]
252
-     * @return ArrayData
253
-     */
254
-    public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='score desc')
255
-    {
256
-        $query = new SearchQuery();
257
-        $params = array(
258
-            'sort'  => $sort,
259
-        );
260
-
261
-        // swap out title search
262
-        if ($params['sort'] == 'SiteTree_Title') {
263
-            $params['sort'] = '_titleSort';
264
-        }
265
-
266
-        // search by keywords
267
-        $query->search(empty($keywords) ? '*:*' : $keywords);
268
-
269
-        // search by filter
270
-        foreach ($filters as $k => $v) {
271
-            if (isset($this->fieldMap[$k])) {
272
-                if (is_string($v) && preg_match('/^RANGE\~(.+)\~(.+)$/', $v, $m)) {
273
-                    // Is it a range value?
274
-                    $range = new SearchQuery_Range($m[1], $m[2]);
275
-                    $query->filter($this->fieldMap[$k], $range);
276
-                } else {
277
-                    // Or a normal scalar value
278
-                    $query->filter($this->fieldMap[$k], $v);
279
-                }
280
-            }
281
-        }
282
-
283
-        // add facets
284
-        $facetSpec = FacetHelper::inst()->expandFacetSpec($facetSpec);
285
-        $params += $this->buildFacetParams($facetSpec);
286
-
287
-        // TODO: add spellcheck
288
-
289
-        return $this->search($query, $start, $limit, $params, $facetSpec);
290
-    }
291
-
292
-
293
-    /**
294
-     * @param string $keywords
295
-     * @param array  $filters
296
-     * @return array
297
-     */
298
-    public function suggestWithResults($keywords, array $filters = array())
299
-    {
300
-        $limit      = (int)ShopSearch::config()->sayt_limit;
301
-
302
-        // process the keywords a bit
303
-        $terms      = preg_split('/\s+/', trim(strtolower($keywords)));
304
-        $lastTerm   = count($terms) > 0 ? array_pop($terms) : '';
305
-        $prefix     = count($terms) > 0 ? implode(' ', $terms) . ' ' : '';
306
-        //$terms[]    = $lastTerm;
307
-        $terms[]    = $lastTerm . '*'; // this allows for partial words to still match
308
-
309
-        // convert that to something solr adapater can handle
310
-        $query = new SearchQuery();
311
-        $query->search('+' . implode(' +', $terms));
312
-
313
-        $params = array(
314
-            'sort'          => 'score desc',
315
-            'facet'         => 'true',
316
-            'facet.field'   => '_autocomplete',
317
-            'facet.limit'   => ShopSearch::config()->suggest_limit,
318
-            'facet.prefix'  => $lastTerm,
319
-        );
237
+		$val->setValue($xml);
238
+		return $val;
239
+	}
240
+
241
+	/**
242
+	 * This is an intermediary to bridge the search form input
243
+	 * and the SearchQuery class. It allows us to have other
244
+	 * drivers that may not use the FullTextSearch module.
245
+	 *
246
+	 * @param string $keywords
247
+	 * @param array $filters [optional]
248
+	 * @param array $facetSpec [optional]
249
+	 * @param int $start [optional]
250
+	 * @param int $limit [optional]
251
+	 * @param string $sort [optional]
252
+	 * @return ArrayData
253
+	 */
254
+	public function searchFromVars($keywords, array $filters=array(), array $facetSpec=array(), $start=-1, $limit=-1, $sort='score desc')
255
+	{
256
+		$query = new SearchQuery();
257
+		$params = array(
258
+			'sort'  => $sort,
259
+		);
260
+
261
+		// swap out title search
262
+		if ($params['sort'] == 'SiteTree_Title') {
263
+			$params['sort'] = '_titleSort';
264
+		}
265
+
266
+		// search by keywords
267
+		$query->search(empty($keywords) ? '*:*' : $keywords);
268
+
269
+		// search by filter
270
+		foreach ($filters as $k => $v) {
271
+			if (isset($this->fieldMap[$k])) {
272
+				if (is_string($v) && preg_match('/^RANGE\~(.+)\~(.+)$/', $v, $m)) {
273
+					// Is it a range value?
274
+					$range = new SearchQuery_Range($m[1], $m[2]);
275
+					$query->filter($this->fieldMap[$k], $range);
276
+				} else {
277
+					// Or a normal scalar value
278
+					$query->filter($this->fieldMap[$k], $v);
279
+				}
280
+			}
281
+		}
282
+
283
+		// add facets
284
+		$facetSpec = FacetHelper::inst()->expandFacetSpec($facetSpec);
285
+		$params += $this->buildFacetParams($facetSpec);
286
+
287
+		// TODO: add spellcheck
288
+
289
+		return $this->search($query, $start, $limit, $params, $facetSpec);
290
+	}
291
+
292
+
293
+	/**
294
+	 * @param string $keywords
295
+	 * @param array  $filters
296
+	 * @return array
297
+	 */
298
+	public function suggestWithResults($keywords, array $filters = array())
299
+	{
300
+		$limit      = (int)ShopSearch::config()->sayt_limit;
301
+
302
+		// process the keywords a bit
303
+		$terms      = preg_split('/\s+/', trim(strtolower($keywords)));
304
+		$lastTerm   = count($terms) > 0 ? array_pop($terms) : '';
305
+		$prefix     = count($terms) > 0 ? implode(' ', $terms) . ' ' : '';
306
+		//$terms[]    = $lastTerm;
307
+		$terms[]    = $lastTerm . '*'; // this allows for partial words to still match
308
+
309
+		// convert that to something solr adapater can handle
310
+		$query = new SearchQuery();
311
+		$query->search('+' . implode(' +', $terms));
312
+
313
+		$params = array(
314
+			'sort'          => 'score desc',
315
+			'facet'         => 'true',
316
+			'facet.field'   => '_autocomplete',
317
+			'facet.limit'   => ShopSearch::config()->suggest_limit,
318
+			'facet.prefix'  => $lastTerm,
319
+		);
320 320
 
321 321
 //		$facetSpec = array(
322 322
 //			'_autocomplete' => array(
@@ -335,14 +335,14 @@  discard block
 block discarded – undo
335 335
 //		$suggestsion = array();
336 336
 ////		if ($)
337 337
 
338
-        $service = $this->getService();
338
+		$service = $this->getService();
339 339
 
340
-        SearchVariant::with(count($query->classes) == 1 ? $query->classes[0]['class'] : null)->call('alterQuery', $query, $this);
340
+		SearchVariant::with(count($query->classes) == 1 ? $query->classes[0]['class'] : null)->call('alterQuery', $query, $this);
341 341
 
342
-        $q = $terms;
343
-        $fq = array();
342
+		$q = $terms;
343
+		$fq = array();
344 344
 
345
-        // Build the search itself
345
+		// Build the search itself
346 346
 //		foreach ($query->search as $search) {
347 347
 //			$text = $search['text'];
348 348
 //			preg_match_all('/"[^"]*"|\S+/', $text, $parts);
@@ -366,408 +366,408 @@  discard block
 block discarded – undo
366 366
 //			}
367 367
 //		}
368 368
 
369
-        // Filter by class if requested
370
-        $classq = array();
371
-
372
-        foreach ($query->classes as $class) {
373
-            if (!empty($class['includeSubclasses'])) {
374
-                $classq[] = 'ClassHierarchy:'.$class['class'];
375
-            } else {
376
-                $classq[] = 'ClassName:'.$class['class'];
377
-            }
378
-        }
379
-
380
-        if ($classq) {
381
-            $fq[] = '+('.implode(' ', $classq).')';
382
-        }
383
-
384
-        // Filter by filters
385
-        foreach ($query->require as $field => $values) {
386
-            $requireq = array();
387
-
388
-            foreach ($values as $value) {
389
-                if ($value === SearchQuery::$missing) {
390
-                    $requireq[] = "(*:* -{$field}:[* TO *])";
391
-                } elseif ($value === SearchQuery::$present) {
392
-                    $requireq[] = "{$field}:[* TO *]";
393
-                } elseif ($value instanceof SearchQuery_Range) {
394
-                    $start = $value->start;
395
-                    if ($start === null) {
396
-                        $start = '*';
397
-                    }
398
-                    $end = $value->end;
399
-                    if ($end === null) {
400
-                        $end = '*';
401
-                    }
402
-                    $requireq[] = "$field:[$start TO $end]";
403
-                } else {
404
-                    $requireq[] = $field.':"'.$value.'"';
405
-                }
406
-            }
407
-
408
-            $fq[] = '+('.implode(' ', $requireq).')';
409
-        }
410
-
411
-        foreach ($query->exclude as $field => $values) {
412
-            $excludeq = array();
413
-            $missing = false;
414
-
415
-            foreach ($values as $value) {
416
-                if ($value === SearchQuery::$missing) {
417
-                    $missing = true;
418
-                } elseif ($value === SearchQuery::$present) {
419
-                    $excludeq[] = "{$field}:[* TO *]";
420
-                } elseif ($value instanceof SearchQuery_Range) {
421
-                    $start = $value->start;
422
-                    if ($start === null) {
423
-                        $start = '*';
424
-                    }
425
-                    $end = $value->end;
426
-                    if ($end === null) {
427
-                        $end = '*';
428
-                    }
429
-                    $excludeq[] = "$field:[$start TO $end]";
430
-                } else {
431
-                    $excludeq[] = $field.':"'.$value.'"';
432
-                }
433
-            }
434
-
435
-            $fq[] = ($missing ? "+{$field}:[* TO *] " : '') . '-('.implode(' ', $excludeq).')';
436
-        }
369
+		// Filter by class if requested
370
+		$classq = array();
371
+
372
+		foreach ($query->classes as $class) {
373
+			if (!empty($class['includeSubclasses'])) {
374
+				$classq[] = 'ClassHierarchy:'.$class['class'];
375
+			} else {
376
+				$classq[] = 'ClassName:'.$class['class'];
377
+			}
378
+		}
379
+
380
+		if ($classq) {
381
+			$fq[] = '+('.implode(' ', $classq).')';
382
+		}
383
+
384
+		// Filter by filters
385
+		foreach ($query->require as $field => $values) {
386
+			$requireq = array();
387
+
388
+			foreach ($values as $value) {
389
+				if ($value === SearchQuery::$missing) {
390
+					$requireq[] = "(*:* -{$field}:[* TO *])";
391
+				} elseif ($value === SearchQuery::$present) {
392
+					$requireq[] = "{$field}:[* TO *]";
393
+				} elseif ($value instanceof SearchQuery_Range) {
394
+					$start = $value->start;
395
+					if ($start === null) {
396
+						$start = '*';
397
+					}
398
+					$end = $value->end;
399
+					if ($end === null) {
400
+						$end = '*';
401
+					}
402
+					$requireq[] = "$field:[$start TO $end]";
403
+				} else {
404
+					$requireq[] = $field.':"'.$value.'"';
405
+				}
406
+			}
407
+
408
+			$fq[] = '+('.implode(' ', $requireq).')';
409
+		}
410
+
411
+		foreach ($query->exclude as $field => $values) {
412
+			$excludeq = array();
413
+			$missing = false;
414
+
415
+			foreach ($values as $value) {
416
+				if ($value === SearchQuery::$missing) {
417
+					$missing = true;
418
+				} elseif ($value === SearchQuery::$present) {
419
+					$excludeq[] = "{$field}:[* TO *]";
420
+				} elseif ($value instanceof SearchQuery_Range) {
421
+					$start = $value->start;
422
+					if ($start === null) {
423
+						$start = '*';
424
+					}
425
+					$end = $value->end;
426
+					if ($end === null) {
427
+						$end = '*';
428
+					}
429
+					$excludeq[] = "$field:[$start TO $end]";
430
+				} else {
431
+					$excludeq[] = $field.':"'.$value.'"';
432
+				}
433
+			}
434
+
435
+			$fq[] = ($missing ? "+{$field}:[* TO *] " : '') . '-('.implode(' ', $excludeq).')';
436
+		}
437 437
 
438 438
 //		if(!headers_sent()) {
439 439
 //			if ($q) header('X-Query: '.implode(' ', $q));
440 440
 //			if ($fq) header('X-Filters: "'.implode('", "', $fq).'"');
441 441
 //		}
442 442
 
443
-        $params = array_merge($params, array('fq' => implode(' ', $fq)));
444
-
445
-        $res = $service->search(
446
-            implode(' ', $q),
447
-            0,
448
-            $limit,
449
-            $params,
450
-            Apache_Solr_Service::METHOD_POST
451
-        );
452
-
453
-        $results = new ArrayList();
454
-        if ($res->getHttpStatus() >= 200 && $res->getHttpStatus() < 300) {
455
-            foreach ($res->response->docs as $doc) {
456
-                $result = DataObject::get_by_id($doc->ClassName, $doc->ID);
457
-                if ($result) {
458
-                    $results->push($result);
459
-                }
460
-            }
461
-            $numFound = $res->response->numFound;
462
-        } else {
463
-            $numFound = 0;
464
-        }
465
-
466
-        $ret = array();
467
-        $ret['products'] = new PaginatedList($results);
468
-        $ret['products']->setLimitItems(false);
469
-        $ret['products']->setTotalItems($numFound);
470
-        $ret['products']->setPageStart(0);
471
-        $ret['products']->setPageLength($limit);
472
-
473
-        // Facets (this is how we're doing suggestions for now...
474
-        $ret['suggestions'] = array();
475
-        if (isset($res->facet_counts->facet_fields->_autocomplete)) {
476
-            foreach ($res->facet_counts->facet_fields->_autocomplete as $term => $count) {
477
-                $ret['suggestions'][] = $prefix . $term;
478
-            }
479
-        }
480
-
481
-        // Suggestions (requires custom setup, assumes spellcheck.collate=true)
443
+		$params = array_merge($params, array('fq' => implode(' ', $fq)));
444
+
445
+		$res = $service->search(
446
+			implode(' ', $q),
447
+			0,
448
+			$limit,
449
+			$params,
450
+			Apache_Solr_Service::METHOD_POST
451
+		);
452
+
453
+		$results = new ArrayList();
454
+		if ($res->getHttpStatus() >= 200 && $res->getHttpStatus() < 300) {
455
+			foreach ($res->response->docs as $doc) {
456
+				$result = DataObject::get_by_id($doc->ClassName, $doc->ID);
457
+				if ($result) {
458
+					$results->push($result);
459
+				}
460
+			}
461
+			$numFound = $res->response->numFound;
462
+		} else {
463
+			$numFound = 0;
464
+		}
465
+
466
+		$ret = array();
467
+		$ret['products'] = new PaginatedList($results);
468
+		$ret['products']->setLimitItems(false);
469
+		$ret['products']->setTotalItems($numFound);
470
+		$ret['products']->setPageStart(0);
471
+		$ret['products']->setPageLength($limit);
472
+
473
+		// Facets (this is how we're doing suggestions for now...
474
+		$ret['suggestions'] = array();
475
+		if (isset($res->facet_counts->facet_fields->_autocomplete)) {
476
+			foreach ($res->facet_counts->facet_fields->_autocomplete as $term => $count) {
477
+				$ret['suggestions'][] = $prefix . $term;
478
+			}
479
+		}
480
+
481
+		// Suggestions (requires custom setup, assumes spellcheck.collate=true)
482 482
 //		if(isset($res->spellcheck->suggestions->collation)) {
483 483
 //			$ret['Suggestion'] = $res->spellcheck->suggestions->collation;
484 484
 //		}
485 485
 
486
-        return $ret;
487
-    }
488
-
489
-    /**
490
-     * @param $facets
491
-     * @return array
492
-     */
493
-    protected function buildFacetParams(array $facets)
494
-    {
495
-        $params = array();
496
-
497
-        if (!empty($facets)) {
498
-            $params['facet'] = 'true';
499
-
500
-            foreach ($facets as $name => $spec) {
501
-                // With our current implementation, "range" facets aren't true facets in solr terms.
502
-                // They're just a type of filter which can be handled elsewhere.
503
-                // For the other types we just ignore the rest of the spec and let Solr do its thing
504
-                if ($spec['Type'] != ShopSearch::FACET_TYPE_RANGE && isset($this->fieldMap[$name])) {
505
-                    $params['facet.field'] = $this->fieldMap[$name];
506
-                }
507
-            }
508
-        }
509
-
510
-        return $params;
511
-    }
512
-
513
-
514
-    /**
515
-     * Fulltextsearch module doesn't yet support facets very well, so I've just copied this function here so
516
-     * we have access to the results. I'd prefer to modify it minimally so we can eventually get rid of it
517
-     * once they add faceting or hooks to get directly at the returned response.
518
-     *
519
-     * @param SearchQuery $query
520
-     * @param integer $offset
521
-     * @param integer $limit
522
-     * @param  Array $params Extra request parameters passed through to Solr
523
-     * @param array $facetSpec - Added for ShopSearch so we can process the facets
524
-     * @return ArrayData Map with the following keys:
525
-     *  - 'Matches': ArrayList of the matched object instances
526
-     */
527
-    public function search(SearchQuery $query, $offset = -1, $limit = -1, $params = array(), $facetSpec = array())
528
-    {
529
-        $service = $this->getService();
530
-
531
-        SearchVariant::with(count($query->classes) == 1 ? $query->classes[0]['class'] : null)->call('alterQuery', $query, $this);
532
-
533
-        $q = array();
534
-        $fq = array();
535
-
536
-        // Build the search itself
537
-
538
-        foreach ($query->search as $search) {
539
-            $text = $search['text'];
540
-            preg_match_all('/"[^"]*"|\S+/', $text, $parts);
541
-
542
-            $fuzzy = $search['fuzzy'] ? '~' : '';
543
-
544
-            foreach ($parts[0] as $part) {
545
-                $fields = (isset($search['fields'])) ? $search['fields'] : array();
546
-                if (isset($search['boost'])) {
547
-                    $fields = array_merge($fields, array_keys($search['boost']));
548
-                }
549
-                if ($fields) {
550
-                    $searchq = array();
551
-                    foreach ($fields as $field) {
552
-                        $boost = (isset($search['boost'][$field])) ? '^' . $search['boost'][$field] : '';
553
-                        $searchq[] = "{$field}:".$part.$fuzzy.$boost;
554
-                    }
555
-                    $q[] = '+('.implode(' OR ', $searchq).')';
556
-                } else {
557
-                    $q[] = '+'.$part.$fuzzy;
558
-                }
559
-            }
560
-        }
561
-
562
-        // Filter by class if requested
563
-
564
-        $classq = array();
565
-
566
-        foreach ($query->classes as $class) {
567
-            if (!empty($class['includeSubclasses'])) {
568
-                $classq[] = 'ClassHierarchy:'.$class['class'];
569
-            } else {
570
-                $classq[] = 'ClassName:'.$class['class'];
571
-            }
572
-        }
573
-
574
-        if ($classq) {
575
-            $fq[] = '+('.implode(' ', $classq).')';
576
-        }
577
-
578
-        // Filter by filters
579
-
580
-        foreach ($query->require as $field => $values) {
581
-            $requireq = array();
582
-
583
-            foreach ($values as $value) {
584
-                if ($value === SearchQuery::$missing) {
585
-                    $requireq[] = "(*:* -{$field}:[* TO *])";
586
-                } elseif ($value === SearchQuery::$present) {
587
-                    $requireq[] = "{$field}:[* TO *]";
588
-                } elseif ($value instanceof SearchQuery_Range) {
589
-                    $start = $value->start;
590
-                    if ($start === null) {
591
-                        $start = '*';
592
-                    }
593
-                    $end = $value->end;
594
-                    if ($end === null) {
595
-                        $end = '*';
596
-                    }
597
-                    $requireq[] = "$field:[$start TO $end]";
598
-                } else {
599
-                    $requireq[] = $field.':"'.$value.'"';
600
-                }
601
-            }
602
-
603
-            $fq[] = '+('.implode(' ', $requireq).')';
604
-        }
605
-
606
-        foreach ($query->exclude as $field => $values) {
607
-            $excludeq = array();
608
-            $missing = false;
609
-
610
-            foreach ($values as $value) {
611
-                if ($value === SearchQuery::$missing) {
612
-                    $missing = true;
613
-                } elseif ($value === SearchQuery::$present) {
614
-                    $excludeq[] = "{$field}:[* TO *]";
615
-                } elseif ($value instanceof SearchQuery_Range) {
616
-                    $start = $value->start;
617
-                    if ($start === null) {
618
-                        $start = '*';
619
-                    }
620
-                    $end = $value->end;
621
-                    if ($end === null) {
622
-                        $end = '*';
623
-                    }
624
-                    $excludeq[] = "$field:[$start TO $end]";
625
-                } else {
626
-                    $excludeq[] = $field.':"'.$value.'"';
627
-                }
628
-            }
629
-
630
-            $fq[] = ($missing ? "+{$field}:[* TO *] " : '') . '-('.implode(' ', $excludeq).')';
631
-        }
486
+		return $ret;
487
+	}
488
+
489
+	/**
490
+	 * @param $facets
491
+	 * @return array
492
+	 */
493
+	protected function buildFacetParams(array $facets)
494
+	{
495
+		$params = array();
496
+
497
+		if (!empty($facets)) {
498
+			$params['facet'] = 'true';
499
+
500
+			foreach ($facets as $name => $spec) {
501
+				// With our current implementation, "range" facets aren't true facets in solr terms.
502
+				// They're just a type of filter which can be handled elsewhere.
503
+				// For the other types we just ignore the rest of the spec and let Solr do its thing
504
+				if ($spec['Type'] != ShopSearch::FACET_TYPE_RANGE && isset($this->fieldMap[$name])) {
505
+					$params['facet.field'] = $this->fieldMap[$name];
506
+				}
507
+			}
508
+		}
509
+
510
+		return $params;
511
+	}
512
+
513
+
514
+	/**
515
+	 * Fulltextsearch module doesn't yet support facets very well, so I've just copied this function here so
516
+	 * we have access to the results. I'd prefer to modify it minimally so we can eventually get rid of it
517
+	 * once they add faceting or hooks to get directly at the returned response.
518
+	 *
519
+	 * @param SearchQuery $query
520
+	 * @param integer $offset
521
+	 * @param integer $limit
522
+	 * @param  Array $params Extra request parameters passed through to Solr
523
+	 * @param array $facetSpec - Added for ShopSearch so we can process the facets
524
+	 * @return ArrayData Map with the following keys:
525
+	 *  - 'Matches': ArrayList of the matched object instances
526
+	 */
527
+	public function search(SearchQuery $query, $offset = -1, $limit = -1, $params = array(), $facetSpec = array())
528
+	{
529
+		$service = $this->getService();
530
+
531
+		SearchVariant::with(count($query->classes) == 1 ? $query->classes[0]['class'] : null)->call('alterQuery', $query, $this);
532
+
533
+		$q = array();
534
+		$fq = array();
535
+
536
+		// Build the search itself
537
+
538
+		foreach ($query->search as $search) {
539
+			$text = $search['text'];
540
+			preg_match_all('/"[^"]*"|\S+/', $text, $parts);
541
+
542
+			$fuzzy = $search['fuzzy'] ? '~' : '';
543
+
544
+			foreach ($parts[0] as $part) {
545
+				$fields = (isset($search['fields'])) ? $search['fields'] : array();
546
+				if (isset($search['boost'])) {
547
+					$fields = array_merge($fields, array_keys($search['boost']));
548
+				}
549
+				if ($fields) {
550
+					$searchq = array();
551
+					foreach ($fields as $field) {
552
+						$boost = (isset($search['boost'][$field])) ? '^' . $search['boost'][$field] : '';
553
+						$searchq[] = "{$field}:".$part.$fuzzy.$boost;
554
+					}
555
+					$q[] = '+('.implode(' OR ', $searchq).')';
556
+				} else {
557
+					$q[] = '+'.$part.$fuzzy;
558
+				}
559
+			}
560
+		}
561
+
562
+		// Filter by class if requested
563
+
564
+		$classq = array();
565
+
566
+		foreach ($query->classes as $class) {
567
+			if (!empty($class['includeSubclasses'])) {
568
+				$classq[] = 'ClassHierarchy:'.$class['class'];
569
+			} else {
570
+				$classq[] = 'ClassName:'.$class['class'];
571
+			}
572
+		}
573
+
574
+		if ($classq) {
575
+			$fq[] = '+('.implode(' ', $classq).')';
576
+		}
577
+
578
+		// Filter by filters
579
+
580
+		foreach ($query->require as $field => $values) {
581
+			$requireq = array();
582
+
583
+			foreach ($values as $value) {
584
+				if ($value === SearchQuery::$missing) {
585
+					$requireq[] = "(*:* -{$field}:[* TO *])";
586
+				} elseif ($value === SearchQuery::$present) {
587
+					$requireq[] = "{$field}:[* TO *]";
588
+				} elseif ($value instanceof SearchQuery_Range) {
589
+					$start = $value->start;
590
+					if ($start === null) {
591
+						$start = '*';
592
+					}
593
+					$end = $value->end;
594
+					if ($end === null) {
595
+						$end = '*';
596
+					}
597
+					$requireq[] = "$field:[$start TO $end]";
598
+				} else {
599
+					$requireq[] = $field.':"'.$value.'"';
600
+				}
601
+			}
602
+
603
+			$fq[] = '+('.implode(' ', $requireq).')';
604
+		}
605
+
606
+		foreach ($query->exclude as $field => $values) {
607
+			$excludeq = array();
608
+			$missing = false;
609
+
610
+			foreach ($values as $value) {
611
+				if ($value === SearchQuery::$missing) {
612
+					$missing = true;
613
+				} elseif ($value === SearchQuery::$present) {
614
+					$excludeq[] = "{$field}:[* TO *]";
615
+				} elseif ($value instanceof SearchQuery_Range) {
616
+					$start = $value->start;
617
+					if ($start === null) {
618
+						$start = '*';
619
+					}
620
+					$end = $value->end;
621
+					if ($end === null) {
622
+						$end = '*';
623
+					}
624
+					$excludeq[] = "$field:[$start TO $end]";
625
+				} else {
626
+					$excludeq[] = $field.':"'.$value.'"';
627
+				}
628
+			}
629
+
630
+			$fq[] = ($missing ? "+{$field}:[* TO *] " : '') . '-('.implode(' ', $excludeq).')';
631
+		}
632 632
 
633 633
 //		if(!headers_sent()) {
634 634
 //			if ($q) header('X-Query: '.implode(' ', $q));
635 635
 //			if ($fq) header('X-Filters: "'.implode('", "', $fq).'"');
636 636
 //		}
637 637
 
638
-        if ($offset == -1) {
639
-            $offset = $query->start;
640
-        }
641
-        if ($limit == -1) {
642
-            $limit = $query->limit;
643
-        }
644
-        if ($limit == -1) {
645
-            $limit = SearchQuery::$default_page_size;
646
-        }
647
-
648
-        $params = array_merge($params, array('fq' => implode(' ', $fq)));
649
-
650
-        $res = $service->search(
651
-            $q ? implode(' ', $q) : '*:*',
652
-            $offset,
653
-            $limit,
654
-            $params,
655
-            Apache_Solr_Service::METHOD_POST
656
-        );
657
-        //Debug::dump($res);
658
-
659
-        $results = new ArrayList();
660
-        if ($res->getHttpStatus() >= 200 && $res->getHttpStatus() < 300) {
661
-            foreach ($res->response->docs as $doc) {
662
-                $result = DataObject::get_by_id($doc->ClassName, $doc->ID);
663
-                if ($result) {
664
-                    $results->push($result);
665
-
666
-                    // Add highlighting (optional)
667
-                    $docId = $doc->_documentid;
668
-                    if ($res->highlighting && $res->highlighting->$docId) {
669
-                        // TODO Create decorator class for search results rather than adding arbitrary object properties
670
-                        // TODO Allow specifying highlighted field, and lazy loading
671
-                        // in case the search API needs another query (similar to SphinxSearchable->buildExcerpt()).
672
-                        $combinedHighlights = array();
673
-                        foreach ($res->highlighting->$docId as $field => $highlights) {
674
-                            $combinedHighlights = array_merge($combinedHighlights, $highlights);
675
-                        }
676
-
677
-                        // Remove entity-encoded U+FFFD replacement character. It signifies non-displayable characters,
678
-                        // and shows up as an encoding error in browsers.
679
-                        $result->Excerpt = DBField::create_field(
680
-                            'HTMLText',
681
-                            str_replace(
682
-                                '&#65533;',
683
-                                '',
684
-                                implode(' ... ', $combinedHighlights)
685
-                            )
686
-                        );
687
-                    }
688
-                }
689
-            }
690
-            $numFound = $res->response->numFound;
691
-        } else {
692
-            $numFound = 0;
693
-        }
694
-
695
-        $ret = array();
696
-        $ret['Matches'] = new PaginatedList($results);
697
-        $ret['Matches']->setLimitItems(false);
698
-        // Tell PaginatedList how many results there are
699
-        $ret['Matches']->setTotalItems($numFound);
700
-        // Results for current page start at $offset
701
-        $ret['Matches']->setPageStart($offset);
702
-        // Results per page
703
-        $ret['Matches']->setPageLength($limit);
704
-
705
-        // Facets
706
-        //Debug::dump($res);
707
-        if (isset($res->facet_counts->facet_fields)) {
708
-            $ret['Facets'] = $this->buildFacetResults($res->facet_counts->facet_fields, $facetSpec);
709
-        }
710
-
711
-        // Suggestions (requires custom setup, assumes spellcheck.collate=true)
712
-        if (isset($res->spellcheck->suggestions->collation)) {
713
-            $ret['Suggestion'] = $res->spellcheck->suggestions->collation;
714
-        }
715
-
716
-        return new ArrayData($ret);
717
-    }
718
-
719
-
720
-    /**
721
-     * @param stdClass $facetFields
722
-     * @param array $facetSpec
723
-     * @return ArrayList
724
-     */
725
-    protected function buildFacetResults($facetFields, array $facetSpec)
726
-    {
727
-        $out = new ArrayList;
728
-
729
-        foreach ($facetSpec as $field => $facet) {
730
-            if ($facet['Type'] == ShopSearch::FACET_TYPE_RANGE) {
731
-                // If it's a range facet, set up the min/max
732
-                // TODO: we could probably get the real min and max with solr's range faceting if we tried
733
-                if (isset($facet['RangeMin'])) {
734
-                    $facet['MinValue'] = $facet['RangeMin'];
735
-                }
736
-                if (isset($facet['RangeMax'])) {
737
-                    $facet['MaxValue'] = $facet['RangeMax'];
738
-                }
739
-                $out->push(new ArrayData($facet));
740
-            } elseif (isset($this->fieldMap[$field])) {
741
-                // Otherwise, look through Solr's results
742
-                $mySolrName = $this->fieldMap[$field];
743
-                foreach ($facetFields as $solrName => $values) {
744
-                    if ($solrName == $mySolrName) {
745
-                        // we found a match, look through the values we were given
746
-                        foreach ($values as $val => $count) {
747
-                            if (!isset($facet['Values'][$val])) {
748
-                                // for link type facets we want to add anything
749
-                                // for checkboxes, if it's not in the provided list we leave it out
750
-                                if ($facet['Type'] != ShopSearch::FACET_TYPE_CHECKBOX && $count > 0) {
751
-                                    $facet['Values'][$val] = new ArrayData(array(
752
-                                        'Label'     => $val,
753
-                                        'Value'     => $val,
754
-                                        'Count'     => $count,
755
-                                    ));
756
-                                }
757
-                            } elseif ($facet['Values'][$val]) {
758
-                                $facet['Values'][$val]->Count = $count;
759
-                            }
760
-                        }
761
-                    }
762
-                }
763
-
764
-                // then add that to the stack
765
-                $facet['Values'] = new ArrayList($facet['Values']);
766
-                $out->push(new ArrayData($facet));
767
-            }
768
-        }
769
-
770
-        //Debug::dump($out);
771
-        return $out;
772
-    }
638
+		if ($offset == -1) {
639
+			$offset = $query->start;
640
+		}
641
+		if ($limit == -1) {
642
+			$limit = $query->limit;
643
+		}
644
+		if ($limit == -1) {
645
+			$limit = SearchQuery::$default_page_size;
646
+		}
647
+
648
+		$params = array_merge($params, array('fq' => implode(' ', $fq)));
649
+
650
+		$res = $service->search(
651
+			$q ? implode(' ', $q) : '*:*',
652
+			$offset,
653
+			$limit,
654
+			$params,
655
+			Apache_Solr_Service::METHOD_POST
656
+		);
657
+		//Debug::dump($res);
658
+
659
+		$results = new ArrayList();
660
+		if ($res->getHttpStatus() >= 200 && $res->getHttpStatus() < 300) {
661
+			foreach ($res->response->docs as $doc) {
662
+				$result = DataObject::get_by_id($doc->ClassName, $doc->ID);
663
+				if ($result) {
664
+					$results->push($result);
665
+
666
+					// Add highlighting (optional)
667
+					$docId = $doc->_documentid;
668
+					if ($res->highlighting && $res->highlighting->$docId) {
669
+						// TODO Create decorator class for search results rather than adding arbitrary object properties
670
+						// TODO Allow specifying highlighted field, and lazy loading
671
+						// in case the search API needs another query (similar to SphinxSearchable->buildExcerpt()).
672
+						$combinedHighlights = array();
673
+						foreach ($res->highlighting->$docId as $field => $highlights) {
674
+							$combinedHighlights = array_merge($combinedHighlights, $highlights);
675
+						}
676
+
677
+						// Remove entity-encoded U+FFFD replacement character. It signifies non-displayable characters,
678
+						// and shows up as an encoding error in browsers.
679
+						$result->Excerpt = DBField::create_field(
680
+							'HTMLText',
681
+							str_replace(
682
+								'&#65533;',
683
+								'',
684
+								implode(' ... ', $combinedHighlights)
685
+							)
686
+						);
687
+					}
688
+				}
689
+			}
690
+			$numFound = $res->response->numFound;
691
+		} else {
692
+			$numFound = 0;
693
+		}
694
+
695
+		$ret = array();
696
+		$ret['Matches'] = new PaginatedList($results);
697
+		$ret['Matches']->setLimitItems(false);
698
+		// Tell PaginatedList how many results there are
699
+		$ret['Matches']->setTotalItems($numFound);
700
+		// Results for current page start at $offset
701
+		$ret['Matches']->setPageStart($offset);
702
+		// Results per page
703
+		$ret['Matches']->setPageLength($limit);
704
+
705
+		// Facets
706
+		//Debug::dump($res);
707
+		if (isset($res->facet_counts->facet_fields)) {
708
+			$ret['Facets'] = $this->buildFacetResults($res->facet_counts->facet_fields, $facetSpec);
709
+		}
710
+
711
+		// Suggestions (requires custom setup, assumes spellcheck.collate=true)
712
+		if (isset($res->spellcheck->suggestions->collation)) {
713
+			$ret['Suggestion'] = $res->spellcheck->suggestions->collation;
714
+		}
715
+
716
+		return new ArrayData($ret);
717
+	}
718
+
719
+
720
+	/**
721
+	 * @param stdClass $facetFields
722
+	 * @param array $facetSpec
723
+	 * @return ArrayList
724
+	 */
725
+	protected function buildFacetResults($facetFields, array $facetSpec)
726
+	{
727
+		$out = new ArrayList;
728
+
729
+		foreach ($facetSpec as $field => $facet) {
730
+			if ($facet['Type'] == ShopSearch::FACET_TYPE_RANGE) {
731
+				// If it's a range facet, set up the min/max
732
+				// TODO: we could probably get the real min and max with solr's range faceting if we tried
733
+				if (isset($facet['RangeMin'])) {
734
+					$facet['MinValue'] = $facet['RangeMin'];
735
+				}
736
+				if (isset($facet['RangeMax'])) {
737
+					$facet['MaxValue'] = $facet['RangeMax'];
738
+				}
739
+				$out->push(new ArrayData($facet));
740
+			} elseif (isset($this->fieldMap[$field])) {
741
+				// Otherwise, look through Solr's results
742
+				$mySolrName = $this->fieldMap[$field];
743
+				foreach ($facetFields as $solrName => $values) {
744
+					if ($solrName == $mySolrName) {
745
+						// we found a match, look through the values we were given
746
+						foreach ($values as $val => $count) {
747
+							if (!isset($facet['Values'][$val])) {
748
+								// for link type facets we want to add anything
749
+								// for checkboxes, if it's not in the provided list we leave it out
750
+								if ($facet['Type'] != ShopSearch::FACET_TYPE_CHECKBOX && $count > 0) {
751
+									$facet['Values'][$val] = new ArrayData(array(
752
+										'Label'     => $val,
753
+										'Value'     => $val,
754
+										'Count'     => $count,
755
+									));
756
+								}
757
+							} elseif ($facet['Values'][$val]) {
758
+								$facet['Values'][$val]->Count = $count;
759
+							}
760
+						}
761
+					}
762
+				}
763
+
764
+				// then add that to the stack
765
+				$facet['Values'] = new ArrayList($facet['Values']);
766
+				$out->push(new ArrayData($facet));
767
+			}
768
+		}
769
+
770
+		//Debug::dump($out);
771
+		return $out;
772
+	}
773 773
 }
Please login to merge, or discard this patch.
Spacing   +18 added lines, -18 removed lines patch added patch discarded remove patch
@@ -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(
@@ -371,14 +371,14 @@  discard block
 block discarded – undo
371 371
 
372 372
         foreach ($query->classes as $class) {
373 373
             if (!empty($class['includeSubclasses'])) {
374
-                $classq[] = 'ClassHierarchy:'.$class['class'];
374
+                $classq[] = 'ClassHierarchy:' . $class['class'];
375 375
             } else {
376
-                $classq[] = 'ClassName:'.$class['class'];
376
+                $classq[] = 'ClassName:' . $class['class'];
377 377
             }
378 378
         }
379 379
 
380 380
         if ($classq) {
381
-            $fq[] = '+('.implode(' ', $classq).')';
381
+            $fq[] = '+(' . implode(' ', $classq) . ')';
382 382
         }
383 383
 
384 384
         // Filter by filters
@@ -401,11 +401,11 @@  discard block
 block discarded – undo
401 401
                     }
402 402
                     $requireq[] = "$field:[$start TO $end]";
403 403
                 } else {
404
-                    $requireq[] = $field.':"'.$value.'"';
404
+                    $requireq[] = $field . ':"' . $value . '"';
405 405
                 }
406 406
             }
407 407
 
408
-            $fq[] = '+('.implode(' ', $requireq).')';
408
+            $fq[] = '+(' . implode(' ', $requireq) . ')';
409 409
         }
410 410
 
411 411
         foreach ($query->exclude as $field => $values) {
@@ -428,11 +428,11 @@  discard block
 block discarded – undo
428 428
                     }
429 429
                     $excludeq[] = "$field:[$start TO $end]";
430 430
                 } else {
431
-                    $excludeq[] = $field.':"'.$value.'"';
431
+                    $excludeq[] = $field . ':"' . $value . '"';
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()) {
@@ -550,11 +550,11 @@  discard block
 block discarded – undo
550 550
                     $searchq = array();
551 551
                     foreach ($fields as $field) {
552 552
                         $boost = (isset($search['boost'][$field])) ? '^' . $search['boost'][$field] : '';
553
-                        $searchq[] = "{$field}:".$part.$fuzzy.$boost;
553
+                        $searchq[] = "{$field}:" . $part . $fuzzy . $boost;
554 554
                     }
555
-                    $q[] = '+('.implode(' OR ', $searchq).')';
555
+                    $q[] = '+(' . implode(' OR ', $searchq) . ')';
556 556
                 } else {
557
-                    $q[] = '+'.$part.$fuzzy;
557
+                    $q[] = '+' . $part . $fuzzy;
558 558
                 }
559 559
             }
560 560
         }
@@ -565,14 +565,14 @@  discard block
 block discarded – undo
565 565
 
566 566
         foreach ($query->classes as $class) {
567 567
             if (!empty($class['includeSubclasses'])) {
568
-                $classq[] = 'ClassHierarchy:'.$class['class'];
568
+                $classq[] = 'ClassHierarchy:' . $class['class'];
569 569
             } else {
570
-                $classq[] = 'ClassName:'.$class['class'];
570
+                $classq[] = 'ClassName:' . $class['class'];
571 571
             }
572 572
         }
573 573
 
574 574
         if ($classq) {
575
-            $fq[] = '+('.implode(' ', $classq).')';
575
+            $fq[] = '+(' . implode(' ', $classq) . ')';
576 576
         }
577 577
 
578 578
         // Filter by filters
@@ -596,11 +596,11 @@  discard block
 block discarded – undo
596 596
                     }
597 597
                     $requireq[] = "$field:[$start TO $end]";
598 598
                 } else {
599
-                    $requireq[] = $field.':"'.$value.'"';
599
+                    $requireq[] = $field . ':"' . $value . '"';
600 600
                 }
601 601
             }
602 602
 
603
-            $fq[] = '+('.implode(' ', $requireq).')';
603
+            $fq[] = '+(' . implode(' ', $requireq) . ')';
604 604
         }
605 605
 
606 606
         foreach ($query->exclude as $field => $values) {
@@ -623,11 +623,11 @@  discard block
 block discarded – undo
623 623
                     }
624 624
                     $excludeq[] = "$field:[$start TO $end]";
625 625
                 } else {
626
-                    $excludeq[] = $field.':"'.$value.'"';
626
+                    $excludeq[] = $field . ':"' . $value . '"';
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.
code/helpers/BetweenFilter.php 1 patch
Indentation   +81 added lines, -81 removed lines patch added patch discarded remove patch
@@ -1,98 +1,98 @@
 block discarded – undo
1 1
 <?php
2 2
 /**
3
- * Checks if the value is within a range (inclusive). Equivalent
4
- * of mysql's between statement but doesn't use that for compatibility.
5
- *
6
- * @author Mark Guinn <[email protected]>
7
- * @date 10.18.2013
8
- * @package shop_Search
9
- * @subpackage helpers
10
- */
3
+	 * Checks if the value is within a range (inclusive). Equivalent
4
+	 * of mysql's between statement but doesn't use that for compatibility.
5
+	 *
6
+	 * @author Mark Guinn <[email protected]>
7
+	 * @date 10.18.2013
8
+	 * @package shop_Search
9
+	 * @subpackage helpers
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/helpers/FacetedCategory.php 2 patches
Indentation   +148 added lines, -148 removed lines patch added patch discarded remove patch
@@ -1,153 +1,153 @@
 block discarded – undo
1 1
 <?php
2 2
 /**
3
- * This extension can be applied to ProductCategory
4
- * to allow categories to have facets as well.
5
- *
6
- * NOTE: You could apply this to either ProductCategory
7
- * or ProductCategory_Controller. I tend to use the model b/c
8
- * that will also cover some other cases like where you
9
- * might list subcategory products on the parent category page.
10
- * In such a case those products would be filtered as well.
11
- *
12
- * @author Mark Guinn <[email protected]>
13
- * @date 10.22.2013
14
- * @package shop_search
15
- * @subpackage helpers
16
- */
3
+	 * This extension can be applied to ProductCategory
4
+	 * to allow categories to have facets as well.
5
+	 *
6
+	 * NOTE: You could apply this to either ProductCategory
7
+	 * or ProductCategory_Controller. I tend to use the model b/c
8
+	 * that will also cover some other cases like where you
9
+	 * might list subcategory products on the parent category page.
10
+	 * In such a case those products would be filtered as well.
11
+	 *
12
+	 * @author Mark Guinn <[email protected]>
13
+	 * @date 10.22.2013
14
+	 * @package shop_search
15
+	 * @subpackage helpers
16
+	 */
17 17
 class FacetedCategory extends SiteTreeExtension
18 18
 {
19
-    private static $db = array(
20
-        'DisabledFacets' => 'Text', // This will be a comma-delimited list of facets that aren't used for a given category
21
-    );
22
-
23
-    /** @var array - facet definition - see ShopSearch and/or docs/en/Facets.md for format */
24
-    private static $facets = array();
25
-
26
-    /** @var bool - if true there will be a tab in the cms to disable some or all defined facets */
27
-    private static $show_disabled_facets_tab = true;
28
-
29
-    /** @var string - which method should we use to get the child products for FilteredProducts */
30
-    private static $products_method = 'ProductsShowable';
31
-
32
-    /** @var bool - automatically create facets for static attributes */
33
-    private static $auto_facet_attributes = false;
34
-
35
-
36
-    /**
37
-     * @param FieldList $fields
38
-     */
39
-    public function updateCMSFields(FieldList $fields)
40
-    {
41
-        if (Config::inst()->get('FacetedCategory', 'show_disabled_facets_tab')) {
42
-            $spec = FacetHelper::inst()->expandFacetSpec($this->getFacetSpec());
43
-            $facets = array();
44
-            foreach ($spec as $f => $v) {
45
-                $facets[$f] = $v['Label'];
46
-            }
47
-            $fields->addFieldToTab('Root.Facets', CheckboxSetField::create('DisabledFacets', "Don't show the following facets for this category:", $facets));
48
-        }
49
-    }
50
-
51
-
52
-    /**
53
-     * @return Controller
54
-     */
55
-    protected function getController()
56
-    {
57
-        return ($this->owner instanceof Controller) ? $this->owner : Controller::curr();
58
-    }
59
-
60
-
61
-    /**
62
-     * @return array
63
-     */
64
-    protected function getFacetSpec()
65
-    {
66
-        $spec = Config::inst()->get('FacetedCategory', 'facets');
67
-        return (empty($spec) || !is_array($spec)) ? array() : $spec;
68
-    }
69
-
70
-
71
-    /**
72
-     * @return array
73
-     */
74
-    protected function getFilters()
75
-    {
76
-        $qs_f       = Config::inst()->get('ShopSearch', 'qs_filters');
77
-        if (!$qs_f) {
78
-            return array();
79
-        }
80
-        $request    = $this->getController()->getRequest();
81
-        $filters    = $request->requestVar($qs_f);
82
-        if (empty($filters) || !is_array($filters)) {
83
-            return array();
84
-        }
85
-        return FacetHelper::inst()->scrubFilters($filters);
86
-    }
87
-
88
-
89
-    /**
90
-     * @param bool $recursive
91
-     * @return mixed
92
-     */
93
-    public function FilteredProducts($recursive=true)
94
-    {
95
-        if (!isset($this->_filteredProducts)) {
96
-            $fn = Config::inst()->get('FacetedCategory', 'products_method');
97
-            if (empty($fn)) {
98
-                $fn = 'ProductsShowable';
99
-            }
100
-            $this->_filteredProducts = $this->owner->$fn($recursive);
101
-            $this->_filteredProducts = FacetHelper::inst()->addFiltersToDataList($this->_filteredProducts, $this->getFilters());
102
-        }
103
-
104
-        return $this->_filteredProducts;
105
-    }
106
-
107
-    protected $_filteredProducts;
108
-
109
-
110
-    /**
111
-     * @return array
112
-     */
113
-    public function getDisabledFacetsArray()
114
-    {
115
-        if (empty($this->owner->DisabledFacets)) {
116
-            return array();
117
-        }
118
-        return explode(',', $this->owner->DisabledFacets);
119
-    }
120
-
121
-
122
-    /**
123
-     * @return ArrayList
124
-     */
125
-    public function Facets()
126
-    {
127
-        $spec       = $this->getFacetSpec();
128
-        if (empty($spec)) {
129
-            return new ArrayList;
130
-        }
131
-
132
-        // remove any disabled facets
133
-        foreach ($this->getDisabledFacetsArray() as $disabled) {
134
-            if (isset($spec[$disabled])) {
135
-                unset($spec[$disabled]);
136
-            }
137
-        }
138
-
139
-        $request    = $this->getController()->getRequest();
140
-        $baseLink   = $request->getURL(false);
141
-        $filters    = $this->getFilters();
142
-        $baseParams = array_merge($request->requestVars(), array());
143
-        unset($baseParams['url']);
144
-
145
-        $products   = $this->owner->hasMethod('ProductsForFaceting') ? $this->owner->ProductsForFaceting() : $this->FilteredProducts();
146
-        $facets     = FacetHelper::inst()->buildFacets($products, $spec, (bool)Config::inst()->get('FacetedCategory', 'auto_facet_attributes'));
147
-        $facets     = FacetHelper::inst()->transformHierarchies($facets);
148
-        $facets     = FacetHelper::inst()->updateFacetState($facets, $filters);
149
-        $facets     = FacetHelper::inst()->insertFacetLinks($facets, $baseParams, $baseLink);
150
-
151
-        return $facets;
152
-    }
19
+	private static $db = array(
20
+		'DisabledFacets' => 'Text', // This will be a comma-delimited list of facets that aren't used for a given category
21
+	);
22
+
23
+	/** @var array - facet definition - see ShopSearch and/or docs/en/Facets.md for format */
24
+	private static $facets = array();
25
+
26
+	/** @var bool - if true there will be a tab in the cms to disable some or all defined facets */
27
+	private static $show_disabled_facets_tab = true;
28
+
29
+	/** @var string - which method should we use to get the child products for FilteredProducts */
30
+	private static $products_method = 'ProductsShowable';
31
+
32
+	/** @var bool - automatically create facets for static attributes */
33
+	private static $auto_facet_attributes = false;
34
+
35
+
36
+	/**
37
+	 * @param FieldList $fields
38
+	 */
39
+	public function updateCMSFields(FieldList $fields)
40
+	{
41
+		if (Config::inst()->get('FacetedCategory', 'show_disabled_facets_tab')) {
42
+			$spec = FacetHelper::inst()->expandFacetSpec($this->getFacetSpec());
43
+			$facets = array();
44
+			foreach ($spec as $f => $v) {
45
+				$facets[$f] = $v['Label'];
46
+			}
47
+			$fields->addFieldToTab('Root.Facets', CheckboxSetField::create('DisabledFacets', "Don't show the following facets for this category:", $facets));
48
+		}
49
+	}
50
+
51
+
52
+	/**
53
+	 * @return Controller
54
+	 */
55
+	protected function getController()
56
+	{
57
+		return ($this->owner instanceof Controller) ? $this->owner : Controller::curr();
58
+	}
59
+
60
+
61
+	/**
62
+	 * @return array
63
+	 */
64
+	protected function getFacetSpec()
65
+	{
66
+		$spec = Config::inst()->get('FacetedCategory', 'facets');
67
+		return (empty($spec) || !is_array($spec)) ? array() : $spec;
68
+	}
69
+
70
+
71
+	/**
72
+	 * @return array
73
+	 */
74
+	protected function getFilters()
75
+	{
76
+		$qs_f       = Config::inst()->get('ShopSearch', 'qs_filters');
77
+		if (!$qs_f) {
78
+			return array();
79
+		}
80
+		$request    = $this->getController()->getRequest();
81
+		$filters    = $request->requestVar($qs_f);
82
+		if (empty($filters) || !is_array($filters)) {
83
+			return array();
84
+		}
85
+		return FacetHelper::inst()->scrubFilters($filters);
86
+	}
87
+
88
+
89
+	/**
90
+	 * @param bool $recursive
91
+	 * @return mixed
92
+	 */
93
+	public function FilteredProducts($recursive=true)
94
+	{
95
+		if (!isset($this->_filteredProducts)) {
96
+			$fn = Config::inst()->get('FacetedCategory', 'products_method');
97
+			if (empty($fn)) {
98
+				$fn = 'ProductsShowable';
99
+			}
100
+			$this->_filteredProducts = $this->owner->$fn($recursive);
101
+			$this->_filteredProducts = FacetHelper::inst()->addFiltersToDataList($this->_filteredProducts, $this->getFilters());
102
+		}
103
+
104
+		return $this->_filteredProducts;
105
+	}
106
+
107
+	protected $_filteredProducts;
108
+
109
+
110
+	/**
111
+	 * @return array
112
+	 */
113
+	public function getDisabledFacetsArray()
114
+	{
115
+		if (empty($this->owner->DisabledFacets)) {
116
+			return array();
117
+		}
118
+		return explode(',', $this->owner->DisabledFacets);
119
+	}
120
+
121
+
122
+	/**
123
+	 * @return ArrayList
124
+	 */
125
+	public function Facets()
126
+	{
127
+		$spec       = $this->getFacetSpec();
128
+		if (empty($spec)) {
129
+			return new ArrayList;
130
+		}
131
+
132
+		// remove any disabled facets
133
+		foreach ($this->getDisabledFacetsArray() as $disabled) {
134
+			if (isset($spec[$disabled])) {
135
+				unset($spec[$disabled]);
136
+			}
137
+		}
138
+
139
+		$request    = $this->getController()->getRequest();
140
+		$baseLink   = $request->getURL(false);
141
+		$filters    = $this->getFilters();
142
+		$baseParams = array_merge($request->requestVars(), array());
143
+		unset($baseParams['url']);
144
+
145
+		$products   = $this->owner->hasMethod('ProductsForFaceting') ? $this->owner->ProductsForFaceting() : $this->FilteredProducts();
146
+		$facets     = FacetHelper::inst()->buildFacets($products, $spec, (bool)Config::inst()->get('FacetedCategory', 'auto_facet_attributes'));
147
+		$facets     = FacetHelper::inst()->transformHierarchies($facets);
148
+		$facets     = FacetHelper::inst()->updateFacetState($facets, $filters);
149
+		$facets     = FacetHelper::inst()->insertFacetLinks($facets, $baseParams, $baseLink);
150
+
151
+		return $facets;
152
+	}
153 153
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -73,7 +73,7 @@  discard block
 block discarded – undo
73 73
      */
74 74
     protected function getFilters()
75 75
     {
76
-        $qs_f       = Config::inst()->get('ShopSearch', 'qs_filters');
76
+        $qs_f = Config::inst()->get('ShopSearch', 'qs_filters');
77 77
         if (!$qs_f) {
78 78
             return array();
79 79
         }
@@ -90,7 +90,7 @@  discard block
 block discarded – undo
90 90
      * @param bool $recursive
91 91
      * @return mixed
92 92
      */
93
-    public function FilteredProducts($recursive=true)
93
+    public function FilteredProducts($recursive = true)
94 94
     {
95 95
         if (!isset($this->_filteredProducts)) {
96 96
             $fn = Config::inst()->get('FacetedCategory', 'products_method');
@@ -124,7 +124,7 @@  discard block
 block discarded – undo
124 124
      */
125 125
     public function Facets()
126 126
     {
127
-        $spec       = $this->getFacetSpec();
127
+        $spec = $this->getFacetSpec();
128 128
         if (empty($spec)) {
129 129
             return new ArrayList;
130 130
         }
Please login to merge, or discard this patch.
code/helpers/ProductCategoryHelperMethods.php 2 patches
Indentation   +132 added lines, -132 removed lines patch added patch discarded remove patch
@@ -11,136 +11,136 @@
 block discarded – undo
11 11
  */
12 12
 class ProductCategoryHelperMethods extends DataExtension
13 13
 {
14
-    /**
15
-     * Includes Parent() and ProductCategories() and all the parent categories of each.
16
-     * @param bool $recursive [optional] - include the parents of the categories as well? - default: true
17
-     * @return array - array of productCategory records
18
-     */
19
-    protected function buildParentCategoriesArray($recursive=true)
20
-    {
21
-        $out = array();
22
-
23
-        // add the main parent
24
-        $parent = $this->owner->Parent();
25
-        if (!$parent || !$parent->exists()) {
26
-            return $out;
27
-        }
28
-        if ($parent->ClassName == 'ProductCategory') {
29
-            $out[$parent->ID] = $parent;
30
-        }
31
-
32
-        if ($recursive) {
33
-            foreach ($parent->getAncestors() as $rec) {
34
-                if ($rec->ClassName == 'ProductCategory') {
35
-                    $out[$rec->ID] = $rec;
36
-                }
37
-            }
38
-        }
39
-
40
-        // add any secondary categories
41
-        foreach ($this->owner->ProductCategories() as $cat) {
42
-            $out[$cat->ID] = $cat;
43
-            if ($recursive) {
44
-                foreach ($cat->getAncestors() as $rec) {
45
-                    if ($rec->ClassName == 'ProductCategory') {
46
-                        $out[$rec->ID] = $rec;
47
-                    }
48
-                }
49
-            }
50
-        }
51
-
52
-        return $out;
53
-    }
54
-
55
-
56
-    /**
57
-     * Includes Parent() and ProductCategories() and all the parent categories of each.
58
-     * @return ArrayList - array of productCategory records
59
-     */
60
-    public function getAllCategories()
61
-    {
62
-        return new ArrayList($this->buildParentCategoriesArray(false));
63
-    }
64
-
65
-
66
-    /**
67
-     * Includes Parent() and ProductCategories() and all the parent categories of each.
68
-     * @return ArrayList - array of productCategory records
69
-     */
70
-    public function getAllCategoriesRecursive()
71
-    {
72
-        return new ArrayList($this->buildParentCategoriesArray(true));
73
-    }
74
-
75
-
76
-    /**
77
-     * Includes Parent() and ProductCategories()
78
-     * @return array - array of ID's
79
-     */
80
-    public function getAllCategoryIDs()
81
-    {
82
-        return array_keys($this->buildParentCategoriesArray(false));
83
-    }
84
-
85
-
86
-    /**
87
-     * Includes Parent() and ProductCategories() and all the parent categories of each.
88
-     * @return array - array of ID's
89
-     */
90
-    public function getAllCategoryIDsRecursive()
91
-    {
92
-        return array_keys($this->buildParentCategoriesArray(true));
93
-    }
94
-
95
-
96
-    /**
97
-     * Returns an array of the titles of all the categories.
98
-     * @return array - (strings)
99
-     */
100
-    public function getAllCategoryTitles()
101
-    {
102
-        $cats = $this->buildParentCategoriesArray(false);
103
-        $out = array();
104
-        foreach ($cats as $cat) {
105
-            $out[] = $cat->Title;
106
-        }
107
-        return $out;
108
-    }
109
-
110
-
111
-    /**
112
-     * Returns an array of the titles of all the categories, including grandparents, etc
113
-     * @return array - (strings)
114
-     */
115
-    public function getAllCategoryTitlesRecursive()
116
-    {
117
-        $cats = $this->buildParentCategoriesArray(true);
118
-        $out = array();
119
-        foreach ($cats as $cat) {
120
-            $out[] = $cat->Title;
121
-        }
122
-        return $out;
123
-    }
124
-
125
-
126
-    /**
127
-     * Returns a string of the titles of all the categories.
128
-     * @param string $sep
129
-     * @return string
130
-     */
131
-    public function getJoinedCategoryTitles($sep = ', ')
132
-    {
133
-        return implode($sep, $this->getAllCategoryTitles());
134
-    }
135
-
136
-
137
-    /**
138
-     * Returns a string of the titles of all the categories.
139
-     * @param string $sep
140
-     * @return string
141
-     */
142
-    public function getJoinedCategoryTitlesRecursive($sep = ', ')
143
-    {
144
-        return implode($sep, $this->getAllCategoryTitlesRecursive());
145
-    }
14
+	/**
15
+	 * Includes Parent() and ProductCategories() and all the parent categories of each.
16
+	 * @param bool $recursive [optional] - include the parents of the categories as well? - default: true
17
+	 * @return array - array of productCategory records
18
+	 */
19
+	protected function buildParentCategoriesArray($recursive=true)
20
+	{
21
+		$out = array();
22
+
23
+		// add the main parent
24
+		$parent = $this->owner->Parent();
25
+		if (!$parent || !$parent->exists()) {
26
+			return $out;
27
+		}
28
+		if ($parent->ClassName == 'ProductCategory') {
29
+			$out[$parent->ID] = $parent;
30
+		}
31
+
32
+		if ($recursive) {
33
+			foreach ($parent->getAncestors() as $rec) {
34
+				if ($rec->ClassName == 'ProductCategory') {
35
+					$out[$rec->ID] = $rec;
36
+				}
37
+			}
38
+		}
39
+
40
+		// add any secondary categories
41
+		foreach ($this->owner->ProductCategories() as $cat) {
42
+			$out[$cat->ID] = $cat;
43
+			if ($recursive) {
44
+				foreach ($cat->getAncestors() as $rec) {
45
+					if ($rec->ClassName == 'ProductCategory') {
46
+						$out[$rec->ID] = $rec;
47
+					}
48
+				}
49
+			}
50
+		}
51
+
52
+		return $out;
53
+	}
54
+
55
+
56
+	/**
57
+	 * Includes Parent() and ProductCategories() and all the parent categories of each.
58
+	 * @return ArrayList - array of productCategory records
59
+	 */
60
+	public function getAllCategories()
61
+	{
62
+		return new ArrayList($this->buildParentCategoriesArray(false));
63
+	}
64
+
65
+
66
+	/**
67
+	 * Includes Parent() and ProductCategories() and all the parent categories of each.
68
+	 * @return ArrayList - array of productCategory records
69
+	 */
70
+	public function getAllCategoriesRecursive()
71
+	{
72
+		return new ArrayList($this->buildParentCategoriesArray(true));
73
+	}
74
+
75
+
76
+	/**
77
+	 * Includes Parent() and ProductCategories()
78
+	 * @return array - array of ID's
79
+	 */
80
+	public function getAllCategoryIDs()
81
+	{
82
+		return array_keys($this->buildParentCategoriesArray(false));
83
+	}
84
+
85
+
86
+	/**
87
+	 * Includes Parent() and ProductCategories() and all the parent categories of each.
88
+	 * @return array - array of ID's
89
+	 */
90
+	public function getAllCategoryIDsRecursive()
91
+	{
92
+		return array_keys($this->buildParentCategoriesArray(true));
93
+	}
94
+
95
+
96
+	/**
97
+	 * Returns an array of the titles of all the categories.
98
+	 * @return array - (strings)
99
+	 */
100
+	public function getAllCategoryTitles()
101
+	{
102
+		$cats = $this->buildParentCategoriesArray(false);
103
+		$out = array();
104
+		foreach ($cats as $cat) {
105
+			$out[] = $cat->Title;
106
+		}
107
+		return $out;
108
+	}
109
+
110
+
111
+	/**
112
+	 * Returns an array of the titles of all the categories, including grandparents, etc
113
+	 * @return array - (strings)
114
+	 */
115
+	public function getAllCategoryTitlesRecursive()
116
+	{
117
+		$cats = $this->buildParentCategoriesArray(true);
118
+		$out = array();
119
+		foreach ($cats as $cat) {
120
+			$out[] = $cat->Title;
121
+		}
122
+		return $out;
123
+	}
124
+
125
+
126
+	/**
127
+	 * Returns a string of the titles of all the categories.
128
+	 * @param string $sep
129
+	 * @return string
130
+	 */
131
+	public function getJoinedCategoryTitles($sep = ', ')
132
+	{
133
+		return implode($sep, $this->getAllCategoryTitles());
134
+	}
135
+
136
+
137
+	/**
138
+	 * Returns a string of the titles of all the categories.
139
+	 * @param string $sep
140
+	 * @return string
141
+	 */
142
+	public function getJoinedCategoryTitlesRecursive($sep = ', ')
143
+	{
144
+		return implode($sep, $this->getAllCategoryTitlesRecursive());
145
+	}
146 146
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -16,7 +16,7 @@
 block discarded – undo
16 16
      * @param bool $recursive [optional] - include the parents of the categories as well? - default: true
17 17
      * @return array - array of productCategory records
18 18
      */
19
-    protected function buildParentCategoriesArray($recursive=true)
19
+    protected function buildParentCategoriesArray($recursive = true)
20 20
     {
21 21
         $out = array();
22 22
 
Please login to merge, or discard this patch.
code/helpers/VirtualFieldIndexQueuedJob.php 1 patch
Indentation   +51 added lines, -51 removed lines patch added patch discarded remove patch
@@ -1,69 +1,69 @@
 block discarded – undo
1 1
 <?php
2 2
 /**
3
- * If the queued jobs module is installed, this will be used instead of
4
- * updating vfi's in onBeforeWrite.
5
- *
6
- * @author  Mark Guinn <[email protected]>
7
- * @date    07.02.2015
8
- * @package shop_search
9
- * @subpackage helpers
10
- */
3
+	 * If the queued jobs module is installed, this will be used instead of
4
+	 * updating vfi's in onBeforeWrite.
5
+	 *
6
+	 * @author  Mark Guinn <[email protected]>
7
+	 * @date    07.02.2015
8
+	 * @package shop_search
9
+	 * @subpackage helpers
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, array $fields)
30
-    {
31
-        $this->setObject($object);
32
-        $this->rebuildFields = $fields;
33
-    }
25
+	/**
26
+	 * @param DataObject $object
27
+	 * @param array $fields
28
+	 */
29
+	public function __construct($object, array $fields)
30
+	{
31
+		$this->setObject($object);
32
+		$this->rebuildFields = $fields;
33
+	}
34 34
 
35 35
 
36
-    /**
37
-     * Helper method
38
-     */
39
-    public function triggerProcessing()
40
-    {
41
-        singleton('QueuedJobService')->queueJob($this);
42
-    }
36
+	/**
37
+	 * Helper method
38
+	 */
39
+	public function triggerProcessing()
40
+	{
41
+		singleton('QueuedJobService')->queueJob($this);
42
+	}
43 43
 
44 44
 
45
-    /**
46
-     * @return string
47
-     */
48
-    public function getTitle()
49
-    {
50
-        $obj = $this->getObject();
51
-        return "Update Virtual Field Indexes: " . ($obj ? $obj->getTitle() : '???');
52
-    }
45
+	/**
46
+	 * @return string
47
+	 */
48
+	public function getTitle()
49
+	{
50
+		$obj = $this->getObject();
51
+		return "Update Virtual Field Indexes: " . ($obj ? $obj->getTitle() : '???');
52
+	}
53 53
 
54 54
 
55
-    /**
56
-     * Reprocess any needed fields
57
-     */
58
-    public function process()
59
-    {
60
-        Versioned::reading_stage('Stage');
61
-        $obj = $this->getObject();
55
+	/**
56
+	 * Reprocess any needed fields
57
+	 */
58
+	public function process()
59
+	{
60
+		Versioned::reading_stage('Stage');
61
+		$obj = $this->getObject();
62 62
 
63
-        if ($obj) {
64
-            $obj->rebuildVFI();
65
-        }
63
+		if ($obj) {
64
+			$obj->rebuildVFI();
65
+		}
66 66
 
67
-        $this->isComplete = true;
68
-    }
67
+		$this->isComplete = true;
68
+	}
69 69
 }
Please login to merge, or discard this patch.
code/reports/SearchTermsReport.php 1 patch
Indentation   +36 added lines, -36 removed lines patch added patch discarded remove patch
@@ -1,43 +1,43 @@
 block discarded – undo
1 1
 <?php
2 2
 /**
3
- * Silverstripe report for searchs
4
- *
5
- * @author Mark Guinn <[email protected]>
6
- * @date 09.24.2014
7
- * @package apluswhs.com
8
- * @subpackage
9
- */
3
+	 * Silverstripe report for searchs
4
+	 *
5
+	 * @author Mark Guinn <[email protected]>
6
+	 * @date 09.24.2014
7
+	 * @package apluswhs.com
8
+	 * @subpackage
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.