Completed
Pull Request — master (#803)
by Timo
22:04
created

SearchResultSetService::processResponse()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 21
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5.0592

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 13
cts 15
cp 0.8667
rs 8.7624
c 0
b 0
f 0
cc 5
eloc 10
nc 8
nop 3
crap 5.0592
1
<?php
2
3
namespace ApacheSolrForTypo3\Solr\Domain\Search\ResultSet;
4
5
/***************************************************************
6
 *  Copyright notice
7
 *
8
 *  (c) 2015-2016 Timo Schmidt <[email protected]>
9
 *  All rights reserved
10
 *
11
 *  This script is part of the TYPO3 project. The TYPO3 project is
12
 *  free software; you can redistribute it and/or modify
13
 *  it under the terms of the GNU General Public License as published by
14
 *  the Free Software Foundation; either version 2 of the License, or
15
 *  (at your option) any later version.
16
 *
17
 *  The GNU General Public License can be found at
18
 *  http://www.gnu.org/copyleft/gpl.html.
19
 *
20
 *  This script is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  This copyright notice MUST APPEAR in all copies of the script!
26
 ***************************************************************/
27
28
use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest;
29
use ApacheSolrForTypo3\Solr\Plugin\PluginAware;
30
use ApacheSolrForTypo3\Solr\Query;
31
use ApacheSolrForTypo3\Solr\Response\Processor\ResponseProcessor;
32
use ApacheSolrForTypo3\Solr\Search;
33
use ApacheSolrForTypo3\Solr\Search\QueryAware;
34
use ApacheSolrForTypo3\Solr\Search\SearchComponentManager;
35
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
36
use TYPO3\CMS\Core\SingletonInterface;
37
use TYPO3\CMS\Core\Utility\GeneralUtility;
38
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
39
use TYPO3\CMS\Frontend\Plugin\AbstractPlugin;
40
41
/**
42
 * The SearchResultSetService is responsible to build a SearchResultSet from a SearchRequest.
43
 * It encapsulates the logic to trigger a search in order to be able to reuse it in multiple places.
44
 *
45
 * @author Timo Schmidt <[email protected]>
46
 */
47
class SearchResultSetService implements SingletonInterface
48
{
49
    /**
50
     * Additional filters, which will be added to the query, as well as to
51
     * suggest queries.
52
     *
53
     * @var array
54
     */
55
    protected $additionalFilters = array();
56
57
    /**
58
     * Track, if the number of results per page has been changed by the current request
59
     *
60
     * @var bool
61
     */
62
    protected $resultsPerPageChanged = false;
63
64
    /**
65
     * @var \ApacheSolrForTypo3\Solr\Search
66
     */
67
    protected $search;
68
69
    /**
70
     * @var SearchResultSet
71
     */
72
    protected $lastResultSet = null;
73
74
    /**
75
     * @var AbstractPlugin
76
     */
77
    protected $parentPlugin;
78
79
    /**
80
     * @var bool
81
     */
82
    protected $useQueryAwareComponents = true;
83
84
    /**
85
     * @var bool
86
     */
87
    protected $usePluginAwareComponents = true;
88
89
    /**
90
     * @var
91
     */
92
    protected $isSolrAvailable = false;
93
94
    /**
95
     * @var TypoScriptConfiguration
96
     */
97
    protected $typoScriptConfiguration;
98
99
    /**
100
     * @param TypoScriptConfiguration $configuration
101
     * @param Search $search
102
     * @param AbstractPlugin $parentPlugin (optional parent plugin, needed for plugin aware components)
103
     */
104 36
    public function __construct(TypoScriptConfiguration $configuration, Search $search, AbstractPlugin $parentPlugin = null)
105
    {
106 36
        $this->search = $search;
107 36
        $this->typoScriptConfiguration = $configuration;
108 36
        $this->parentPlugin = $parentPlugin;
109 36
    }
110
111
    /**
112
     * @return AbstractPlugin
113
     */
114 1
    public function getParentPlugin()
115
    {
116 1
        return $this->parentPlugin;
117
    }
118
119
    /**
120
     * @param bool $useCache
121
     * @return bool
122
     */
123 25
    public function getIsSolrAvailable($useCache = true)
124
    {
125 25
        $this->isSolrAvailable = $this->search->ping($useCache);
126 25
        return $this->isSolrAvailable;
127
    }
128
129
    /**
130
     * @return bool
131
     */
132 25
    public function getHasSearched()
133
    {
134 25
        return $this->search->hasSearched();
135
    }
136
137
    /**
138
     * Retrieves the used search instance.
139
     *
140
     * @return Search
141
     */
142 25
    public function getSearch()
143
    {
144 25
        return $this->search;
145
    }
146
147
    /**
148
     * @param bool $usePluginAwareComponents
149
     */
150
    public function setUsePluginAwareComponents($usePluginAwareComponents)
151
    {
152
        $this->usePluginAwareComponents = $usePluginAwareComponents;
153
    }
154
155
    /**
156
     * @return bool
157
     */
158
    public function getUsePluginAwareComponents()
159
    {
160
        return $this->usePluginAwareComponents;
161
    }
162
163
    /**
164
     * @param bool $useQueryAwareComponents
165
     */
166
    public function setUseQueryAwareComponents($useQueryAwareComponents)
167
    {
168
        $this->useQueryAwareComponents = $useQueryAwareComponents;
169
    }
170
171
    /**
172
     * @return bool
173
     */
174
    public function getUseQueryAwareComponents()
175
    {
176
        return $this->useQueryAwareComponents;
177
    }
178
179
    /**
180
     * Initializes the Query object and SearchComponents and returns
181
     * the initialized query object, when a search should be executed.
182
     *
183
     * @param string $rawQuery
184
     * @param int $resultsPerPage
185
     * @return Query
186
     */
187 30
    protected function getPreparedQuery($rawQuery, $resultsPerPage)
188
    {
189
        /* @var $query Query */
190 30
        $query = GeneralUtility::makeInstance(Query::class, $rawQuery);
191
192 30
        $this->applyPageSectionsRootLineFilter($query);
193
194 30
        if ($this->typoScriptConfiguration->getLoggingQuerySearchWords()) {
195
            GeneralUtility::devLog('received search query', 'solr', 0, array($rawQuery));
196
        }
197
198 30
        $query->setResultsPerPage($resultsPerPage);
199
200 30
        $this->initializeRegisteredSearchComponents($query);
201
202 30
        if ($this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchQueryAllowEmptyQuery()) {
203
            // empty main query, but using a "return everything"
204
            // alternative query in q.alt
205 23
            $query->setAlternativeQuery('*:*');
206 23
        }
207
208 30
        if ($this->typoScriptConfiguration->getSearchInitializeWithQuery()) {
209 3
            $query->setAlternativeQuery($this->typoScriptConfiguration->getSearchInitializeWithQuery());
210 3
        }
211
212 30
        foreach ($this->getAdditionalFilters() as $additionalFilter) {
213 2
            $query->addFilter($additionalFilter);
214 30
        }
215
216 30
        return $query;
217
    }
218
219
    /**
220
     * @param Query $query
221
     */
222 30
    protected function initializeRegisteredSearchComponents(Query $query)
223
    {
224 30
        $searchComponents = $this->getRegisteredSearchComponents();
225
226 30
        foreach ($searchComponents as $searchComponent) {
227
            /** @var Search\SearchComponent $searchComponent */
228 24
            $searchComponent->setSearchConfiguration($this->typoScriptConfiguration->getSearchConfiguration());
229
230 24
            if ($searchComponent instanceof QueryAware && $this->useQueryAwareComponents) {
231 24
                $searchComponent->setQuery($query);
232 24
            }
233
234 24
            if ($searchComponent instanceof PluginAware && $this->usePluginAwareComponents) {
235
                $searchComponent->setParentPlugin($this->parentPlugin);
236
            }
237
238 24
            $searchComponent->initializeSearchComponent();
239 30
        }
240 30
    }
241
242
    /**
243
     * Returns the number of results per Page.
244
     *
245
     * Also influences how many result documents are returned by the Solr
246
     * server as the return value is used in the Solr "rows" GET parameter.
247
     *
248
     * @param string $rawQuery
249
     * @param int|null $requestedPerPage
250
     * @return int number of results to show per page
251
     */
252 30
    protected function getNumberOfResultsPerPage($rawQuery, $requestedPerPage = null)
253
    {
254 30
        $perPageSwitchOptions = $this->typoScriptConfiguration->getSearchResultsPerPageSwitchOptionsAsArray();
255 30
        if (isset($requestedPerPage) && in_array($requestedPerPage, $perPageSwitchOptions)) {
256 1
            $this->setPerPageInSession($requestedPerPage);
257 1
            $this->resultsPerPageChanged = true;
258 1
        }
259
260 30
        $defaultResultsPerPage = $this->typoScriptConfiguration->getSearchResultsPerPage();
261 30
        $sessionResultPerPage = $this->getPerPageFromSession();
262
263 30
        $currentNumberOfResultsShown = $defaultResultsPerPage;
264 30
        if (!is_null($sessionResultPerPage) && in_array($sessionResultPerPage, $perPageSwitchOptions)) {
265
            $currentNumberOfResultsShown = (int)$sessionResultPerPage;
266
        }
267
268 30
        if ($this->shouldHideResultsFromInitialSearch($rawQuery)) {
269
            // initialize search with an empty query, which would by default return all documents
270
            // anyway, tell Solr to not return any result documents
271
            // Solr will still return facets though
272
            $currentNumberOfResultsShown = 0;
273
        }
274
275 30
        return $currentNumberOfResultsShown;
276
    }
277
278
    /**
279
     * Provides a hook for other classes to process the search's response.
280
     *
281
     * @param string $rawQuery
282
     * @param Query $query The query that has been searched for.
283
     * @param \Apache_Solr_Response $response The search's response.
284
     */
285 31
    protected function processResponse($rawQuery, Query $query, \Apache_Solr_Response &$response)
286
    {
287 31
        if ($this->shouldHideResultsFromInitialSearch($rawQuery)) {
288
            // explicitly set number of results to 0 as we just wanted
289
            // facets and the like according to configuration
290
            // @see getNumberOfResultsPerPage()
291
            $response->response->numFound = 0;
292
        }
293
294 31
        $this->wrapResultDocumentInResultObject($response);
295 31
        $this->addExpandedDocumentsFromVariants($response);
296
297 31
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['processSearchResponse'])) {
298 21
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['processSearchResponse'] as $classReference) {
299 21
                $responseProcessor = GeneralUtility::getUserObj($classReference);
300 21
                if ($responseProcessor instanceof ResponseProcessor) {
301 21
                    $responseProcessor->processResponse($query, $response);
302 21
                }
303 21
            }
304 21
        }
305 31
    }
306
307
    /**
308
     * This method is used to add documents to the expanded documents of the SearchResult
309
     * when collapsing is configured.
310
     *
311
     * @param \Apache_Solr_Response $response
312
     */
313 31
    protected function addExpandedDocumentsFromVariants(\Apache_Solr_Response &$response)
314
    {
315 31
        if (!is_array($response->response->docs)) {
316 5
            return;
317
        }
318
319 26
        if (!$this->typoScriptConfiguration->getSearchVariants()) {
320 24
            return;
321
        }
322
323 2
        $variantsField = $this->typoScriptConfiguration->getSearchVariantsField();
324 2
        foreach ($response->response->docs as $key => $resultDocument) {
325
            /** @var $resultDocument SearchResult */
326 2
            $variantField = $resultDocument->getField($variantsField);
327 2
            $variantId = isset($variantField['value']) ? $variantField['value'] : null;
328
329
                // when there is no value in the collapsing field, we can return
330 2
            if ($variantId === null) {
331
                continue;
332
            }
333
334 2
            $variantAccessKey = strtolower($variantId);
335 2
            if (!isset($response->{'expanded'}) || !isset($response->{'expanded'}->{$variantAccessKey})) {
336
                continue;
337
            }
338
339 2
            foreach ($response->{'expanded'}->{$variantAccessKey}->{'docs'} as $variantDocumentArray) {
340 2
                $variantDocument = new \Apache_Solr_Document();
341 2
                foreach (get_object_vars($variantDocumentArray) as $propertyName => $propertyValue) {
342 2
                    $variantDocument->{$propertyName} = $propertyValue;
343 2
                }
344 2
                $variantSearchResult = $this->wrapApacheSolrDocumentInResultObject($variantDocument);
345 2
                $variantSearchResult->setIsVariant(true);
346 2
                $variantSearchResult->setVariantParent($resultDocument);
347
348 2
                $resultDocument->addVariant($variantSearchResult);
349 2
            }
350 2
        }
351 2
    }
352
353
    /**
354
     * Wrap all results document it a custom EXT:solr SearchResult object.
355
     *
356
     * Can be overwritten:
357
     *
358
     * $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName '] = ''
359
     *
360
     * to use a custom result object.
361
     *
362
     * @param \Apache_Solr_Response $response
363
     * @throws \Apache_Solr_ParserException
364
     */
365 31
    protected function wrapResultDocumentInResultObject(\Apache_Solr_Response &$response)
366
    {
367 31
        try {
368 5
            $documents = $response->response->docs;
369
        } catch (\Apache_Solr_ParserException $e) {
2 ignored issues
show
Unused Code introduced by
catch (\Apache_Solr_Pars...$documents = array(); } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
Bug introduced by
The class Apache_Solr_ParserException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
370
            // when variant are enable and the index is empty, we get a parse exception, because of a
371 26
            // Apache Solr Bug.
372 24
            // see: https://github.com/TYPO3-Solr/ext-solr/issues/668
373 24
            // @todo this try/catch block can be removed after upgrading to Apache Solr 6.4
374 26
            if (!$this->typoScriptConfiguration->getSearchVariants()) {
375 26
                throw $e;
376
            }
377
378
            $response->response = new \stdClass();
379
            $response->spellcheck = [];
380
            $response->debug = [];
381
            $response->responseHeader = [];
382
            $documents = [];
383
        }
384
385 24
        if (!is_array($documents)) {
386
            return;
387 24
        }
388 24
389 24
        foreach ($documents as $key => $originalDocument) {
390
            $result = $this->wrapApacheSolrDocumentInResultObject($originalDocument);
391
            $documents[$key] = $result;
392
        }
393 24
394
        $response->response->docs = $documents;
395
    }
396
397
    /**
398
     * This method is used to wrap the \Apache_Solr_Document instance in an instance of the configured SearchResult
399 24
     * class.
400
     *
401 24
     * @param \Apache_Solr_Document $originalDocument
402 24
     * @throws \InvalidArgumentException
403
     * @return SearchResult
404
     */
405
    protected function wrapApacheSolrDocumentInResultObject(\Apache_Solr_Document $originalDocument)
406
    {
407
        $searchResultClassName = $this->getResultClassName();
408 33
        $result = GeneralUtility::makeInstance($searchResultClassName, $originalDocument);
409
        if (!$result instanceof SearchResult) {
410 33
            throw new \InvalidArgumentException('Could not create result object with class: ' . (string)$searchResultClassName, 1470037679);
411 33
        }
412
413
        return $result;
414
    }
415
416
    /**
417
     * @return string
418
     */
419
    protected function getResultClassName()
420 31
    {
421
        return isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName ']) ?
422 31
            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName '] : SearchResult::class;
423
    }
424
425
    /**
426
     * @return string
427
     */
428
    protected function getResultSetClassName()
429
    {
430
        return isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultSetClassName ']) ?
431
            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultSetClassName '] : SearchResultSet::class;
432 30
    }
433
434 30
    /**
435 30
     * Checks it the results should be hidden in the response.
436 28
     *
437
     * @param string $rawQuery
438
     * @return bool
439
     */
440 2
    protected function shouldHideResultsFromInitialSearch($rawQuery)
441
    {
442
        return ($this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchInitializeWithQuery()) && !$this->typoScriptConfiguration->getSearchShowResultsOfInitialEmptyQuery() && !$this->typoScriptConfiguration->getSearchShowResultsOfInitialQuery() && empty($rawQuery);
443
    }
444 2
445
    /**
446
     * Initializes additional filters configured through TypoScript and
447
     * Flexforms for use in regular queries and suggest queries.
448
     *
449
     * @param Query $query
450
     * @return void
451 35
     */
452
    protected function applyPageSectionsRootLineFilter(Query $query)
453
    {
454 35
        $searchQueryFilters = $this->typoScriptConfiguration->getSearchQueryFilterConfiguration();
455 2
        if (count($searchQueryFilters) <= 0) {
456
            return;
457
        }
458 35
459 35
        // special filter to limit search to specific page tree branches
460 32
        if (array_key_exists('__pageSections', $searchQueryFilters)) {
461
            $query->setRootlineFilter($searchQueryFilters['__pageSections']);
462
            $this->typoScriptConfiguration->removeSearchQueryFilterForPageSections();
463 3
        }
464
    }
465
466 3
    /**
467
     * Retrieves the configuration filters from the TypoScript configuration, except the __pageSections filter.
468 3
     *
469
     * @return array
470
     */
471
    public function getAdditionalFilters()
472 3
    {
473 3
        // when we've build the additionalFilter once, we could return them
474
        if (count($this->additionalFilters) > 0) {
475
            return $this->additionalFilters;
476
        }
477 3
478 3
        $searchQueryFilters = $this->typoScriptConfiguration->getSearchQueryFilterConfiguration();
479
        if (count($searchQueryFilters) <= 0) {
480
            return array();
481
        }
482 3
483 3
        $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
484
485 3
        // all other regular filters
486
        foreach ($searchQueryFilters as $filterKey => $filter) {
487
            // the __pageSections filter should not be handled as additional filter
488
            if ($filterKey === '__pageSections') {
489
                continue;
490
            }
491
492
            $filterIsArray = is_array($searchQueryFilters[$filterKey]);
493
            if ($filterIsArray) {
494 33
                continue;
495
            }
496
497 33
            $hasSubConfiguration = is_array($searchQueryFilters[$filterKey . '.']);
498 33
            if ($hasSubConfiguration) {
499 33
                $filter = $cObj->stdWrap($searchQueryFilters[$filterKey], $searchQueryFilters[$filterKey . '.']);
500 33
            }
501
502 33
            $this->additionalFilters[$filterKey] = $filter;
503
        }
504 33
505
        return $this->additionalFilters;
506 1
    }
507
508
    /**
509 32
     * Performs a search and returns a SearchResultSet.
510
     *
511 2
     * @param SearchRequest $searchRequest
512
     * @return SearchResultSet
513
     */
514 30
    public function search(SearchRequest $searchRequest)
515 30
    {
516 30
        /** @var $resultSet SearchResultSet */
517
        $resultSetClass = $this->getResultSetClassName();
518 30
        $resultSet = GeneralUtility::makeInstance($resultSetClass);
519
        $resultSet->setUsedSearchRequest($searchRequest);
520 30
        $this->lastResultSet = $resultSet;
521
522 30
        $resultSet = $this->handleSearchHook('beforeSearch', $resultSet);
523 1
524 1
        if ($searchRequest->getRawUserQueryIsNull() && !$this->getInitialSearchIsConfigured()) {
525
            // when no rawQuery was passed or no initialSearch is configured, we pass an empty result set
526 30
            return $resultSet;
527
        }
528 30
529
        if ($searchRequest->getRawUserQueryIsEmptyString() && !$this->typoScriptConfiguration->getSearchQueryAllowEmptyQuery()) {
530 30
            // the user entered an empty query string "" or "  " and empty querystring is not allowed
531 30
            return $resultSet;
532
        }
533 30
534 30
        $rawQuery = $searchRequest->getRawUserQuery();
535 30
        $resultsPerPage = $this->getNumberOfResultsPerPage($rawQuery, $searchRequest->getResultsPerPage());
0 ignored issues
show
Documentation introduced by
$rawQuery 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...
Bug introduced by
It seems like $searchRequest->getResultsPerPage() targeting ApacheSolrForTypo3\Solr\...st::getResultsPerPage() can also be of type array; however, ApacheSolrForTypo3\Solr\...umberOfResultsPerPage() does only seem to accept integer|null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
536 30
        $query = $this->getPreparedQuery($rawQuery, $resultsPerPage);
0 ignored issues
show
Documentation introduced by
$rawQuery 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...
537 30
538
        $resultSet->setUsedQuery($query);
539 30
540
        $currentPage = max(0, $searchRequest->getPage());
541
        // if the number of results per page has been changed by the current request, reset the pagebrowser
542
        if ($this->resultsPerPageChanged) {
543
            $currentPage = 0;
544
        }
545
546
        $offSet = $currentPage * $resultsPerPage;
547
        // performing the actual search, sending the query to the Solr server
548 1
        $response = $this->search->search($query, $offSet, null);
549
550
        $this->processResponse($rawQuery, $query, $response);
0 ignored issues
show
Documentation introduced by
$rawQuery 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...
551 1
        $this->addSearchResultsToResultSet($response, $resultSet);
552 1
553
        $resultSet->setResponse($response);
554 1
        $resultSet->setUsedPage($currentPage);
555 1
        $resultSet->setUsedResultsPerPage($resultsPerPage);
556
        $resultSet->setUsedAdditionalFilters($this->getAdditionalFilters());
557 1
        $resultSet->setUsedSearch($this->search);
558 1
559
        return $this->handleSearchHook('afterSearch', $resultSet);
560
    }
561
562
    /**
563
     * Retrieves a single document from solr by document id.
564
     *
565
     * @param string $documentId
566
     * @return SearchResult
567
     */
568 33
    public function getDocumentById($documentId)
569
    {
570 33
        /* @var $query Query */
571 33
        $query = GeneralUtility::makeInstance(Query::class, $documentId);
572
        $query->setQueryFieldsFromString('id');
573
574
        $response = $this->search->search($query, 0, 1);
575
        $this->processResponse($documentId, $query, $response);
576
577
        $resultDocument = isset($response->response->docs[0]) ? $response->response->docs[0] : null;
578
        return $resultDocument;
579
    }
580
581
    /**
582
     * This method is used to call the registered hooks during the search execution.
583
     *
584
     * @param string $eventName
585
     * @param SearchResultSet $resultSet
586
     * @return SearchResultSet
587 19
     */
588
    private function handleSearchHook($eventName, SearchResultSet $resultSet)
589 19
    {
590
        if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr'][$eventName])) {
591
            return $resultSet;
592
        }
593
594
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr'][$eventName] as $classReference) {
595
            $afterSearchProcessor = GeneralUtility::getUserObj($classReference);
596
            if ($afterSearchProcessor instanceof SearchResultSetProcessor) {
597
                $afterSearchProcessor->process($resultSet);
598 25
            }
599
        }
600 25
601 25
        return $resultSet;
602 23
    }
603 23
604
    /**
605 25
     * @return SearchResultSet
606
     */
607
    public function getLastResultSet()
608
    {
609
        return $this->lastResultSet;
610
    }
611
612
    /**
613
     * This method returns true when the last search was executed with an empty query
614
     * string or whitespaces only. When no search was triggered it will return false.
615
     *
616
     * @return bool
617
     */
618
    public function getLastSearchWasExecutedWithEmptyQueryString()
619 23
    {
620
        $wasEmptyQueryString = false;
621 23
        if ($this->lastResultSet != null) {
622
            $wasEmptyQueryString = $this->lastResultSet->getUsedSearchRequest()->getRawUserQueryIsEmptyString();
623
        }
624
625
        return $wasEmptyQueryString;
626
    }
627 2
628
    /**
629 2
     * @param int $requestedPerPage
630
     */
631
    protected function setPerPageInSession($requestedPerPage)
632
    {
633
        $GLOBALS['TSFE']->fe_user->setKey('ses', 'tx_solr_resultsPerPage', intval($requestedPerPage));
634
    }
635 23
636
    /**
637 23
     * @return mixed
638
     */
639
    protected function getPerPageFromSession()
640
    {
641
        return $GLOBALS['TSFE']->fe_user->getKey('ses', 'tx_solr_resultsPerPage');
642
    }
643
644
    /**
645
     * @return bool
646 30
     */
647
    protected function getInitialSearchIsConfigured()
648 30
    {
649 5
        return $this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchShowResultsOfInitialEmptyQuery() || $this->typoScriptConfiguration->getSearchInitializeWithQuery() || $this->typoScriptConfiguration->getSearchShowResultsOfInitialQuery();
650
    }
651
652 25
    /**
653 23
     * @return mixed
654 25
     */
655 25
    protected function getRegisteredSearchComponents()
656
    {
657
        return GeneralUtility::makeInstance(SearchComponentManager::class)->getSearchComponents();
658
    }
659
660
    /**
661
     * This method is used to reference the SearchResult object from the response in the SearchResultSet object.
662
     *
663
     * @param \Apache_Solr_Response $response
664
     * @param SearchResultSet $resultSet
665
     */
666
    protected function addSearchResultsToResultSet($response, $resultSet)
667
    {
668
        if (!is_array($response->response->docs)) {
669
            return;
670
        }
671
672
        foreach ($response->response->docs as $searchResult) {
673
            $resultSet->addSearchResult($searchResult);
674
        }
675
    }
676
}
677