Failed Conditions
Push — master ( a052b2...ee9c7d )
by Rafael
18:58
created

parseFacetsIntoObjects()   D

Complexity

Conditions 9
Paths 8

Size

Total Lines 35
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 9.0117

Importance

Changes 0
Metric Value
dl 0
loc 35
ccs 18
cts 19
cp 0.9474
rs 4.909
c 0
b 0
f 0
cc 9
eloc 19
nc 8
nop 1
crap 9.0117
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
 * @package ApacheSolrForTypo3\Solr\Domain\Search\ResultSet
33
 */
34
class ResultSetReconstitutionProcessor implements SearchResultSetProcessor
35
{
36
    /**
37
     * @var ObjectManagerInterface
38
     */
39
    protected $objectManager;
40
41
    /**
42
     * @return ObjectManagerInterface
43
     */
44 55
    public function getObjectManager()
45
    {
46 55
        if ($this->objectManager === null) {
47 27
            $this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
48
        }
49 55
        return $this->objectManager;
50
    }
51
52
    /**
53
     * @param ObjectManagerInterface $objectManager
54
     */
55 28
    public function setObjectManager($objectManager)
56
    {
57 28
        $this->objectManager = $objectManager;
58 28
    }
59
60
61
    /**
62
     * @return FacetRegistry
63
     */
64 55
    protected function getFacetRegistry()
65
    {
66 55
        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 66
    public function process(SearchResultSet $resultSet)
77
    {
78 66
        if (!$resultSet instanceof SearchResultSet) {
79
            return $resultSet;
80
        }
81
82 66
        $resultSet = $this->parseResultCount($resultSet);
83 66
        $resultSet = $this->parseSpellCheckingResponseIntoObjects($resultSet);
84 66
        $resultSet = $this->parseSortingIntoObjects($resultSet);
85
86
        // here we can reconstitute other domain objects from the solr response
87 66
        $resultSet = $this->parseFacetsIntoObjects($resultSet);
88
89 66
        return $resultSet;
90
    }
91
92
    /**
93
     * @param SearchResultSet $resultSet
94
     * @return SearchResultSet
95
     */
96 66
    protected function parseResultCount(SearchResultSet $resultSet)
97
    {
98 66
        $response = $resultSet->getResponse();
99 66
        $solrResponse = $response->__get('response');
100 66
        if (!isset($solrResponse->numFound)) {
101 5
            return $resultSet;
102
        }
103 61
        $resultSet->setAllResultCount($solrResponse->numFound);
104 61
        return $resultSet;
105
    }
106
107
    /**
108
     * @param SearchResultSet $resultSet
109
     * @return SearchResultSet
110
     */
111 66
    protected function parseSortingIntoObjects(SearchResultSet $resultSet)
112
    {
113 66
        $configuration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration();
114 66
        $hasSorting = $resultSet->getUsedSearchRequest()->getHasSorting();
115 66
        $activeSortingName = $resultSet->getUsedSearchRequest()->getSortingName();
116 66
        $activeSortingDirection = $resultSet->getUsedSearchRequest()->getSortingDirection();
117
118
        // no configuration available
119 66
        if (!isset($configuration)) {
120 7
            return $resultSet;
121
        }
122
123
        // no sorting enabled
124 59
        if (!$configuration->getSearchSorting()) {
125 30
            return $resultSet;
126
        }
127 29
        foreach ($configuration->getSearchSortingOptionsConfiguration() as $sortingKeyName => $sortingOptions) {
128 29
            $sortingName = rtrim($sortingKeyName, '.');
129 29
            $selected = false;
130 29
            $direction = $configuration->getSearchSortingDefaultOrderBySortOptionName($sortingName);
131
132
            // when we have an active sorting in the request we compare the sortingName and mark is as active and
133
            // use the direction from the request
134 29
            if ($hasSorting && $activeSortingName == $sortingName) {
135 1
                $selected = true;
136 1
                $direction = $activeSortingDirection;
137
            }
138
139 29
            $field = $sortingOptions['field'];
140 29
            $label = $sortingOptions['label'];
141
142 29
            $isResetOption = $field === 'relevance';
143
            // @todo allow stdWrap on label
144 29
            $sorting = new Sorting($resultSet, $sortingName, $field, $direction, $label, $selected, $isResetOption);
145 29
            $resultSet->addSorting($sorting);
146
        }
147
148 29
        return $resultSet;
149
    }
150
151
    /**
152
     * @param SearchResultSet $resultSet
153
     * @return SearchResultSet
154
     */
155 66
    private function parseSpellCheckingResponseIntoObjects(SearchResultSet $resultSet)
156
    {
157
        //read the response
158 66
        $response = $resultSet->getResponse();
159 66
        if (!is_object($response->spellcheck->suggestions)) {
160 58
            return $resultSet;
161
        }
162
163 8
        foreach ($response->spellcheck->suggestions as $key => $suggestionData) {
164 5
            if (!isset($suggestionData->suggestion) && !is_array($suggestionData->suggestion)) {
165 1
                continue;
166
            }
167
168
            // the key contains the misspelled word expect the internal key "collation"
169 5
            if ($key === 'collation') {
170
                continue;
171
            }
172
            //create the spellchecking object structure
173 5
            $misspelledTerm = $key;
174 5
            foreach ($suggestionData->suggestion as $suggestedTerm) {
175 5
                $suggestion = $this->createSuggestionFromResponseFragment($suggestionData, $suggestedTerm, $misspelledTerm);
176
177
                //add it to the resultSet
178 5
                $resultSet->addSpellCheckingSuggestion($suggestion);
179
            }
180
        }
181
182 8
        return $resultSet;
183
    }
184
185
    /**
186
     * @param \stdClass $suggestionData
187
     * @param string $suggestedTerm
188
     * @param string $misspelledTerm
189
     * @return Suggestion
190
     */
191 5
    private function createSuggestionFromResponseFragment($suggestionData, $suggestedTerm, $misspelledTerm)
192
    {
193 5
        $numFound = isset($suggestionData->numFound) ? $suggestionData->numFound : 0;
194 5
        $startOffset = isset($suggestionData->startOffset) ? $suggestionData->startOffset : 0;
195 5
        $endOffset = isset($suggestionData->endOffset) ? $suggestionData->endOffset : 0;
196
197
        // by now we avoid to use GeneralUtility::makeInstance, since we only create a value object
198
        // and the usage might be a overhead.
199 5
        $suggestion = new Suggestion($suggestedTerm, $misspelledTerm, $numFound, $startOffset, $endOffset);
200 5
        return $suggestion;
201
    }
202
203
    /**
204
     * Parse available facets into objects
205
     *
206
     * @param SearchResultSet $resultSet
207
     * @return SearchResultSet
208
     */
209 66
    private function parseFacetsIntoObjects(SearchResultSet $resultSet)
210
    {
211
        // Make sure we can access the facet configuration
212 66
        if (!$resultSet->getUsedSearchRequest() || !$resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()) {
213 7
            return $resultSet;
214
        }
215
216
        // Read the response
217 59
        $response = $resultSet->getResponse();
218 59
        if (!is_object($response->facet_counts) && !is_object($response->facets)) {
219 4
            return $resultSet;
220
        }
221
222
        /** @var FacetRegistry $facetRegistry */
223 55
        $facetRegistry = $this->getFacetRegistry();
224 55
        $facetsConfiguration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()->getSearchFacetingFacets();
225
226 55
        foreach ($facetsConfiguration as $name => $options) {
227 52
            if (!is_array($options)) {
228
                continue;
229
            }
230 52
            $facetName = rtrim($name, '.');
231 52
            $type = !empty($options['type']) ? $options['type'] : '';
232
233 52
            $parser = $facetRegistry->getPackage($type)->getParser();
234 52
            $facet = $parser->parse($resultSet, $facetName, $options);
235 52
            if ($facet !== null) {
236 52
                $resultSet->addFacet($facet);
237
            }
238
        }
239
240 55
        $this->applyRequirements($resultSet);
241
242 55
        return $resultSet;
243
    }
244
245
    /**
246
     * @param SearchResultSet $resultSet
247
     */
248 55
    protected function applyRequirements(SearchResultSet $resultSet)
249
    {
250 55
        $requirementsService = $this->getRequirementsService();
251 55
        $facets = $resultSet->getFacets();
252 55
        foreach ($facets as $facet) {
253
            /** @var $facet AbstractFacet */
254 50
            $requirementsMet = $requirementsService->getAllRequirementsMet($facet);
255 50
            $facet->setAllRequirementsMet($requirementsMet);
256
        }
257 55
    }
258
259
    /**
260
     * @return RequirementsService
261
     */
262 55
    protected function getRequirementsService()
263
    {
264 55
        return $this->getObjectManager()->get(RequirementsService::class);
265
    }
266
}
267