Passed
Push — master ( 0ba0e3...644d9b )
by Timo
19:06
created

SearchResultSetService::modifyResponse()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 31
ccs 7
cts 7
cp 1
rs 8.439
c 0
b 0
f 0
cc 6
eloc 17
nc 3
nop 3
crap 6
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\Domain\Search\SearchRequestAware;
30
use ApacheSolrForTypo3\Solr\Query;
31
use ApacheSolrForTypo3\Solr\Query\Modifier\Modifier;
32
use ApacheSolrForTypo3\Solr\Response\Processor\ResponseProcessor;
33
use ApacheSolrForTypo3\Solr\Search;
34
use ApacheSolrForTypo3\Solr\Search\QueryAware;
35
use ApacheSolrForTypo3\Solr\Search\ResponseModifier;
36
use ApacheSolrForTypo3\Solr\Search\SearchAware;
37
use ApacheSolrForTypo3\Solr\Search\SearchComponentManager;
38
use ApacheSolrForTypo3\Solr\System\Configuration\TypoScriptConfiguration;
39
use ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
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 \ApacheSolrForTypo3\Solr\Search
69
     */
70
    protected $search;
71
72
    /**
73
     * @var SearchResultSet
74
     */
75
    protected $lastResultSet = null;
76
77
    /**
78
     * @var bool
79
     */
80
    protected $useQueryAwareComponents = true;
81
82
    /**
83
     * @var
84
     */
85
    protected $isSolrAvailable = false;
86
87
    /**
88
     * @var TypoScriptConfiguration
89
     */
90
    protected $typoScriptConfiguration;
91
92
    /**
93
     * @var \ApacheSolrForTypo3\Solr\System\Logging\SolrLogManager;
94
     */
95
    protected $logger = null;
96
97 41
    /**
98
     * @param TypoScriptConfiguration $configuration
99 41
     * @param Search $search
100 41
     */
101 41
    public function __construct(TypoScriptConfiguration $configuration, Search $search)
102 41
    {
103
        $this->logger = GeneralUtility::makeInstance(SolrLogManager::class, __CLASS__);
104
        $this->search = $search;
105
        $this->typoScriptConfiguration = $configuration;
106
    }
107
108 25
    /**
109
     * @param bool $useCache
110 25
     * @return bool
111 25
     */
112
    public function getIsSolrAvailable($useCache = true)
113
    {
114
        $this->isSolrAvailable = $this->search->ping($useCache);
115
        return $this->isSolrAvailable;
116
    }
117 25
118
    /**
119 25
     * @return bool
120
     */
121
    public function getHasSearched()
122
    {
123
        return $this->search->hasSearched();
124
    }
125
126
    /**
127 2
     * Retrieves the used search instance.
128
     *
129 2
     * @return Search
130
     */
131
    public function getSearch()
132
    {
133
        return $this->search;
134
    }
135
136
    /**
137
     * @param bool $useQueryAwareComponents
138
     */
139
    public function setUseQueryAwareComponents($useQueryAwareComponents)
140
    {
141
        $this->useQueryAwareComponents = $useQueryAwareComponents;
142
    }
143
144
    /**
145
     * @return bool
146
     */
147
    public function getUseQueryAwareComponents()
148
    {
149
        return $this->useQueryAwareComponents;
150
    }
151
152
    /**
153
     * Initializes the Query object and SearchComponents and returns
154
     * the initialized query object, when a search should be executed.
155
     *
156 33
     * @param string $rawQuery
157
     * @param int $resultsPerPage
158
     * @return Query
159 33
     */
160
    protected function getPreparedQuery($rawQuery, $resultsPerPage)
161 33
    {
162
        /* @var $query Query */
163 33
        $query = GeneralUtility::makeInstance(Query::class, $rawQuery);
164
165
        $this->applyPageSectionsRootLineFilter($query);
166
167
        if ($this->typoScriptConfiguration->getLoggingQuerySearchWords()) {
168
            $this->logger->log(
169
                SolrLogManager::INFO,
170
                'Received search query',
171
                [
172
                    $rawQuery
173 33
                ]
174
            );
175 33
        }
176
177 33
        $query->setResultsPerPage($resultsPerPage);
178
179
        if ($this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchQueryAllowEmptyQuery()) {
180 26
            // empty main query, but using a "return everything"
181
            // alternative query in q.alt
182
            $query->setAlternativeQuery('*:*');
183 33
        }
184 1
185
        if ($this->typoScriptConfiguration->getSearchInitializeWithQuery()) {
186
            $query->setAlternativeQuery($this->typoScriptConfiguration->getSearchInitializeWithQuery());
187 33
        }
188 2
189
        foreach ($this->getAdditionalFilters() as $additionalFilter) {
190
            $query->addFilter($additionalFilter);
191 33
        }
192
193
        return $query;
194
    }
195
196
    /**
197 33
     * @param Query $query
198
     * @param SearchRequest $searchRequest
199 33
     */
200
    protected function initializeRegisteredSearchComponents(Query $query, SearchRequest $searchRequest)
201 33
    {
202
        $searchComponents = $this->getRegisteredSearchComponents();
203 27
204
        foreach ($searchComponents as $searchComponent) {
205 27
            /** @var Search\SearchComponent $searchComponent */
206 27
            $searchComponent->setSearchConfiguration($this->typoScriptConfiguration->getSearchConfiguration());
207
208
            if ($searchComponent instanceof QueryAware && $this->useQueryAwareComponents) {
209 27
                $searchComponent->setQuery($query);
210
            }
211 33
212
            if ($searchComponent instanceof SearchRequestAware) {
213
                $searchComponent->setSearchRequest($searchRequest);
214
            }
215
216
            $searchComponent->initializeSearchComponent();
217
        }
218
    }
219
220
    /**
221
     * Returns the number of results per Page.
222
     *
223 33
     * Also influences how many result documents are returned by the Solr
224
     * server as the return value is used in the Solr "rows" GET parameter.
225 33
     *
226 33
     * @param string $rawQuery
227 4
     * @param int|null $requestedPerPage
228 4
     * @return int number of results to show per page
229
     */
230
    protected function getNumberOfResultsPerPage($rawQuery, $requestedPerPage = null)
231 33
    {
232 33
        $perPageSwitchOptions = $this->typoScriptConfiguration->getSearchResultsPerPageSwitchOptionsAsArray();
233
        if (isset($requestedPerPage) && in_array($requestedPerPage, $perPageSwitchOptions)) {
234 33
            $this->setPerPageInSession($requestedPerPage);
235 33
            $this->resultsPerPageChanged = true;
236 3
        }
237
238
        $defaultResultsPerPage = $this->typoScriptConfiguration->getSearchResultsPerPage();
239 33
        $sessionResultPerPage = $this->getPerPageFromSession();
240
241
        $currentNumberOfResultsShown = $defaultResultsPerPage;
242
        if (!is_null($sessionResultPerPage) && in_array($sessionResultPerPage, $perPageSwitchOptions)) {
243
            $currentNumberOfResultsShown = (int)$sessionResultPerPage;
244
        }
245
246 33
        if ($this->shouldHideResultsFromInitialSearch($rawQuery)) {
247
            // initialize search with an empty query, which would by default return all documents
248
            // anyway, tell Solr to not return any result documents
249
            // Solr will still return facets though
250
            $currentNumberOfResultsShown = 0;
251
        }
252
253
        return $currentNumberOfResultsShown;
254
    }
255
256 35
    /**
257
     * Provides a hook for other classes to process the search's response.
258 35
     *
259
     * @param string $rawQuery
260
     * @param Query $query The query that has been searched for.
261
     * @param \Apache_Solr_Response $response The search's response.
262
     */
263
    protected function processResponse($rawQuery, Query $query, \Apache_Solr_Response $response)
264
    {
265 35
        if ($this->shouldHideResultsFromInitialSearch($rawQuery)) {
266 35
            // explicitly set number of results to 0 as we just wanted
267
            // facets and the like according to configuration
268 35
            // @see getNumberOfResultsPerPage()
269 23
            $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...
270 23
        }
271 23
272 23
        $this->wrapResultDocumentInResultObject($response);
273
        $this->addExpandedDocumentsFromVariants($response);
274
275
        $this->applySearchResponseProcessors($query, $response);
0 ignored issues
show
Deprecated Code introduced by
The method ApacheSolrForTypo3\Solr\...rchResponseProcessors() has been deprecated with message: Please use $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['afterSearch'] now to register your SearchResultSetProcessor will be removed in 8.0

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

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

Loading history...
276 35
    }
277
278
    /**
279
     * Executes the register searchResponse processors.
280
     *
281
     * @deprecated Please use $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['afterSearch'] now to register your SearchResultSetProcessor will be removed in 8.0
282
     * @param Query $query
283
     * @param \Apache_Solr_Response $response
284 35
     */
285
    private function applySearchResponseProcessors(Query $query, \Apache_Solr_Response $response)
286 35
    {
287 5
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['processSearchResponse'])) {
288
            GeneralUtility::logDeprecatedFunction();
289
290 30
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['processSearchResponse'] as $classReference) {
291 27
                $responseProcessor = GeneralUtility::getUserObj($classReference);
292
                if ($responseProcessor instanceof ResponseProcessor) {
293
                    $responseProcessor->processResponse($query, $response);
294 3
                }
295 3
            }
296
        }
297 2
    }
298 2
299
    /**
300
     * This method is used to add documents to the expanded documents of the SearchResult
301 2
     * when collapsing is configured.
302
     *
303
     * @param \Apache_Solr_Response $response
304
     */
305 2
    protected function addExpandedDocumentsFromVariants(\Apache_Solr_Response $response)
306 2
    {
307
        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...
308
            return;
309
        }
310 2
311 2
        if (!$this->typoScriptConfiguration->getSearchVariants()) {
312 2
            return;
313 2
        }
314
315 2
        $variantsField = $this->typoScriptConfiguration->getSearchVariantsField();
316 2
        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...
317 2
            /** @var $resultDocument SearchResult */
318
            $variantField = $resultDocument->getField($variantsField);
319 2
            $variantId = isset($variantField['value']) ? $variantField['value'] : null;
320
321
                // when there is no value in the collapsing field, we can return
322 3
            if ($variantId === null) {
323
                continue;
324
            }
325
326
            $variantAccessKey = strtolower($variantId);
327
            if (!isset($response->{'expanded'}) || !isset($response->{'expanded'}->{$variantAccessKey})) {
328
                continue;
329
            }
330
331
            foreach ($response->{'expanded'}->{$variantAccessKey}->{'docs'} as $variantDocumentArray) {
332
                $variantDocument = new \Apache_Solr_Document();
333
                foreach (get_object_vars($variantDocumentArray) as $propertyName => $propertyValue) {
334
                    $variantDocument->{$propertyName} = $propertyValue;
335
                }
336 35
                $variantSearchResult = $this->wrapApacheSolrDocumentInResultObject($variantDocument);
337
                $variantSearchResult->setIsVariant(true);
338
                $variantSearchResult->setVariantParent($resultDocument);
339 35
340 1
                $resultDocument->addVariant($variantSearchResult);
341
            }
342
        }
343
    }
344
345 1
    /**
346
     * Wrap all results document it a custom EXT:solr SearchResult object.
347
     *
348
     * Can be overwritten:
349 1
     *
350 1
     * $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName '] = ''
351 1
     *
352 1
     * to use a custom result object.
353 1
     *
354
     * @param \Apache_Solr_Response $response
355 1
     * @throws \Apache_Solr_ParserException
356
     */
357
    protected function wrapResultDocumentInResultObject(\Apache_Solr_Response $response)
358 35
    {
359 5
        try {
360
            $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...
361
        } 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...
362 30
            // when variant are enable and the index is empty, we get a parse exception, because of a
363 27
            // Apache Solr Bug.
364 27
            // see: https://github.com/TYPO3-Solr/ext-solr/issues/668
365
            // @todo this try/catch block can be removed after upgrading to Apache Solr 6.4
366
            if (!$this->typoScriptConfiguration->getSearchVariants()) {
367 30
                throw $e;
368 30
            }
369
370
            $response->response = new \stdClass();
371
            $response->spellcheck = [];
372
            $response->debug = [];
373
            $response->responseHeader = [];
374
            $response->facet_counts = [];
375
376
            $documents = [];
377
        }
378 27
379
        if (!is_array($documents)) {
380 27
            return;
381 27
        }
382 27
383
        foreach ($documents as $key => $originalDocument) {
384
            $result = $this->wrapApacheSolrDocumentInResultObject($originalDocument);
385
            $documents[$key] = $result;
386 27
        }
387
388
        $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...
389
    }
390
391
    /**
392 27
     * This method is used to wrap the \Apache_Solr_Document instance in an instance of the configured SearchResult
393
     * class.
394 27
     *
395 27
     * @param \Apache_Solr_Document $originalDocument
396
     * @throws \InvalidArgumentException
397
     * @return SearchResult
398
     */
399
    protected function wrapApacheSolrDocumentInResultObject(\Apache_Solr_Document $originalDocument)
400
    {
401 35
        $searchResultClassName = $this->getResultClassName();
402
        $result = GeneralUtility::makeInstance($searchResultClassName, $originalDocument);
403 35
        if (!$result instanceof SearchResult) {
404 35
            throw new \InvalidArgumentException('Could not create result object with class: ' . (string)$searchResultClassName, 1470037679);
405
        }
406
407
        return $result;
408
    }
409
410
    /**
411
     * @return string
412
     */
413 35
    protected function getResultClassName()
414
    {
415 35
        return isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName ']) ?
416
            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultClassName '] : SearchResult::class;
417
    }
418
419
    /**
420
     * @return string
421
     */
422
    protected function getResultSetClassName()
423
    {
424
        return isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultSetClassName ']) ?
425 33
            $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['searchResultSetClassName '] : SearchResultSet::class;
426
    }
427 33
428 33
    /**
429 31
     * Checks it the results should be hidden in the response.
430
     *
431
     * @param string $rawQuery
432
     * @return bool
433 2
     */
434
    protected function shouldHideResultsFromInitialSearch($rawQuery)
435
    {
436
        return ($this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchInitializeWithQuery()) && !$this->typoScriptConfiguration->getSearchShowResultsOfInitialEmptyQuery() && !$this->typoScriptConfiguration->getSearchShowResultsOfInitialQuery() && $rawQuery === null;
437 2
    }
438
439
    /**
440
     * Initializes additional filters configured through TypoScript and
441
     * Flexforms for use in regular queries and suggest queries.
442
     *
443
     * @param Query $query
444 38
     * @return void
445
     */
446
    protected function applyPageSectionsRootLineFilter(Query $query)
447 38
    {
448 2
        $searchQueryFilters = $this->typoScriptConfiguration->getSearchQueryFilterConfiguration();
449
        if (count($searchQueryFilters) <= 0) {
450
            return;
451 38
        }
452 38
453 36
        // special filter to limit search to specific page tree branches
454
        if (array_key_exists('__pageSections', $searchQueryFilters)) {
455
            $query->setRootlineFilter($searchQueryFilters['__pageSections']);
456 2
            $this->typoScriptConfiguration->removeSearchQueryFilterForPageSections();
457
        }
458
    }
459 2
460
    /**
461 2
     * Retrieves the configuration filters from the TypoScript configuration, except the __pageSections filter.
462
     *
463
     * @return array
464
     */
465 2
    public function getAdditionalFilters()
466 2
    {
467
        // when we've build the additionalFilter once, we could return them
468
        if (count($this->additionalFilters) > 0) {
469
            return $this->additionalFilters;
470 2
        }
471 2
472
        $searchQueryFilters = $this->typoScriptConfiguration->getSearchQueryFilterConfiguration();
473
        if (count($searchQueryFilters) <= 0) {
474
            return [];
475 2
        }
476
477
        $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
478 2
479
        // all other regular filters
480
        foreach ($searchQueryFilters as $filterKey => $filter) {
481
            // the __pageSections filter should not be handled as additional filter
482
            if ($filterKey === '__pageSections') {
483
                continue;
484
            }
485
486
            $filterIsArray = is_array($searchQueryFilters[$filterKey]);
487 35
            if ($filterIsArray) {
488
                continue;
489
            }
490 35
491 35
            $hasSubConfiguration = is_array($searchQueryFilters[$filterKey . '.']);
492 35
            if ($hasSubConfiguration) {
493 35
                $filter = $cObj->stdWrap($searchQueryFilters[$filterKey], $searchQueryFilters[$filterKey . '.']);
494
            }
495 35
496
            $this->additionalFilters[$filterKey] = $filter;
497 35
        }
498
499 2
        return $this->additionalFilters;
500
    }
501
502 33
    /**
503
     * Performs a search and returns a SearchResultSet.
504
     *
505
     * @param SearchRequest $searchRequest
506
     * @return SearchResultSet
507 33
     */
508 33
    public function search(SearchRequest $searchRequest)
509 33
    {
510
        /** @var $resultSet SearchResultSet */
511 33
        $resultSetClass = $this->getResultSetClassName();
512
        $resultSet = GeneralUtility::makeInstance($resultSetClass);
513 33
        $resultSet->setUsedSearchRequest($searchRequest);
514
        $this->lastResultSet = $resultSet;
515 33
516 4
        $resultSet = $this->handleSearchHook('beforeSearch', $resultSet);
517
518
        if ($searchRequest->getRawUserQueryIsNull() && !$this->getInitialSearchIsConfigured()) {
519 33
            // when no rawQuery was passed or no initialSearch is configured, we pass an empty result set
520
            return $resultSet;
521 33
        }
522
523 33
        if ($searchRequest->getRawUserQueryIsEmptyString() && !$this->typoScriptConfiguration->getSearchQueryAllowEmptyQuery()) {
524 33
            // the user entered an empty query string "" or "  " and empty querystring is not allowed
525
            return $resultSet;
526 33
        }
527 33
528 33
        $rawQuery = $searchRequest->getRawUserQuery();
529 33
        $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...
530 33
        $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...
531
        $this->initializeRegisteredSearchComponents($query, $searchRequest);
532
        $resultSet->setUsedQuery($query);
533 33
534 33
        $currentPage = max(0, $searchRequest->getPage());
535
        // if the number of results per page has been changed by the current request, reset the pagebrowser
536 33
        if ($this->resultsPerPageChanged) {
537
            $currentPage = 0;
538
        }
539
540
        $offSet = $currentPage * $resultsPerPage;
541
542
        // performing the actual search, sending the query to the Solr server
543
        $query = $this->modifyQuery($query, $searchRequest, $this->search);
544
        $response = $this->search->search($query, $offSet, null);
545 2
        $response = $this->modifyResponse($response, $searchRequest, $this->search);
0 ignored issues
show
Deprecated Code introduced by
The method ApacheSolrForTypo3\Solr\...rvice::modifyResponse() has been deprecated with message: Please use $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['afterSearch'] now to register your SearchResultSetProcessor will be removed in 8.0

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

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

Loading history...
546
547
        $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...
548 2
549 2
        $this->addSearchResultsToResultSet($response, $resultSet);
550
551 2
        $resultSet->setResponse($response);
552 2
        $resultSet->setUsedPage($currentPage);
553
        $resultSet->setUsedResultsPerPage($resultsPerPage);
554 2
        $resultSet->setUsedAdditionalFilters($this->getAdditionalFilters());
555 2
        $resultSet->setUsedSearch($this->search);
556
557
        /** @var $searchResultReconstitutionProcessor ResultSetReconstitutionProcessor */
558
        $searchResultReconstitutionProcessor = GeneralUtility::makeInstance(ResultSetReconstitutionProcessor::class);
559
        $searchResultReconstitutionProcessor->process($resultSet);
560
561
        return $this->handleSearchHook('afterSearch', $resultSet);
562
    }
563
564
    /**
565 35
     * Allows to modify a query before eventually handing it over to Solr.
566
     *
567 35
     * @param Query $query The current query before it's being handed over to Solr.
568 35
     * @param SearchRequest $searchRequest The searchRequest, relevant in the current context
569
     * @param Search $search The search, relevant in the current context
570
     * @throws \UnexpectedValueException
571
     * @return Query The modified query that is actually going to be given to Solr.
572
     */
573
    protected function modifyQuery(Query $query, SearchRequest $searchRequest, Search $search)
574
    {
575
        // hook to modify the search query
576
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifySearchQuery'])) {
577
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifySearchQuery'] as $classReference) {
578
                $queryModifier = GeneralUtility::getUserObj($classReference);
579
580
                if ($queryModifier instanceof Modifier) {
581
                    if ($queryModifier instanceof SearchAware) {
582
                        $queryModifier->setSearch($search);
583
                    }
584 24
585
                    if ($queryModifier instanceof SearchRequestAware) {
586 24
                        $queryModifier->setSearchRequest($searchRequest);
587
                    }
588
589
                    $query = $queryModifier->modifyQuery($query);
590
                } else {
591
                    throw new \UnexpectedValueException(
592
                        get_class($queryModifier) . ' must implement interface ' . Modifier::class,
593
                        1310387414
594
                    );
595
                }
596
            }
597
        }
598
599
        return $query;
600
    }
601
602
    /**
603
     * Allows to modify a response returned from Solr before returning it to
604
     * the rest of the extension.
605
     *
606
     * @deprecated Please use $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['afterSearch'] now to register your SearchResultSetProcessor will be removed in 8.0
607
     * @param \Apache_Solr_Response $response The response as returned by Solr
608 3
     * @param SearchRequest $searchRequest The searchRequest, relevant in the current context
609
     * @param Search $search The search, relevant in the current context
610 3
     * @return \Apache_Solr_Response The modified response that is actually going to be returned to the extension.
611 3
     * @throws \UnexpectedValueException if a response modifier does not implement interface ApacheSolrForTypo3\Solr\Search\ResponseModifier
612
     */
613
    protected function modifyResponse(\Apache_Solr_Response $response, SearchRequest $searchRequest, Search $search)
614
    {
615
        // hook to modify the search response
616 26
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifySearchResponse'])) {
617
            GeneralUtility::logDeprecatedFunction();
618 26
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifySearchResponse'] as $classReference) {
619
                $responseModifier = GeneralUtility::getUserObj($classReference);
620
621
                if ($responseModifier instanceof ResponseModifier) {
622
                    if ($responseModifier instanceof SearchAware) {
623
                        $responseModifier->setSearch($search);
624 2
                    }
625
626 2
                    if ($responseModifier instanceof SearchRequestAware) {
627
                        $responseModifier->setSearchRequest($searchRequest);
628
                    }
629
                    $response = $responseModifier->modifyResponse($response);
630
                } else {
631
                    throw new \UnexpectedValueException(
632 26
                        get_class($responseModifier) . ' must implement interface ' . ResponseModifier::class,
633
                        1343147211
634 26
                    );
635
                }
636
            }
637
638
            // add modification indicator
639
            $response->response->isModified = true;
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...
640
        }
641
642
        return $response;
643 33
    }
644
    /**
645 33
     * Retrieves a single document from solr by document id.
646 5
     *
647
     * @param string $documentId
648
     * @return SearchResult
649 28
     */
650 25
    public function getDocumentById($documentId)
651
    {
652 28
        /* @var $query Query */
653
        $query = GeneralUtility::makeInstance(Query::class, $documentId);
654
        $query->setQueryFieldsFromString('id');
655
656
        $response = $this->search->search($query, 0, 1);
657
        $this->processResponse($documentId, $query, $response);
658
659
        $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...
660
        return $resultDocument;
661
    }
662
663
    /**
664
     * This method is used to call the registered hooks during the search execution.
665
     *
666
     * @param string $eventName
667
     * @param SearchResultSet $resultSet
668
     * @return SearchResultSet
669
     */
670
    private function handleSearchHook($eventName, SearchResultSet $resultSet)
671
    {
672
        if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr'][$eventName])) {
673
            return $resultSet;
674
        }
675
676
        foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr'][$eventName] as $classReference) {
677
            $afterSearchProcessor = GeneralUtility::getUserObj($classReference);
678
            if ($afterSearchProcessor instanceof SearchResultSetProcessor) {
679
                $afterSearchProcessor->process($resultSet);
680
            }
681
        }
682
683
        return $resultSet;
684
    }
685
686
    /**
687
     * @return SearchResultSet
688
     */
689
    public function getLastResultSet()
690
    {
691
        return $this->lastResultSet;
692
    }
693
694
    /**
695
     * This method returns true when the last search was executed with an empty query
696
     * string or whitespaces only. When no search was triggered it will return false.
697
     *
698
     * @return bool
699
     */
700
    public function getLastSearchWasExecutedWithEmptyQueryString()
701
    {
702
        $wasEmptyQueryString = false;
703
        if ($this->lastResultSet != null) {
704
            $wasEmptyQueryString = $this->lastResultSet->getUsedSearchRequest()->getRawUserQueryIsEmptyString();
705
        }
706
707
        return $wasEmptyQueryString;
708
    }
709
710
    /**
711
     * @param int $requestedPerPage
712
     */
713
    protected function setPerPageInSession($requestedPerPage)
714
    {
715
        $GLOBALS['TSFE']->fe_user->setKey('ses', 'tx_solr_resultsPerPage', intval($requestedPerPage));
716
    }
717
718
    /**
719
     * @return mixed
720
     */
721
    protected function getPerPageFromSession()
722
    {
723
        return $GLOBALS['TSFE']->fe_user->getKey('ses', 'tx_solr_resultsPerPage');
724
    }
725
726
    /**
727
     * @return bool
728
     */
729
    protected function getInitialSearchIsConfigured()
730
    {
731
        return $this->typoScriptConfiguration->getSearchInitializeWithEmptyQuery() || $this->typoScriptConfiguration->getSearchShowResultsOfInitialEmptyQuery() || $this->typoScriptConfiguration->getSearchInitializeWithQuery() || $this->typoScriptConfiguration->getSearchShowResultsOfInitialQuery();
732
    }
733
734
    /**
735
     * @return mixed
736
     */
737
    protected function getRegisteredSearchComponents()
738
    {
739
        return GeneralUtility::makeInstance(SearchComponentManager::class)->getSearchComponents();
740
    }
741
742
    /**
743
     * This method is used to reference the SearchResult object from the response in the SearchResultSet object.
744
     *
745
     * @param \Apache_Solr_Response $response
746
     * @param SearchResultSet $resultSet
747
     */
748
    protected function addSearchResultsToResultSet($response, $resultSet)
749
    {
750
        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...
751
            return;
752
        }
753
754
        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...
755
            $resultSet->addSearchResult($searchResult);
756
        }
757
    }
758
}
759