Passed
Push — master ( 5adbd6...5901ba )
by Timo
20:55
created

setObjectManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

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