Passed
Push — release-11.5.x ( 729324...fafc82 )
by Rafael
38:35
created

getRequirementsService()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace ApacheSolrForTypo3\Solr\Domain\Search\ResultSet;
17
18
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\AbstractFacet;
19
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\FacetRegistry;
20
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\InvalidFacetPackageException;
21
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Facets\RequirementsService;
22
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Sorting\Sorting;
23
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\Spellchecking\Suggestion;
24
use stdClass;
25
use TYPO3\CMS\Core\Utility\GeneralUtility;
26
use TYPO3\CMS\Extbase\Object\ObjectManager;
27
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
28
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
29
use UnexpectedValueException;
30
31
/**
32
 * This processor is used to transform the solr response into a
33
 * domain object hierarchy that can be used in the application (controller and view).
34
 *
35
 * @author Frans Saris <[email protected]>
36
 * @author Timo Hund <[email protected]>
37
 */
38
class ResultSetReconstitutionProcessor implements SearchResultSetProcessor
39
{
40
    /**
41
     * @var ObjectManagerInterface|null
42
     */
43
    protected ?ObjectManagerInterface $objectManager = null;
44
45
    /**
46
     * @return ObjectManagerInterface
47
     */
48 45
    public function getObjectManager(): ?ObjectManagerInterface
49
    {
50 45
        if ($this->objectManager === null) {
51 17
            $this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
52
        }
53 45
        return $this->objectManager;
54
    }
55
56
    /**
57
     * @param ObjectManagerInterface $objectManager
58
     * Purpose: PhpUnit
59
     * @todo: Replace with proper DI
60
     */
61 28
    public function setObjectManager(ObjectManagerInterface $objectManager)
62
    {
63 28
        $this->objectManager = $objectManager;
64
    }
65
66
    /**
67
     * @return FacetRegistry
68
     */
69 45
    protected function getFacetRegistry(): FacetRegistry
70
    {
71
        // @extensionScannerIgnoreLine
72 45
        return $this->getObjectManager()->get(FacetRegistry::class);
0 ignored issues
show
Deprecated Code introduced by
The function TYPO3\CMS\Extbase\Object...ManagerInterface::get() has been deprecated: since TYPO3 10.4, will be removed in version 12.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

72
        return /** @scrutinizer ignore-deprecated */ $this->getObjectManager()->get(FacetRegistry::class);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
73
    }
74
75
    /**
76
     * The implementation can be used to influence a SearchResultSet that is
77
     * created and processed in the SearchResultSetService.
78
     *
79
     * @param SearchResultSet $resultSet
80
     * @return SearchResultSet
81
     * @throws InvalidFacetPackageException
82
     */
83 70
    public function process(SearchResultSet $resultSet): SearchResultSet
84
    {
85 70
        $resultSet = $this->parseSpellCheckingResponseIntoObjects($resultSet);
86 70
        $resultSet = $this->parseSortingIntoObjects($resultSet);
87
88
        // here we can reconstitute other domain objects from the solr response
89 70
        return $this->parseFacetsIntoObjects($resultSet);
90
    }
91
92
    /**
93
     * @param SearchResultSet $resultSet
94
     * @return SearchResultSet
95
     */
96 70
    protected function parseSortingIntoObjects(SearchResultSet $resultSet): SearchResultSet
97
    {
98 70
        $configuration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration();
99 70
        $activeSortings = $resultSet->getUsedSearchRequest()->getSeperatedSortings();
100 70
        $hasSorting = $resultSet->getUsedSearchRequest()->getHasSorting();
101
102
        // no configuration available
103 70
        if (!isset($configuration)) {
104 7
            return $resultSet;
105
        }
106
107
        // no sorting enabled
108 63
        if (!$configuration->getSearchSorting()) {
109 61
            return $resultSet;
110
        }
111 2
        foreach ($configuration->getSearchSortingOptionsConfiguration() as $sortingKeyName => $sortingOptions) {
112 2
            $sortingName = rtrim($sortingKeyName, '.');
113 2
            $selected = false;
114 2
            $direction = $configuration->getSearchSortingDefaultOrderBySortOptionName($sortingName);
115
116
            // when we have an active sorting in the request we compare the sortingName and mark is as active and
117
            // use the direction from the request
118 2
            if ($hasSorting && array_key_exists($sortingName, $activeSortings)) {
119 1
                $selected = true;
120 1
                $direction = $activeSortings[$sortingName];
121
            }
122
123 2
            $field = $sortingOptions['field'];
124 2
            $label = $sortingOptions['label'];
125
126 2
            $isResetOption = $field === 'relevance';
127
128
            // Allow stdWrap on label:
129 2
            $labelHasSubConfiguration = is_array($sortingOptions['label.'] ?? null);
130 2
            if ($labelHasSubConfiguration) {
131
                $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
132
                $label = $cObj->stdWrap($label, $sortingOptions['label.']);
133
            }
134
135 2
            if ($isResetOption && !$hasSorting) {
136 1
                $selected = true;
137
            }
138
139
            /** @noinspection PhpParamsInspection */
140 2
            $sorting = $this->getObjectManager()->get(
0 ignored issues
show
Deprecated Code introduced by
The function TYPO3\CMS\Extbase\Object...ManagerInterface::get() has been deprecated: since TYPO3 10.4, will be removed in version 12.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

140
            $sorting = /** @scrutinizer ignore-deprecated */ $this->getObjectManager()->get(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
141 2
                Sorting::class,
142 2
                $resultSet,
143 2
                $sortingName,
144 2
                $field,
145 2
                $direction,
146 2
                $label,
147 2
                $selected,
148 2
                $isResetOption
149 2
            );
150 2
            $resultSet->addSorting($sorting);
151
        }
152
153 2
        return $resultSet;
154
    }
155
156
    /**
157
     * @param SearchResultSet $resultSet
158
     * @return SearchResultSet
159
     */
160 70
    private function parseSpellCheckingResponseIntoObjects(SearchResultSet $resultSet): SearchResultSet
161
    {
162
        //read the response
163 70
        $response = $resultSet->getResponse();
164
165 70
        if (!is_array($response->spellcheck->suggestions ?? null)) {
166 67
            return $resultSet;
167
        }
168
169 3
        $misspelledTerm = '';
170 3
        foreach ($response->spellcheck->suggestions as $key => $suggestionData) {
171 3
            if (is_string($suggestionData)) {
172 3
                $misspelledTerm = $key;
173 3
                continue;
174
            }
175
176 3
            if ($misspelledTerm === '') {
177
                throw new UnexpectedValueException('No misspelled term before suggestion');
178
            }
179
180 3
            if (!is_object($suggestionData) && !is_array($suggestionData->suggestion)) {
181
                continue;
182
            }
183
184 3
            foreach ($suggestionData->suggestion as $suggestedTerm) {
185 3
                $suggestion = $this->createSuggestionFromResponseFragment($suggestionData, $suggestedTerm, $misspelledTerm);
186
                //add it to the resultSet
187 3
                $resultSet->addSpellCheckingSuggestion($suggestion);
188
            }
189
        }
190
191 3
        return $resultSet;
192
    }
193
194
    /**
195
     * @param stdClass $suggestionData
196
     * @param string $suggestedTerm
197
     * @param string $misspelledTerm
198
     * @return Suggestion
199
     */
200 3
    private function createSuggestionFromResponseFragment(
201
        stdClass $suggestionData,
202
        string $suggestedTerm,
203
        string $misspelledTerm
204
    ): Suggestion {
205 3
        $numFound = $suggestionData->numFound ?? 0;
206 3
        $startOffset = $suggestionData->startOffset ?? 0;
207 3
        $endOffset = $suggestionData->endOffset ?? 0;
208
209
        // by now we avoid to use GeneralUtility::makeInstance, since we only create a value object
210
        // and the usage might be an overhead.
211 3
        return new Suggestion($suggestedTerm, $misspelledTerm, $numFound, $startOffset, $endOffset);
212
    }
213
214
    /**
215
     * Parse available facets into objects
216
     *
217
     * @param SearchResultSet $resultSet
218
     * @return SearchResultSet
219
     * @throws InvalidFacetPackageException
220
     */
221 70
    private function parseFacetsIntoObjects(SearchResultSet $resultSet): SearchResultSet
222
    {
223
        // Make sure we can access the facet configuration
224 70
        if (!$resultSet->getUsedSearchRequest() || !$resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()) {
225 7
            return $resultSet;
226
        }
227
228
        // Read the response
229 63
        $response = $resultSet->getResponse();
230 63
        if (!is_object($response->facet_counts) && !is_object($response->facets)) {
231 18
            return $resultSet;
232
        }
233
234 45
        $facetRegistry = $this->getFacetRegistry();
235 45
        $facetsConfiguration = $resultSet->getUsedSearchRequest()->getContextTypoScriptConfiguration()->getSearchFacetingFacets();
236
237 45
        foreach ($facetsConfiguration as $name => $options) {
238 43
            if (!is_array($options)) {
239
                continue;
240
            }
241 43
            $facetName = rtrim($name, '.');
242 43
            $type = !empty($options['type']) ? $options['type'] : '';
243
244 43
            $parser = $facetRegistry->getPackage($type)->getParser();
245 43
            $facet = $parser->parse($resultSet, $facetName, $options);
246 43
            if ($facet !== null) {
247 43
                $resultSet->addFacet($facet);
248
            }
249
        }
250
251 45
        $this->applyRequirements($resultSet);
252
253 45
        return $resultSet;
254
    }
255
256
    /**
257
     * @param SearchResultSet $resultSet
258
     */
259 45
    protected function applyRequirements(SearchResultSet $resultSet)
260
    {
261 45
        $requirementsService = $this->getRequirementsService();
262 45
        $facets = $resultSet->getFacets();
263 45
        foreach ($facets as $facet) {
264
            /** @var $facet AbstractFacet */
265 43
            $requirementsMet = $requirementsService->getAllRequirementsMet($facet);
266 43
            $facet->setAllRequirementsMet($requirementsMet);
267
        }
268
    }
269
270
    /**
271
     * @return RequirementsService
272
     */
273 45
    protected function getRequirementsService(): RequirementsService
274
    {
275
        // @extensionScannerIgnoreLine
276 45
        return $this->getObjectManager()->get(RequirementsService::class);
0 ignored issues
show
Deprecated Code introduced by
The function TYPO3\CMS\Extbase\Object...ManagerInterface::get() has been deprecated: since TYPO3 10.4, will be removed in version 12.0 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

276
        return /** @scrutinizer ignore-deprecated */ $this->getObjectManager()->get(RequirementsService::class);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
277
    }
278
}
279