Completed
Push — task/master/1311-combine-facet... ( a56b0a...dd0a56 )
by Timo
21:56
created

ResultSetReconstitutionProcessor   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 235
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 96.84%

Importance

Changes 0
Metric Value
wmc 37
lcom 1
cbo 10
dl 0
loc 235
ccs 92
cts 95
cp 0.9684
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 process() 0 17 2
A parseResultCount() 0 10 2
B parseSortingIntoObjects() 0 39 6
C parseSpellCheckingResponseIntoObjects() 0 29 7
A createSuggestionFromResponseFragment() 0 11 4
A getFacetRegistry() 0 4 1
C parseFacetsIntoObjects() 0 33 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\Facets\FacetRegistry;
19
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Sorting\Sorting;
20
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Spellchecking\Suggestion;
21
use TYPO3\CMS\Core\Utility\GeneralUtility;
22
use TYPO3\CMS\Extbase\Object\ObjectManager;
23
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
24
25
/**
26
 * This processor is used to transform the solr response into a
27
 * domain object hierarchy that can be used in the application (controller and view).
28
 *
29
 * @todo: the logic in this class can go into the SearchResultSetService after moving the
30
 * code of EXT:solrfluid to EXT:solr
31
 *
32
 * @author Frans Saris <[email protected]>
33
 * @author Timo Hund <[email protected]>
34
 * @package ApacheSolrForTypo3\Solr\Domain\Search\ResultSet
35
 */
36
class ResultSetReconstitutionProcessor implements SearchResultSetProcessor
37
{
38
    /**
39
     * @var ObjectManagerInterface
40
     */
41
    protected $objectManager;
42
43
    /**
44
     * @return ObjectManagerInterface
45
     */
46 42
    public function getObjectManager()
47
    {
48 42
        if ($this->objectManager === null) {
49 21
            $this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
50
        }
51 42
        return $this->objectManager;
52
    }
53
54
    /**
55
     * @param ObjectManagerInterface $objectManager
56
     */
57 21
    public function setObjectManager($objectManager)
58
    {
59 21
        $this->objectManager = $objectManager;
60 21
    }
61
62
63
    /**
64
     * @return FacetRegistry
65
     */
66 42
    protected function getFacetRegistry()
67
    {
68 42
        return $this->getObjectManager()->get(FacetRegistry::class);
69
    }
70
71
    /**
72
     * The implementation can be used to influence a SearchResultSet that is
73
     * created and processed in the SearchResultSetService.
74
     *
75
     * @param SearchResultSet $resultSet
76
     * @return SearchResultSet
77
     */
78 54
    public function process(SearchResultSet $resultSet)
79
    {
80 54
        if (!$resultSet instanceof SearchResultSet) {
81
            return $resultSet;
82
        }
83
84 54
        $resultSet = $this->parseResultCount($resultSet);
85 54
        $resultSet = $this->parseSpellCheckingResponseIntoObjects($resultSet);
86 54
        $resultSet = $this->parseSortingIntoObjects($resultSet);
87
88
        // here we can reconstitute other domain objects from the solr response
89 54
        $resultSet = $this->parseFacetsIntoObjects($resultSet);
90
91 54
        $this->storeLastSearches($resultSet);
92
93 54
        return $resultSet;
94
    }
95
96
    /**
97
     * @param SearchResultSet $resultSet
98
     * @return SearchResultSet
99
     */
100 54
    protected function parseResultCount(SearchResultSet $resultSet)
101
    {
102 54
        $response = $resultSet->getResponse();
103 54
        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...
104 5
            return $resultSet;
105
        }
106
107 49
        $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...
108 49
        return $resultSet;
109
    }
110
111
    /**
112
     * @param SearchResultSet $resultSet
113
     * @return SearchResultSet
114
     */
115 54
    protected function parseSortingIntoObjects(SearchResultSet $resultSet)
116
    {
117 54
        $configuration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration();
118 54
        $hasSorting = $resultSet->getUsedSearchRequest()->getHasSorting();
119 54
        $activeSortingName = $resultSet->getUsedSearchRequest()->getSortingName();
120 54
        $activeSortingDirection = $resultSet->getUsedSearchRequest()->getSortingDirection();
121
122
        // no configuration available
123 54
        if (!isset($configuration)) {
124 8
            return $resultSet;
125
        }
126
127
        // no sorting enabled
128 46
        if (!$configuration->getSearchSorting()) {
129 23
            return $resultSet;
130
        }
131 23
        foreach ($configuration->getSearchSortingOptionsConfiguration() as $sortingKeyName => $sortingOptions) {
132 23
            $sortingName = rtrim($sortingKeyName, '.');
133 23
            $selected = false;
134 23
            $direction = $configuration->getSearchSortingDefaultOrderBySortOptionName($sortingName);
135
136
            // when we have an active sorting in the request we compare the sortingName and mark is as active and
137
            // use the direction from the request
138 23
            if ($hasSorting && $activeSortingName == $sortingName) {
139 1
                $selected = true;
140 1
                $direction = $activeSortingDirection;
141
            }
142
143 23
            $field = $sortingOptions['field'];
144 23
            $label = $sortingOptions['label'];
145
146 23
            $isResetOption = $field === 'relevance';
147
            // @todo allow stdWrap on label
148 23
            $sorting = new Sorting($resultSet, $sortingName, $field, $direction, $label, $selected, $isResetOption);
149 23
            $resultSet->addSorting($sorting);
150
        }
151
152 23
        return $resultSet;
153
    }
154
155
    /**
156
     * @param SearchResultSet $resultSet
157
     * @return SearchResultSet
158
     */
159 54
    private function parseSpellCheckingResponseIntoObjects(SearchResultSet $resultSet)
160
    {
161
        //read the response
162 54
        $response = $resultSet->getResponse();
163 54
        if (!is_object($response->spellcheck->suggestions)) {
164 47
            return $resultSet;
165
        }
166
167 7
        foreach ($response->spellcheck->suggestions as $key => $suggestionData) {
168 4
            if (!isset($suggestionData->suggestion) && !is_array($suggestionData->suggestion)) {
169 1
                continue;
170
            }
171
172
            // the key contains the misspelled word expect the internal key "collation"
173 4
            if ($key == 'collation') {
174
                continue;
175
            }
176
            //create the spellchecking object structure
177 4
            $misspelledTerm = $key;
178 4
            foreach ($suggestionData->suggestion as $suggestedTerm) {
179 4
                $suggestion = $this->createSuggestionFromResponseFragment($suggestionData, $suggestedTerm, $misspelledTerm);
180
181
                //add it to the resultSet
182 4
                $resultSet->addSpellCheckingSuggestion($suggestion);
183
            }
184
        }
185
186 7
        return $resultSet;
187
    }
188
189
    /**
190
     * @param \stdClass $suggestionData
191
     * @param string $suggestedTerm
192
     * @param string $misspelledTerm
193
     * @return Suggestion
194
     */
195 4
    private function createSuggestionFromResponseFragment($suggestionData, $suggestedTerm, $misspelledTerm)
196
    {
197 4
        $numFound = isset($suggestionData->numFound) ? $suggestionData->numFound : 0;
198 4
        $startOffset = isset($suggestionData->startOffset) ? $suggestionData->startOffset : 0;
199 4
        $endOffset = isset($suggestionData->endOffset) ? $suggestionData->endOffset : 0;
200
201
        // by now we avoid to use GeneralUtility::makeInstance, since we only create a value object
202
        // and the usage might be a overhead.
203 4
        $suggestion = new Suggestion($suggestedTerm, $misspelledTerm, $numFound, $startOffset, $endOffset);
204 4
        return $suggestion;
205
    }
206
207
    /**
208
     * Parse available facets into objects
209
     *
210
     * @param SearchResultSet $resultSet
211
     * @return SearchResultSet
212
     */
213 54
    private function parseFacetsIntoObjects(SearchResultSet $resultSet)
214
    {
215
        // Make sure we can access the facet configuration
216 54
        if (!$resultSet->getUsedSearchRequest() || !$resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()) {
217 8
            return $resultSet;
218
        }
219
220
        // Read the response
221 46
        $response = $resultSet->getResponse();
222 46
        if (!is_object($response->facet_counts)) {
223 4
            return $resultSet;
224
        }
225
226
        /** @var FacetRegistry $facetRegistry */
227 42
        $facetRegistry = $this->getFacetRegistry();
228 42
        $facetsConfiguration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()->getSearchFacetingFacets();
229
230 42
        foreach ($facetsConfiguration as $name => $options) {
231 40
            if (!is_array($options)) {
232
                continue;
233
            }
234 40
            $facetName = rtrim($name, '.');
235 40
            $type = !empty($options['type']) ? $options['type'] : '';
236
237 40
            $parser = $facetRegistry->getPackage($type)->getParser();
238 40
            $facet = $parser->parse($resultSet, $facetName, $options);
239 40
            if ($facet !== null) {
240 40
                $resultSet->addFacet($facet);
241
            }
242
        }
243
244 42
        return $resultSet;
245
    }
246
247
    /**
248
     * @param SearchResultSet $resultSet
249
     */
250 54
    protected function storeLastSearches(SearchResultSet $resultSet)
251
    {
252 54
        if ($resultSet->getAllResultCount() === 0) {
253
            // when the search does not produce a result we do not store the last searches
254 15
            return;
255
        }
256
257 39
        if (!isset($GLOBALS['TSFE']) || !isset($GLOBALS['TYPO3_DB'])) {
258 15
            return;
259
        }
260
261
        /** @var $lastSearchesService \ApacheSolrForTypo3\Solr\Domain\Search\LastSearches\LastSearchesService */
262 24
        $lastSearchesService = GeneralUtility::makeInstance(LastSearchesService::class,
263 24
            $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration(),
264 24
            $GLOBALS['TSFE'],
265 24
            $GLOBALS['TYPO3_DB']);
266
267
268 24
        $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 24
    }
270
}
271