Passed
Push — master ( 4877e0...70b946 )
by Timo
18:59
created

getFacetRegistry()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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