Passed
Push — master ( 9f83de...fb7724 )
by Timo
04:36
created

SearchResultSetService::getPerPageFromSession()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 3
cp 0
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 2
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\Query\ParameterBuilder\QueryFields;
29
use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequest;
30
use ApacheSolrForTypo3\Solr\Domain\Search\SearchRequestAware;
31
use ApacheSolrForTypo3\Solr\Query;
32
use ApacheSolrForTypo3\Solr\Query\Modifier\Modifier;
33
use ApacheSolrForTypo3\Solr\Search;
34
use ApacheSolrForTypo3\Solr\Search\QueryAware;
35
use ApacheSolrForTypo3\Solr\Search\SearchAware;
36
use ApacheSolrForTypo3\Solr\Search\SearchComponentManager;
37
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
38
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
39
use ApacheSolrForTypo3\Solr\System\Session\FrontendUserSession;
40
use TYPO3\CMS\Core\Utility\GeneralUtility;
41
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
42
use Apache_Solr_ParserException;
43
44
/**
45
 * The SearchResultSetService is responsible to build a SearchResultSet from a SearchRequest.
46
 * It encapsulates the logic to trigger a search in order to be able to reuse it in multiple places.
47
 *
48
 * @author Timo Schmidt <[email protected]>
49
 */
50
class SearchResultSetService
51
{
52
    /**
53
     * Additional filters, which will be added to the query, as well as to
54
     * suggest queries.
55
     *
56
     * @var array
57
     */
58
    protected $additionalFilters = [];
59
60
    /**
61
     * Track, if the number of results per page has been changed by the current request
62
     *
63
     * @var bool
64
     */
65
    protected $resultsPerPageChanged = false;
66
67
    /**
68
     * @var Search
69
     */
70
    protected $search;
71
72
    /**
73
     * @var SearchResultSet
74
     */
75
    protected $lastResultSet = null;
76
77
    /**
78
     * @var boolean
79
     */
80
    protected $isSolrAvailable = false;
81
82
    /**
83
     * @var TypoScriptConfiguration
84
     */
85
    protected $typoScriptConfiguration;
86
87
    /**
88
     * @var SolrLogManager;
89
     */
90
    protected $logger = null;
91
92
    /**
93
     * @var FrontendUserSession
94
     */
95
    protected $session = null;
96
97
    /**
98
     * @param TypoScriptConfiguration $configuration
99
     * @param Search $search
100
     * @param SolrLogManager $solrLogManager
101
     * @param FrontendUserSession $frontendUserSession
102
     */
103 46
    public function __construct(TypoScriptConfiguration $configuration, Search $search, SolrLogManager $solrLogManager = null, FrontendUserSession $frontendUserSession = null)
104
    {
105 46
        $this->search = $search;
106 46
        $this->typoScriptConfiguration = $configuration;
107 46
        $this->logger = is_null($solrLogManager) ? GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__) : $solrLogManager;
108 46
        $this->session = is_null($frontendUserSession) ? GeneralUtility::makeInstance(FrontendUserSession::class) : $frontendUserSession;
109
    }
110
111
    /**
112
     * @param bool $useCache
113
     * @return bool
114 30
     */
115
    public function getIsSolrAvailable($useCache = true)
116 30
    {
117 30
        $this->isSolrAvailable = $this->search->ping($useCache);
118
        return $this->isSolrAvailable;
119
    }
120
121
    /**
122
     * @return bool
123 30
     */
124
    public function getHasSearched()
125 30
    {
126
        return $this->search->hasSearched();
127
    }
128
129
    /**
130
     * Retrieves the used search instance.
131
     *
132
     * @return Search
133 2
     */
134
    public function getSearch()
135 2
    {
136
        return $this->search;
137
    }
138
139
    /**
140
     * Initializes the Query object and SearchComponents and returns
141
     * the initialized query object, when a search should be executed.
142
     *
143
     * @param string|null $rawQuery
144
     * @param int $resultsPerPage
145
     * @return Query
146
     */
147
    protected function getPreparedQuery($rawQuery, $resultsPerPage)
148
    {
149
        /* @var $query Query */
150
        $query = $this->getQueryInstance($rawQuery);
151
152
        $this->applyPageSectionsRootLineFilter($query);
153
154
        if ($this->typoScriptConfiguration->getLoggingQuerySearchWords()) {
155
            $this->logger->log(
156
                SolrLogManager::INFO,
157
                'Received search query',
158
                [
159
                    $rawQuery
160
                ]
161
            );
162 38
        }
163
164
        $query->setResultsPerPage($resultsPerPage);
165 38
166
        if ($this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchQueryAllowEmptyQuery()) {
167 38
            // empty main query, but using a "return everything"
168
            // alternative query in q.alt
169 38
            $query->setAlternativeQuery('*:*');
170
        }
171
172
        if ($this->typoScriptConfiguration->getSearchInitializeWithQuery()) {
173
            $query->setAlternativeQuery($this->typoScriptConfiguration->getSearchInitializeWithQuery());
174
        }
175
176
        foreach ($this->getAdditionalFilters() as $additionalFilter) {
177
            $query->getFilters()->add($additionalFilter);
178
        }
179 38
180
        return $query;
181 38
    }
182
183
    /**
184 31
     * @param Query $query
185
     * @param SearchRequest $searchRequest
186
     */
187 38
    protected function initializeRegisteredSearchComponents(Query $query, SearchRequest $searchRequest)
188 3
    {
189
        $searchComponents = $this->getRegisteredSearchComponents();
190
191 38
        foreach ($searchComponents as $searchComponent) {
192 2
            /** @var Search\SearchComponent $searchComponent */
193
            $searchComponent->setSearchConfiguration($this->typoScriptConfiguration->getSearchConfiguration());
194
195 38
            if ($searchComponent instanceof QueryAware) {
196
                $searchComponent->setQuery($query);
197
            }
198
199
            if ($searchComponent instanceof SearchRequestAware) {
200
                $searchComponent->setSearchRequest($searchRequest);
201
            }
202 38
203
            $searchComponent->initializeSearchComponent();
204 38
        }
205
    }
206 38
207
    /**
208 32
     * Returns the number of results per Page.
209
     *
210 32
     * Also influences how many result documents are returned by the Solr
211 32
     * server as the return value is used in the Solr "rows" GET parameter.
212
     *
213
     * @param SearchRequest $searchRequest
214 32
     * @return int number of results to show per page
215 31
     */
216
    protected function getNumberOfResultsPerPage(SearchRequest $searchRequest)
217
    {
218 32
        $requestedPerPage = $searchRequest->getResultsPerPage();
219
        $perPageSwitchOptions = $this->typoScriptConfiguration->getSearchResultsPerPageSwitchOptionsAsArray();
220 38
        if (isset($requestedPerPage) && in_array($requestedPerPage, $perPageSwitchOptions)) {
221
            $this->session->setPerPage($requestedPerPage);
0 ignored issues
show
Documentation introduced by
$requestedPerPage is of type array, but the function expects a integer.

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...
222
            $this->resultsPerPageChanged = true;
223
        }
224
225
        $defaultResultsPerPage = $this->typoScriptConfiguration->getSearchResultsPerPage();
226
        $currentNumberOfResultsShown = $defaultResultsPerPage;
227
        if ($this->session->getHasPerPage()) {
228
            $sessionResultPerPage = $this->session->getPerPage();
229
            if (in_array($sessionResultPerPage, $perPageSwitchOptions)) {
230
                $currentNumberOfResultsShown = (int)$sessionResultPerPage;
231 38
            }
232
        }
233 38
234 38
        if ($this->shouldHideResultsFromInitialSearch($searchRequest)) {
235 38
            // initialize search with an empty query, which would by default return all documents
236 4
            // anyway, tell Solr to not return any result documents
237 4
            // Solr will still return facets though
238
            $currentNumberOfResultsShown = 0;
239
        }
240 38
241 38
        return $currentNumberOfResultsShown;
242
    }
243 38
244 38
    /**
245 3
     * Does post processing of the response.
246
     *
247
     * @param \Apache_Solr_Response $response The search's response.
248 38
     */
249
    protected function processResponse(\Apache_Solr_Response $response)
250
    {
251
        $this->wrapResultDocumentInResultObject($response);
252 2
        $this->addExpandedDocumentsFromVariants($response);
253
    }
254
255 38
    /**
256
     * This method is used to add documents to the expanded documents of the SearchResult
257
     * when collapsing is configured.
258
     *
259
     * @param \Apache_Solr_Response $response
260
     */
261
    protected function addExpandedDocumentsFromVariants(\Apache_Solr_Response $response)
262
    {
263
        if (!is_array($response->response->docs)) {
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...
264 40
            return;
265
        }
266 40
267 40
        if (!$this->typoScriptConfiguration->getSearchVariants()) {
268
            return;
269 40
        }
270 40
271
        $variantsField = $this->typoScriptConfiguration->getSearchVariantsField();
272
        foreach ($response->response->docs as $key => $resultDocument) {
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...
273
            /** @var $resultDocument SearchResult */
274
            $variantField = $resultDocument->getField($variantsField);
275
            $variantId = isset($variantField['value']) ? $variantField['value'] : null;
276
277
                // when there is no value in the collapsing field, we can return
278
            if ($variantId === null) {
279 40
                continue;
280
            }
281 40
282 1
            $variantAccessKey = mb_strtolower($variantId);
283
            if (!isset($response->{'expanded'}) || !isset($response->{'expanded'}->{$variantAccessKey})) {
284 1
                continue;
285 1
            }
286 1
287 1
            foreach ($response->{'expanded'}->{$variantAccessKey}->{'docs'} as $variantDocumentArray) {
288
                $variantDocument = new \Apache_Solr_Document();
289
                foreach (get_object_vars($variantDocumentArray) as $propertyName => $propertyValue) {
290
                    $variantDocument->{$propertyName} = $propertyValue;
291 40
                }
292
                $variantSearchResult = $this->wrapApacheSolrDocumentInResultObject($variantDocument);
293
                $variantSearchResult->setIsVariant(true);
294
                $variantSearchResult->setVariantParent($resultDocument);
295
296
                $resultDocument->addVariant($variantSearchResult);
297
            }
298
        }
299 40
    }
300
301 40
    /**
302 5
     * Wrap all results document it a custom EXT:solr SearchResult object.
303
     *
304
     * Can be overwritten:
305 35
     *
306 32
     * $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName '] = ''
307
     *
308
     * to use a custom result object.
309 3
     *
310 3
     * @param \Apache_Solr_Response $response
311
     * @throws \Apache_Solr_ParserException
312 2
     */
313 2
    protected function wrapResultDocumentInResultObject(\Apache_Solr_Response $response)
314
    {
315
        try {
316 2
            $documents = $response->response->docs;
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...
317
        } catch (Apache_Solr_ParserException $e) {
0 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...
318
            // when variant are enable and the index is empty, we get a parse exception, because of a
319
            // Apache Solr Bug.
320 2
            // see: https://github.com/TYPO3-Solr/ext-solr/issues/668
321 2
            // @todo this try/catch block can be removed after upgrading to Apache Solr 6.4
322
            if (!$this->typoScriptConfiguration->getSearchVariants()) {
323
                throw $e;
324
            }
325 2
326 2
            $response->response = new \stdClass();
327 2
            $response->spellcheck = [];
328 2
            $response->debug = [];
329
            $response->responseHeader = [];
330 2
            $response->facet_counts = [];
331 2
332 2
            $documents = [];
333
        }
334 2
335
        if (!is_array($documents)) {
336
            return;
337 3
        }
338
339
        foreach ($documents as $key => $originalDocument) {
340
            $result = $this->wrapApacheSolrDocumentInResultObject($originalDocument);
341
            $documents[$key] = $result;
342
        }
343
344
        $response->response->docs = $documents;
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...
345
    }
346
347
    /**
348
     * This method is used to wrap the \Apache_Solr_Document instance in an instance of the configured SearchResult
349
     * class.
350
     *
351 40
     * @param \Apache_Solr_Document $originalDocument
352
     * @throws \InvalidArgumentException
353
     * @return SearchResult
354 40
     */
355 1
    protected function wrapApacheSolrDocumentInResultObject(\Apache_Solr_Document $originalDocument)
356
    {
357
        $searchResultClassName = $this->getResultClassName();
358
        $result = GeneralUtility::makeInstance($searchResultClassName, $originalDocument);
359
        if (!$result instanceof SearchResult) {
360 1
            throw new \InvalidArgumentException('Could not create result object with class: ' . (string)$searchResultClassName, 1470037679);
361
        }
362
363
        return $result;
364 1
    }
365 1
366 1
    /**
367 1
     * @return string
368 1
     */
369
    protected function getResultClassName()
370 1
    {
371
        return isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName ']) ?
372
            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName '] : SearchResult::class;
373 40
    }
374 5
375
    /**
376
     * @return string
377 35
     */
378 29
    protected function getResultSetClassName()
379 29
    {
380
        return isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultSetClassName ']) ?
381
            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultSetClassName '] : SearchResultSet::class;
382 35
    }
383 35
384
    /**
385
     * Checks it the results should be hidden in the response.
386
     *
387
     * @param SearchRequest $searchRequest
388
     * @return bool
389
     */
390
    protected function shouldHideResultsFromInitialSearch(SearchRequest $searchRequest)
391
    {
392
        return ($this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchInitializeWithQuery()) && !$this->typoScriptConfiguration->getSearchShowResultsOfInitialEmptyQuery() && !$this->typoScriptConfiguration->getSearchShowResultsOfInitialQuery() && $searchRequest->getRawUserQueryIsNull();
393 29
    }
394
395 29
    /**
396 29
     * Initializes additional filters configured through TypoScript and
397 29
     * Flexforms for use in regular queries and suggest queries.
398
     *
399
     * @param Query $query
400
     * @return void
401 29
     */
402
    protected function applyPageSectionsRootLineFilter(Query $query)
403
    {
404
        $searchQueryFilters = $this->typoScriptConfiguration->getSearchQueryFilterConfiguration();
405
        if (count($searchQueryFilters) <= 0) {
406
            return;
407 29
        }
408
409 29
        // special filter to limit search to specific page tree branches
410 29
        if (array_key_exists('__pageSections', $searchQueryFilters)) {
411
            $query->setRootlineFilter($searchQueryFilters['__pageSections']);
412
            $this->typoScriptConfiguration->removeSearchQueryFilterForPageSections();
413
        }
414
    }
415
416 40
    /**
417
     * Retrieves the configuration filters from the TypoScript configuration, except the __pageSections filter.
418 40
     *
419 40
     * @return array
420
     */
421
    public function getAdditionalFilters()
422
    {
423
        // when we've build the additionalFilter once, we could return them
424
        if (count($this->additionalFilters) > 0) {
425
            return $this->additionalFilters;
426
        }
427
428 38
        $searchQueryFilters = $this->typoScriptConfiguration->getSearchQueryFilterConfiguration();
429
        if (count($searchQueryFilters) <= 0) {
430 38
            return [];
431
        }
432
433
        $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
434
435
        // all other regular filters
436
        foreach ($searchQueryFilters as $filterKey => $filter) {
437
            // the __pageSections filter should not be handled as additional filter
438
            if ($filterKey === '__pageSections') {
439
                continue;
440 38
            }
441
442 38
            $filterIsArray = is_array($searchQueryFilters[$filterKey]);
443 38
            if ($filterIsArray) {
444 36
                continue;
445
            }
446
447
            $hasSubConfiguration = is_array($searchQueryFilters[$filterKey . '.']);
448 2
            if ($hasSubConfiguration) {
449
                $filter = $cObj->stdWrap($searchQueryFilters[$filterKey], $searchQueryFilters[$filterKey . '.']);
450
            }
451
452 2
            $this->additionalFilters[$filterKey] = $filter;
453
        }
454
455
        return $this->additionalFilters;
456
    }
457
458
    /**
459 43
     * Performs a search and returns a SearchResultSet.
460
     *
461
     * @param SearchRequest $searchRequest
462 43
     * @return SearchResultSet
463 2
     */
464
    public function search(SearchRequest $searchRequest)
465
    {
466 43
        /** @var $resultSet SearchResultSet */
467 43
        $resultSetClass = $this->getResultSetClassName();
468 41
        $resultSet = GeneralUtility::makeInstance($resultSetClass);
469
        $resultSet->setUsedSearchRequest($searchRequest);
470
        $this->lastResultSet = $resultSet;
471 2
472
        $resultSet = $this->handleSearchHook('beforeSearch', $resultSet);
473
474 2
        if ($searchRequest->getRawUserQueryIsNull() && !$this->getInitialSearchIsConfigured()) {
475
            // when no rawQuery was passed or no initialSearch is configured, we pass an empty result set
476 2
            return $resultSet;
477
        }
478
479
        if ($searchRequest->getRawUserQueryIsEmptyString() && !$this->typoScriptConfiguration->getSearchQueryAllowEmptyQuery()) {
480 2
            // the user entered an empty query string "" or "  " and empty querystring is not allowed
481 2
            return $resultSet;
482
        }
483
484
        $rawQuery = $searchRequest->getRawUserQuery();
485 2
        $resultsPerPage = $this->getNumberOfResultsPerPage($searchRequest);
486 2
        $query = $this->getPreparedQuery($rawQuery, $resultsPerPage);
487
        $this->initializeRegisteredSearchComponents($query, $searchRequest);
488
        $resultSet->setUsedQuery($query);
489
490 2
        $currentPage = max(0, $searchRequest->getPage());
491
        // if the number of results per page has been changed by the current request, reset the pagebrowser
492
        if ($this->resultsPerPageChanged) {
493 2
            $currentPage = 0;
494
        }
495
496
        $offSet = $currentPage * $resultsPerPage;
497
498
        // performing the actual search, sending the query to the Solr server
499
        $query = $this->modifyQuery($query, $searchRequest, $this->search);
500
        $response = $this->search->search($query, $offSet, null);
501
502 40
        if ($resultsPerPage === 0) {
503
            // when resultPerPage was forced to 0 we also set the numFound to 0 to hide results, e.g.
504
            // when results for the initial search should not be shown.
505 40
            $response->response->numFound = 0;
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...
506 40
        }
507 40
508 40
        $this->processResponse($response);
509
510 40
        $this->addSearchResultsToResultSet($response, $resultSet);
511
512 40
        $resultSet->setResponse($response);
513
        $resultSet->setUsedPage($currentPage);
514 2
        $resultSet->setUsedResultsPerPage($resultsPerPage);
515
        $resultSet->setUsedAdditionalFilters($this->getAdditionalFilters());
516
        $resultSet->setUsedSearch($this->search);
517 38
518
        /** @var $searchResultReconstitutionProcessor ResultSetReconstitutionProcessor */
519
        $searchResultReconstitutionProcessor = GeneralUtility::makeInstance(ResultSetReconstitutionProcessor::class);
520
        $searchResultReconstitutionProcessor->process($resultSet);
521
522 38
        $resultSet = $this->getAutoCorrection($resultSet);
523 38
524 38
        return $this->handleSearchHook('afterSearch', $resultSet);
525 38
    }
526 38
527
    /**
528 38
     * @param SearchResultSet $searchResultSet
529
     * @return SearchResultSet
530 38
     */
531 4
    protected function getAutoCorrection(SearchResultSet $searchResultSet)
532
    {
533
        // no secondary search configured
534 38
        if (!$this->typoScriptConfiguration->getSearchSpellcheckingSearchUsingSpellCheckerSuggestion()) {
535
            return $searchResultSet;
536
        }
537 38
538 38
        // more then zero results
539 38
        if ($searchResultSet->getAllResultCount() > 0) {
540
            return $searchResultSet;
541 38
        }
542
543
        // no corrections present
544 2
        if (!$searchResultSet->getHasSpellCheckingSuggestions()) {
545
            return $searchResultSet;
546
        }
547 38
548
        $searchResultSet = $this->peformAutoCorrection($searchResultSet);
549 38
550
        return $searchResultSet;
551 38
    }
552 38
553 38
    /**
554 38
     * @param SearchResultSet $searchResultSet
555 38
     * @return SearchResultSet
556
     */
557
    protected function peformAutoCorrection(SearchResultSet $searchResultSet)
558 38
    {
559 38
        $searchRequest = $searchResultSet->getUsedSearchRequest();
560
        $suggestions = $searchResultSet->getSpellCheckingSuggestions();
561 38
562
        $maximumRuns = $this->typoScriptConfiguration->getSearchSpellcheckingNumberOfSuggestionsToTry(1);
563 38
        $runs = 0;
564
565
        foreach ($suggestions as $suggestion) {
566
            $runs++;
567
568
            $correction = $suggestion->getSuggestion();
569
            $initialQuery = $searchRequest->getRawUserQuery();
570 38
571
            $searchRequest->setRawQueryString($correction);
572
            $searchResultSet = $this->search($searchRequest);
573 38
            if ($searchResultSet->getAllResultCount() > 0) {
574 37
                $searchResultSet->setIsAutoCorrected(true);
575
                $searchResultSet->setCorrectedQueryString($correction);
576
                $searchResultSet->setInitialQueryString($initialQuery);
577
                break;
578 1
            }
579 1
580
            if ($runs > $maximumRuns) {
581
                break;
582
            }
583 1
        }
584
        return $searchResultSet;
585
    }
586
587 1
    /**
588
     * Allows to modify a query before eventually handing it over to Solr.
589 1
     *
590
     * @param Query $query The current query before it's being handed over to Solr.
591
     * @param SearchRequest $searchRequest The searchRequest, relevant in the current context
592
     * @param Search $search The search, relevant in the current context
593
     * @throws \UnexpectedValueException
594
     * @return Query The modified query that is actually going to be given to Solr.
595
     */
596 1
    protected function modifyQuery(Query $query, SearchRequest $searchRequest, Search $search)
597
    {
598 1
        // hook to modify the search query
599 1
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifySearchQuery'])) {
600
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifySearchQuery'] as $classReference) {
601 1
                $queryModifier = GeneralUtility::getUserObj($classReference);
602 1
603
                if ($queryModifier instanceof Modifier) {
604 1
                    if ($queryModifier instanceof SearchAware) {
605 1
                        $queryModifier->setSearch($search);
606
                    }
607 1
608 1
                    if ($queryModifier instanceof SearchRequestAware) {
609
                        $queryModifier->setSearchRequest($searchRequest);
610 1
                    }
611 1
612 1
                    $query = $queryModifier->modifyQuery($query);
613 1
                } else {
614 1
                    throw new \UnexpectedValueException(
615 1
                        get_class($queryModifier) . ' must implement interface ' . Modifier::class,
616 1
                        1310387414
617
                    );
618
                }
619
            }
620
        }
621
622
        return $query;
623 1
    }
624
625
    /**
626
     * Retrieves a single document from solr by document id.
627
     *
628
     * @param string $documentId
629
     * @return SearchResult
630
     */
631
    public function getDocumentById($documentId)
632
    {
633
        /* @var $query Query */
634
        $query = GeneralUtility::makeInstance(Query::class, $documentId);
635 38
        $query->setQueryFields(QueryFields::fromString('id'));
636
637
        $response = $this->search->search($query, 0, 1);
638 38
        $this->processResponse($response);
639 31
640 31
        $resultDocument = isset($response->response->docs[0]) ? $response->response->docs[0] : null;
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...
641
        return $resultDocument;
642 31
    }
643 31
644
    /**
645
     * This method is used to call the registered hooks during the search execution.
646
     *
647 31
     * @param string $eventName
648 31
     * @param SearchResultSet $resultSet
649
     * @return SearchResultSet
650
     */
651 31
    private function handleSearchHook($eventName, SearchResultSet $resultSet)
652
    {
653
        if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr'][$eventName])) {
654
            return $resultSet;
655 31
        }
656
657
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr'][$eventName] as $classReference) {
658
            $afterSearchProcessor = GeneralUtility::getUserObj($classReference);
659
            if ($afterSearchProcessor instanceof SearchResultSetProcessor) {
660
                $afterSearchProcessor->process($resultSet);
661 38
            }
662
        }
663
664
        return $resultSet;
665
    }
666
667
    /**
668
     * @return SearchResultSet
669
     */
670
    public function getLastResultSet()
671
    {
672
        return $this->lastResultSet;
673
    }
674
675 38
    /**
676
     * This method returns true when the last search was executed with an empty query
677
     * string or whitespaces only. When no search was triggered it will return false.
678 38
     *
679
     * @return bool
680
     */
681
    public function getLastSearchWasExecutedWithEmptyQueryString()
682
    {
683
        $wasEmptyQueryString = false;
684
        if ($this->lastResultSet != null) {
685
            $wasEmptyQueryString = $this->lastResultSet->getUsedSearchRequest()->getRawUserQueryIsEmptyString();
686
        }
687
688
        return $wasEmptyQueryString;
689
    }
690
691
    /**
692
     * @return bool
693
     */
694
    protected function getInitialSearchIsConfigured()
695
    {
696
        return $this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchShowResultsOfInitialEmptyQuery() || $this->typoScriptConfiguration->getSearchInitializeWithQuery() || $this->typoScriptConfiguration->getSearchShowResultsOfInitialQuery();
697
    }
698
699
    /**
700
     * @return mixed
701
     */
702
    protected function getRegisteredSearchComponents()
703
    {
704 38
        return GeneralUtility::makeInstance(SearchComponentManager::class)->getSearchComponents();
705
    }
706
707
    /**
708
     * This method is used to reference the SearchResult object from the response in the SearchResultSet object.
709
     *
710
     * @param \Apache_Solr_Response $response
711
     * @param SearchResultSet $resultSet
712 2
     */
713
    protected function addSearchResultsToResultSet($response, $resultSet)
714
    {
715 2
        if (!is_array($response->response->docs)) {
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...
716 2
            return;
717
        }
718 2
719 2
        foreach ($response->response->docs as $searchResult) {
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...
720
            $resultSet->addSearchResult($searchResult);
721 2
        }
722 2
    }
723
724
    /**
725
     * @param string $rawQuery
726
     * @return Query|object
727
     */
728
    protected function getQueryInstance($rawQuery)
729
    {
730
        $query = GeneralUtility::makeInstance(Query::class, $rawQuery, $this->typoScriptConfiguration);
731
        return $query;
732 40
    }
733
}
734