1
|
|
|
<?php |
2
|
|
|
namespace ApacheSolrForTypo3\Solr\Domain\Search\ResultSet; |
3
|
|
|
|
4
|
|
|
/* |
5
|
|
|
* This file is part of the TYPO3 CMS project. |
6
|
|
|
* |
7
|
|
|
* It is free software; you can redistribute it and/or modify it under |
8
|
|
|
* the terms of the GNU General Public License, either version 2 |
9
|
|
|
* of the License, or any later version. |
10
|
|
|
* |
11
|
|
|
* For the full copyright and license information, please read the |
12
|
|
|
* LICENSE.txt file that was distributed with this source code. |
13
|
|
|
* |
14
|
|
|
* The TYPO3 project - inspiring people to share! |
15
|
|
|
*/ |
16
|
|
|
|
17
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\AbstractFacet; |
18
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\FacetRegistry; |
19
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\RequirementsService; |
20
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Sorting\Sorting; |
21
|
|
|
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Spellchecking\Suggestion; |
22
|
|
|
use TYPO3\CMS\Core\Utility\GeneralUtility; |
23
|
|
|
use TYPO3\CMS\Extbase\Object\ObjectManager; |
24
|
|
|
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface; |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* This processor is used to transform the solr response into a |
28
|
|
|
* domain object hierarchy that can be used in the application (controller and view). |
29
|
|
|
* |
30
|
|
|
* @author Frans Saris <[email protected]> |
31
|
|
|
* @author Timo Hund <[email protected]> |
32
|
|
|
*/ |
33
|
|
|
class ResultSetReconstitutionProcessor implements SearchResultSetProcessor |
34
|
|
|
{ |
35
|
|
|
/** |
36
|
|
|
* @var ObjectManagerInterface |
37
|
|
|
*/ |
38
|
|
|
protected $objectManager; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @return ObjectManagerInterface |
42
|
|
|
*/ |
43
|
|
|
public function getObjectManager() |
44
|
61 |
|
{ |
45
|
|
|
if ($this->objectManager === null) { |
46
|
61 |
|
$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class); |
47
|
32 |
|
} |
48
|
|
|
return $this->objectManager; |
49
|
61 |
|
} |
50
|
|
|
|
51
|
|
|
/** |
52
|
|
|
* @param ObjectManagerInterface $objectManager |
53
|
|
|
*/ |
54
|
|
|
public function setObjectManager($objectManager) |
55
|
29 |
|
{ |
56
|
|
|
$this->objectManager = $objectManager; |
57
|
29 |
|
} |
58
|
29 |
|
|
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @return FacetRegistry |
62
|
|
|
*/ |
63
|
|
|
protected function getFacetRegistry() |
64
|
61 |
|
{ |
65
|
|
|
// @extensionScannerIgnoreLine |
66
|
61 |
|
return $this->getObjectManager()->get(FacetRegistry::class); |
|
|
|
|
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* The implementation can be used to influence a SearchResultSet that is |
71
|
|
|
* created and processed in the SearchResultSetService. |
72
|
|
|
* |
73
|
|
|
* @param SearchResultSet $resultSet |
74
|
|
|
* @return SearchResultSet |
75
|
|
|
*/ |
76
|
72 |
|
public function process(SearchResultSet $resultSet) |
77
|
|
|
{ |
78
|
72 |
|
if (!$resultSet instanceof SearchResultSet) { |
|
|
|
|
79
|
|
|
return $resultSet; |
80
|
|
|
} |
81
|
|
|
|
82
|
72 |
|
$resultSet = $this->parseSpellCheckingResponseIntoObjects($resultSet); |
83
|
72 |
|
$resultSet = $this->parseSortingIntoObjects($resultSet); |
84
|
72 |
|
|
85
|
|
|
// here we can reconstitute other domain objects from the solr response |
86
|
|
|
$resultSet = $this->parseFacetsIntoObjects($resultSet); |
87
|
72 |
|
|
88
|
|
|
return $resultSet; |
89
|
72 |
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* @param SearchResultSet $resultSet |
93
|
|
|
* @return SearchResultSet |
94
|
|
|
*/ |
95
|
|
|
protected function parseSortingIntoObjects(SearchResultSet $resultSet) |
96
|
72 |
|
{ |
97
|
|
|
$configuration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration(); |
98
|
72 |
|
$hasSorting = $resultSet->getUsedSearchRequest()->getHasSorting(); |
99
|
72 |
|
$activeSortingName = $resultSet->getUsedSearchRequest()->getSortingName(); |
100
|
72 |
|
$activeSortingDirection = $resultSet->getUsedSearchRequest()->getSortingDirection(); |
101
|
4 |
|
|
102
|
|
|
// no configuration available |
103
|
68 |
|
if (!isset($configuration)) { |
104
|
68 |
|
return $resultSet; |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
// no sorting enabled |
108
|
|
|
if (!$configuration->getSearchSorting()) { |
109
|
|
|
return $resultSet; |
110
|
|
|
} |
111
|
72 |
|
foreach ($configuration->getSearchSortingOptionsConfiguration() as $sortingKeyName => $sortingOptions) { |
112
|
|
|
$sortingName = rtrim($sortingKeyName, '.'); |
113
|
72 |
|
$selected = false; |
114
|
72 |
|
$direction = $configuration->getSearchSortingDefaultOrderBySortOptionName($sortingName); |
115
|
72 |
|
|
116
|
72 |
|
// when we have an active sorting in the request we compare the sortingName and mark is as active and |
117
|
|
|
// use the direction from the request |
118
|
|
|
if ($hasSorting && $activeSortingName == $sortingName) { |
119
|
72 |
|
$selected = true; |
120
|
7 |
|
$direction = $activeSortingDirection; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
$field = $sortingOptions['field']; |
124
|
65 |
|
$label = $sortingOptions['label']; |
125
|
32 |
|
|
126
|
|
|
$isResetOption = $field === 'relevance'; |
127
|
33 |
|
// @todo allow stdWrap on label |
128
|
33 |
|
$sorting = $this->getObjectManager()->get(Sorting::class, $resultSet, $sortingName, $field, $direction, $label, $selected, $isResetOption); |
|
|
|
|
129
|
33 |
|
$resultSet->addSorting($sorting); |
130
|
33 |
|
} |
131
|
|
|
|
132
|
|
|
return $resultSet; |
133
|
|
|
} |
134
|
33 |
|
|
135
|
1 |
|
/** |
136
|
1 |
|
* @param SearchResultSet $resultSet |
137
|
|
|
* @return SearchResultSet |
138
|
|
|
*/ |
139
|
33 |
|
private function parseSpellCheckingResponseIntoObjects(SearchResultSet $resultSet) |
140
|
33 |
|
{ |
141
|
|
|
//read the response |
142
|
33 |
|
$response = $resultSet->getResponse(); |
143
|
|
|
|
144
|
33 |
|
if (!is_array($response->spellcheck->suggestions)) { |
145
|
33 |
|
return $resultSet; |
146
|
|
|
} |
147
|
|
|
|
148
|
33 |
|
$misspelledTerm = ''; |
149
|
|
|
foreach ($response->spellcheck->suggestions as $key => $suggestionData) { |
150
|
|
|
if (is_string($suggestionData)) { |
151
|
|
|
$misspelledTerm = $key; |
152
|
|
|
continue; |
153
|
|
|
} |
154
|
|
|
|
155
|
72 |
|
if ($misspelledTerm === '') { |
156
|
|
|
throw new \UnexpectedValueException('No missspelled term before suggestion'); |
157
|
|
|
} |
158
|
72 |
|
|
159
|
72 |
|
if (!is_object($suggestionData) && !is_array($suggestionData->suggestion)) { |
160
|
64 |
|
continue; |
161
|
|
|
} |
162
|
|
|
|
163
|
8 |
|
foreach ($suggestionData->suggestion as $suggestedTerm) { |
164
|
5 |
|
$suggestion = $this->createSuggestionFromResponseFragment($suggestionData, $suggestedTerm, $misspelledTerm); |
165
|
1 |
|
//add it to the resultSet |
166
|
|
|
$resultSet->addSpellCheckingSuggestion($suggestion); |
167
|
|
|
} |
168
|
|
|
|
169
|
5 |
|
} |
170
|
|
|
|
171
|
|
|
return $resultSet; |
172
|
|
|
} |
173
|
5 |
|
|
174
|
5 |
|
/** |
175
|
5 |
|
* @param \stdClass $suggestionData |
176
|
|
|
* @param string $suggestedTerm |
177
|
|
|
* @param string $misspelledTerm |
178
|
5 |
|
* @return Suggestion |
179
|
|
|
*/ |
180
|
|
|
private function createSuggestionFromResponseFragment($suggestionData, $suggestedTerm, $misspelledTerm) |
181
|
|
|
{ |
182
|
8 |
|
$numFound = isset($suggestionData->numFound) ? $suggestionData->numFound : 0; |
183
|
|
|
$startOffset = isset($suggestionData->startOffset) ? $suggestionData->startOffset : 0; |
184
|
|
|
$endOffset = isset($suggestionData->endOffset) ? $suggestionData->endOffset : 0; |
185
|
|
|
|
186
|
|
|
// by now we avoid to use GeneralUtility::makeInstance, since we only create a value object |
187
|
|
|
// and the usage might be a overhead. |
188
|
|
|
$suggestion = new Suggestion($suggestedTerm, $misspelledTerm, $numFound, $startOffset, $endOffset); |
189
|
|
|
return $suggestion; |
190
|
|
|
} |
191
|
5 |
|
|
192
|
|
|
/** |
193
|
5 |
|
* Parse available facets into objects |
194
|
5 |
|
* |
195
|
5 |
|
* @param SearchResultSet $resultSet |
196
|
|
|
* @return SearchResultSet |
197
|
|
|
*/ |
198
|
|
|
private function parseFacetsIntoObjects(SearchResultSet $resultSet) |
199
|
5 |
|
{ |
200
|
5 |
|
// Make sure we can access the facet configuration |
201
|
|
|
if (!$resultSet->getUsedSearchRequest() || !$resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()) { |
202
|
|
|
return $resultSet; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
// Read the response |
206
|
|
|
$response = $resultSet->getResponse(); |
207
|
|
|
if (!is_object($response->facet_counts) && !is_object($response->facets)) { |
208
|
|
|
return $resultSet; |
209
|
72 |
|
} |
210
|
|
|
|
211
|
|
|
/** @var FacetRegistry $facetRegistry */ |
212
|
72 |
|
$facetRegistry = $this->getFacetRegistry(); |
213
|
7 |
|
$facetsConfiguration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()->getSearchFacetingFacets(); |
214
|
|
|
|
215
|
|
|
foreach ($facetsConfiguration as $name => $options) { |
216
|
|
|
if (!is_array($options)) { |
217
|
65 |
|
continue; |
218
|
65 |
|
} |
219
|
4 |
|
$facetName = rtrim($name, '.'); |
220
|
|
|
$type = !empty($options['type']) ? $options['type'] : ''; |
221
|
|
|
|
222
|
|
|
$parser = $facetRegistry->getPackage($type)->getParser(); |
223
|
61 |
|
$facet = $parser->parse($resultSet, $facetName, $options); |
224
|
61 |
|
if ($facet !== null) { |
225
|
|
|
$resultSet->addFacet($facet); |
226
|
61 |
|
} |
227
|
58 |
|
} |
228
|
|
|
|
229
|
|
|
$this->applyRequirements($resultSet); |
230
|
58 |
|
|
231
|
58 |
|
return $resultSet; |
232
|
|
|
} |
233
|
58 |
|
|
234
|
58 |
|
/** |
235
|
58 |
|
* @param SearchResultSet $resultSet |
236
|
58 |
|
*/ |
237
|
|
|
protected function applyRequirements(SearchResultSet $resultSet) |
238
|
|
|
{ |
239
|
|
|
$requirementsService = $this->getRequirementsService(); |
240
|
61 |
|
$facets = $resultSet->getFacets(); |
241
|
|
|
foreach ($facets as $facet) { |
242
|
61 |
|
/** @var $facet AbstractFacet */ |
243
|
|
|
$requirementsMet = $requirementsService->getAllRequirementsMet($facet); |
244
|
|
|
$facet->setAllRequirementsMet($requirementsMet); |
245
|
|
|
} |
246
|
|
|
} |
247
|
|
|
|
248
|
61 |
|
/** |
249
|
|
|
* @return RequirementsService |
250
|
61 |
|
*/ |
251
|
61 |
|
protected function getRequirementsService() |
252
|
61 |
|
{ |
253
|
|
|
// @extensionScannerIgnoreLine |
254
|
56 |
|
return $this->getObjectManager()->get(RequirementsService::class); |
|
|
|
|
255
|
56 |
|
} |
256
|
|
|
} |
257
|
|
|
|
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.