Passed
Pull Request — master (#1318)
by
unknown
32:46
created

ResultSetReconstitutionProcessor   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 236
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Importance

Changes 0
Metric Value
wmc 37
lcom 1
cbo 9
dl 0
loc 236
rs 8.6
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getObjectManager() 0 7 2
A setObjectManager() 0 4 1
A getFacetParserRegistry() 0 4 1
A process() 0 17 2
A parseResultCount() 0 10 2
B parseSortingIntoObjects() 0 39 6
C parseSpellCheckingResponseIntoObjects() 0 29 7
A createSuggestionFromResponseFragment() 0 11 4
C parseFacetsIntoObjects() 0 34 8
A storeLastSearches() 0 20 4
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
use ApacheSolrForTypo3\Solr\Domain\Search\LastSearches\LastSearchesService;
17
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\FacetParserRegistry;
18
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Sorting\Sorting;
19
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Spellchecking\Suggestion;
20
use TYPO3\CMS\Core\Utility\GeneralUtility;
21
use TYPO3\CMS\Extbase\Object\ObjectManager;
22
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
23
24
/**
25
 * This processor is used to transform the solr response into a
26
 * domain object hierarchy that can be used in the application (controller and view).
27
 *
28
 * @todo: the logic in this class can go into the SearchResultSetService after moving the
29
 * code of EXT:solrfluid to EXT:solr
30
 *
31
 * @author Frans Saris <[email protected]>
32
 * @author Timo Hund <[email protected]>
33
 * @package ApacheSolrForTypo3\Solr\Domain\Search\ResultSet
34
 */
35
class ResultSetReconstitutionProcessor implements SearchResultSetProcessor
36
{
37
    /**
38
     * @var ObjectManagerInterface
39
     */
40
    protected $objectManager;
41
42
    /**
43
     * @return ObjectManagerInterface
44
     */
45
    public function getObjectManager()
46
    {
47
        if ($this->objectManager === null) {
48
            $this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
49
        }
50
        return $this->objectManager;
51
    }
52
53
    /**
54
     * @param ObjectManagerInterface $objectManager
55
     */
56
    public function setObjectManager($objectManager)
57
    {
58
        $this->objectManager = $objectManager;
59
    }
60
61
62
    /**
63
     * @return FacetParserRegistry
64
     */
65
    protected function getFacetParserRegistry()
66
    {
67
        return $this->getObjectManager()->get(FacetParserRegistry::class);
68
    }
69
70
    /**
71
     * The implementation can be used to influence a SearchResultSet that is
72
     * created and processed in the SearchResultSetService.
73
     *
74
     * @param SearchResultSet $resultSet
75
     * @return SearchResultSet
76
     */
77
    public function process(SearchResultSet $resultSet)
78
    {
79
        if (!$resultSet instanceof SearchResultSet) {
80
            return $resultSet;
81
        }
82
83
        $resultSet = $this->parseResultCount($resultSet);
84
        $resultSet = $this->parseSpellCheckingResponseIntoObjects($resultSet);
85
        $resultSet = $this->parseSortingIntoObjects($resultSet);
86
87
        // here we can reconstitute other domain objects from the solr response
88
        $resultSet = $this->parseFacetsIntoObjects($resultSet);
89
90
        $this->storeLastSearches($resultSet);
91
92
        return $resultSet;
93
    }
94
95
    /**
96
     * @param SearchResultSet $resultSet
97
     * @return SearchResultSet
98
     */
99
    protected function parseResultCount(SearchResultSet $resultSet)
100
    {
101
        $response = $resultSet->getResponse();
102
        if (!isset($response->response->numFound)) {
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
103
            return $resultSet;
104
        }
105
106
        $resultSet->setAllResultCount($response->response->numFound);
0 ignored issues
show
Bug introduced by
The property response does not seem to exist. Did you mean _response?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
107
        return $resultSet;
108
    }
109
110
    /**
111
     * @param SearchResultSet $resultSet
112
     * @return SearchResultSet
113
     */
114
    protected function parseSortingIntoObjects(SearchResultSet $resultSet)
115
    {
116
        $configuration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration();
117
        $hasSorting = $resultSet->getUsedSearchRequest()->getHasSorting();
118
        $activeSortingName = $resultSet->getUsedSearchRequest()->getSortingName();
119
        $activeSortingDirection = $resultSet->getUsedSearchRequest()->getSortingDirection();
120
121
        // no configuration available
122
        if (!isset($configuration)) {
123
            return $resultSet;
124
        }
125
126
        // no sorting enabled
127
        if (!$configuration->getSearchSorting()) {
128
            return $resultSet;
129
        }
130
        foreach ($configuration->getSearchSortingOptionsConfiguration() as $sortingKeyName => $sortingOptions) {
131
            $sortingName = rtrim($sortingKeyName, '.');
132
            $selected = false;
133
            $direction = $configuration->getSearchSortingDefaultOrderBySortOptionName($sortingName);
134
135
            // when we have an active sorting in the request we compare the sortingName and mark is as active and
136
            // use the direction from the request
137
            if ($hasSorting && $activeSortingName == $sortingName) {
138
                $selected = true;
139
                $direction = $activeSortingDirection;
140
            }
141
142
            $field = $sortingOptions['field'];
143
            $label = $sortingOptions['label'];
144
145
            $isResetOption = $field === 'relevance';
146
            // @todo allow stdWrap on label
147
            $sorting = new Sorting($resultSet, $sortingName, $field, $direction, $label, $selected, $isResetOption);
148
            $resultSet->addSorting($sorting);
149
        }
150
151
        return $resultSet;
152
    }
153
154
    /**
155
     * @param SearchResultSet $resultSet
156
     * @return SearchResultSet
157
     */
158
    private function parseSpellCheckingResponseIntoObjects(SearchResultSet $resultSet)
159
    {
160
        //read the response
161
        $response = $resultSet->getResponse();
162
        if (!is_object($response->spellcheck->suggestions)) {
163
            return $resultSet;
164
        }
165
166
        foreach ($response->spellcheck->suggestions as $key => $suggestionData) {
167
            if (!isset($suggestionData->suggestion) && !is_array($suggestionData->suggestion)) {
168
                continue;
169
            }
170
171
            // the key contains the misspelled word expect the internal key "collation"
172
            if ($key == 'collation') {
173
                continue;
174
            }
175
            //create the spellchecking object structure
176
            $misspelledTerm = $key;
177
            foreach ($suggestionData->suggestion as $suggestedTerm) {
178
                $suggestion = $this->createSuggestionFromResponseFragment($suggestionData, $suggestedTerm, $misspelledTerm);
179
180
                //add it to the resultSet
181
                $resultSet->addSpellCheckingSuggestion($suggestion);
182
            }
183
        }
184
185
        return $resultSet;
186
    }
187
188
    /**
189
     * @param \stdClass $suggestionData
190
     * @param string $suggestedTerm
191
     * @param string $misspelledTerm
192
     * @return Suggestion
193
     */
194
    private function createSuggestionFromResponseFragment($suggestionData, $suggestedTerm, $misspelledTerm)
195
    {
196
        $numFound = isset($suggestionData->numFound) ? $suggestionData->numFound : 0;
197
        $startOffset = isset($suggestionData->startOffset) ? $suggestionData->startOffset : 0;
198
        $endOffset = isset($suggestionData->endOffset) ? $suggestionData->endOffset : 0;
199
200
        // by now we avoid to use GeneralUtility::makeInstance, since we only create a value object
201
        // and the usage might be a overhead.
202
        $suggestion = new Suggestion($suggestedTerm, $misspelledTerm, $numFound, $startOffset, $endOffset);
203
        return $suggestion;
204
    }
205
206
    /**
207
     * Parse available facets into objects
208
     *
209
     * @param SearchResultSet $resultSet
210
     * @return SearchResultSet
211
     */
212
    private function parseFacetsIntoObjects(SearchResultSet $resultSet)
213
    {
214
        // Make sure we can access the facet configuration
215
        if (!$resultSet->getUsedSearchRequest() || !$resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()) {
216
            return $resultSet;
217
        }
218
219
        // Read the response
220
        $response = $resultSet->getResponse();
221
        if (!is_object($response->facet_counts)) {
222
            return $resultSet;
223
        }
224
225
        /** @var FacetParserRegistry $facetParserRegistry */
226
        $facetParserRegistry = $this->getFacetParserRegistry();
227
        $facetsConfiguration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()->getSearchFacetingFacets();
228
229
        foreach ($facetsConfiguration as $name => $options) {
230
            if (!is_array($options)) {
231
                continue;
232
            }
233
            $facetName = rtrim($name, '.');
234
            $type = !empty($options['type']) ? $options['type'] : '';
235
236
            $parser = $facetParserRegistry->getParser($type);
237
238
            $facet = $parser->parse($resultSet, $facetName, $options);
239
            if ($facet !== null) {
240
                $resultSet->addFacet($facet);
241
            }
242
        }
243
244
        return $resultSet;
245
    }
246
247
    /**
248
     * @param SearchResultSet $resultSet
249
     */
250
    protected function storeLastSearches(SearchResultSet $resultSet)
251
    {
252
        if ($resultSet->getAllResultCount() === 0) {
253
            // when the search does not produce a result we do not store the last searches
254
            return;
255
        }
256
257
        if (!isset($GLOBALS['TSFE']) || !isset($GLOBALS['TYPO3_DB'])) {
258
            return;
259
        }
260
261
        /** @var $lastSearchesService \ApacheSolrForTypo3\Solr\Domain\Search\LastSearches\LastSearchesService */
262
        $lastSearchesService = GeneralUtility::makeInstance(LastSearchesService::class,
263
            $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration(),
264
            $GLOBALS['TSFE'],
265
            $GLOBALS['TYPO3_DB']);
266
267
268
        $lastSearchesService->addToLastSearches($resultSet->getUsedSearchRequest()->getRawUserQuery());
0 ignored issues
show
Documentation introduced by
$resultSet->getUsedSearc...st()->getRawUserQuery() is of type array|null, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
269
    }
270
}
271