Completed
Push — master ( 165dee...fedafb )
by Timo
191:42 queued 190:58
created

ResultsCommand::getPageBrowserLabels()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.7462

Importance

Changes 0
Metric Value
dl 0
loc 16
ccs 6
cts 14
cp 0.4286
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 10
nc 2
nop 0
crap 2.7462
1
<?php
2
namespace ApacheSolrForTypo3\Solr\Plugin\Results;
3
4
/***************************************************************
5
 *  Copyright notice
6
 *
7
 *  (c) 2009-2015 Ingo Renner <[email protected]>
8
 *  All rights reserved
9
 *
10
 *  This script is part of the TYPO3 project. The TYPO3 project is
11
 *  free software; you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
13
 *  the Free Software Foundation; either version 2 of the License, or
14
 *  (at your option) any later version.
15
 *
16
 *  The GNU General Public License can be found at
17
 *  http://www.gnu.org/copyleft/gpl.html.
18
 *
19
 *  This script is distributed in the hope that it will be useful,
20
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 *  GNU General Public License for more details.
23
 *
24
 *  This copyright notice MUST APPEAR in all copies of the script!
25
 ***************************************************************/
26
27
use ApacheSolrForTypo3\Solr\Domain\Search\ResultSet\SearchResult;
28
use ApacheSolrForTypo3\Solr\Plugin\CommandPluginBase;
29
use ApacheSolrForTypo3\Solr\Plugin\PluginCommand;
30
use ApacheSolrForTypo3\Solr\ResultDocumentModifier\ResultDocumentModifier;
31
use ApacheSolrForTypo3\Solr\ResultsetModifier\ResultSetModifier;
32
use ApacheSolrForTypo3\Solr\Search;
33
use ApacheSolrForTypo3\Solr\System\DateTime\FormatService;
34
use ApacheSolrForTypo3\Solr\Template;
35
use TYPO3\CMS\Core\Utility\GeneralUtility;
36
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
37
38
/**
39
 * Results view command
40
 *
41
 * @author Ingo Renner <[email protected]>
42
 */
43
class ResultsCommand implements PluginCommand
44
{
45
46
    /**
47
     * @var Search
48
     */
49
    protected $search;
50
51
    /**
52
     * Parent plugin
53
     *
54
     * @var Results
55
     */
56
    protected $parentPlugin;
57
58
    /**
59
     * Configuration
60
     *
61
     * @var array
62
     */
63
    protected $configuration;
64
65
    /**
66
     * Constructor.
67
     *
68
     * @param CommandPluginBase $parentPlugin Parent plugin object.
69
     */
70 18
    public function __construct(CommandPluginBase $parentPlugin)
71
    {
72 18
        $this->parentPlugin = $parentPlugin;
0 ignored issues
show
Documentation Bug introduced by
$parentPlugin is of type object<ApacheSolrForTypo...ugin\CommandPluginBase>, but the property $parentPlugin was declared to be of type object<ApacheSolrForTypo...Plugin\Results\Results>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
73 18
        $this->configuration = $parentPlugin->typoScriptConfiguration;
0 ignored issues
show
Documentation Bug introduced by
It seems like $parentPlugin->typoScriptConfiguration of type object<ApacheSolrForTypo...ypoScriptConfiguration> is incompatible with the declared type array of property $configuration.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
74 18
        $this->search = $parentPlugin->getSearchResultSetService()->getSearch();
75 18
    }
76
77
    /**
78
     * @return array
79
     */
80 18
    public function execute()
81
    {
82 18
        $numberOfResults = $this->search->getNumberOfResults();
83
84 18
        $query = $this->search->getQuery();
85 18
        $rawQueryTerms = $query->getKeywordsRaw();
86 18
        $queryTerms = $query->getKeywordsCleaned();
87
88 18
        $searchedFor = strtr(
89 18
            $this->parentPlugin->pi_getLL('results_searched_for'),
90 18
            ['@searchWord' => '<span class="tx-solr-search-word">' . $queryTerms . '</span>']
91
        );
92
93 18
        $foundResultsInfo = strtr(
94 18
            $this->parentPlugin->pi_getLL('results_found'),
95
            [
96 18
                '@resultsTotal' => $this->search->getNumberOfResults(),
97 18
                '@resultsTime' => $this->search->getQueryTime()
98
            ]
99
        );
100
101
        return [
102 18
            'searched_for' => $searchedFor,
103 18
            'query' => $queryTerms,
104 18
            'query_urlencoded' => rawurlencode($rawQueryTerms),
105 18
            'query_raw' => $rawQueryTerms,
106 18
            'found' => $foundResultsInfo,
107 18
            'range' => $this->getPageBrowserRange(),
108 18
            'count' => $this->search->getNumberOfResults(),
109 18
            'offset' => ($this->search->getResultOffset() + 1),
110 18
            'query_time' => $this->search->getQueryTime(),
111 18
            'pagebrowser' => $this->getPageBrowser($numberOfResults),
112 18
            'filtered' => $this->isFiltered(),
113 18
            'filtered_by_user' => $this->isFilteredByUser(),
114
            /* construction of the array key:
115
             * loop_ : tells the plugin that the content of that field should be processed in a loop
116
             * result_documents : is the loop name as in the template
117
             * result_document : is the marker name for the single items in the loop
118
             */
119 18
            'loop_result_documents|result_document' => $this->getResultDocuments()
120
        ];
121
    }
122
123
    /**
124
     * @return \Apache_Solr_Document[]
125
     */
126 18
    protected function getResultDocuments()
127
    {
128 18
        $responseDocuments = $this->search->getResultDocumentsEscaped();
129 18
        $resultDocuments = [];
130
131 18
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyResultSet'])) {
132 18
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyResultSet'] as $classReference) {
133 18
                $resultSetModifier = GeneralUtility::getUserObj($classReference);
134
135 18
                if ($resultSetModifier instanceof ResultSetModifier) {
136 18
                    $responseDocuments = $resultSetModifier->modifyResultSet($this,
137
                        $responseDocuments);
138
                } else {
139
                    throw new \UnexpectedValueException(
140
                        get_class($resultSetModifier) . ' must implement interface' . ResultSetModifier::class,
141 18
                        1310386927
142
                    );
143
                }
144
            }
145
        }
146
147 18
        foreach ($responseDocuments as $resultDocument) {
148
            /** @var  $resultDocument SearchResult */
149 18
            $temporaryResultDocument = $this->processDocumentFieldsToArray($resultDocument);
150
151 18
            if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyResultDocument'])) {
152 18
                foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyResultDocument'] as $classReference) {
153 18
                    $resultDocumentModifier = GeneralUtility::getUserObj($classReference);
154
155 18
                    if ($resultDocumentModifier instanceof ResultDocumentModifier) {
156 18
                        $temporaryResultDocument = $resultDocumentModifier->modifyResultDocument($this,
157
                            $temporaryResultDocument);
158
                    } else {
159
                        throw new \UnexpectedValueException(
160
                            get_class($resultDocumentModifier) . ' must implement interface ' . ResultDocumentModifier::class,
161 18
                            1310386725
162
                        );
163
                    }
164
                }
165
            }
166
167 18
            $renderedResultDocument = $this->renderDocumentFields($temporaryResultDocument);
168 18
            $renderedResultDocument = $this->renderVariants($resultDocument, $renderedResultDocument);
169
170 18
            $resultDocuments[] = $renderedResultDocument;
171 18
            unset($temporaryResultDocument);
172
        }
173
174 18
        return $resultDocuments;
175
    }
176
177
    /**
178
     * Renders the collapsedDocuments/variants of a document and adds them into the virtual field "variants".
179
     *
180
     * @param SearchResult $resultDocument
181
     * @param array $renderedResultDocument
182
     * @return mixed
183
     */
184 18
    protected function renderVariants(SearchResult $resultDocument, $renderedResultDocument)
185
    {
186 18
        $availableVariants = $resultDocument->getVariants();
187
188 18
        if (!is_array($availableVariants)) {
189
            return $renderedResultDocument;
190
        }
191
192 18
        foreach ($availableVariants as $expandedResult) {
193
            $expandedDocumentArray = $this->processDocumentFieldsToArray($expandedResult);
194
            $renderedResultDocument['variants'][] = $expandedDocumentArray;
195
        }
196
197 18
        return $renderedResultDocument;
198
    }
199
200
    /**
201
     * takes a search result document and processes its fields according to the
202
     * instructions configured in TS. Currently available instructions are
203
     *    * timestamp - converts a date field into a unix timestamp
204
     *    * serialize - uses serialize() to encode multivalue fields which then can be put out using the MULTIVALUE view helper
205
     *    * skip - skips the whole field so that it is not available in the result, useful for the spell field f.e.
206
     * The default is to do nothing and just add the document's field to the
207
     * resulting array.
208
     *
209
     * @param \Apache_Solr_Document $document the Apache_Solr_Document result document
210
     * @return array An array with field values processed like defined in TS
211
     */
212 18
    protected function processDocumentFieldsToArray(
213
        \Apache_Solr_Document $document
214
    ) {
215 18
        $formatService = GeneralUtility::makeInstance(FormatService::class);
216 18
        $processingInstructions = $this->configuration->getSearchResultsFieldProcessingInstructionsConfiguration();
0 ignored issues
show
Bug introduced by
The method getSearchResultsFieldPro...structionsConfiguration cannot be called on $this->configuration (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
217 18
        $availableFields = $document->getFieldNames();
218 18
        $result = [];
219
220 18
        foreach ($availableFields as $fieldName) {
221 18
            $processingInstruction = $processingInstructions[$fieldName];
222
223
            // TODO switch to field processors
224
            // TODO allow to have multiple (comma-separated) instructions for each field
225
            switch ($processingInstruction) {
226 18
                case 'timestamp':
227 18
                    $processedFieldValue = $formatService->isoToTimestamp($document->{$fieldName});
228 18
                    break;
229 18
                case 'serialize':
230
                    if (!empty($document->{$fieldName})) {
231
                        $processedFieldValue = serialize($document->{$fieldName});
232
                    } else {
233
                        $processedFieldValue = '';
234
                    }
235
                    break;
236 18
                case 'skip':
237
                    continue 2;
238
                default:
239 18
                    $processedFieldValue = $document->{$fieldName};
240
            }
241
242
            // escape markers in document fields
243
            // TODO remove after switching to fluid templates
244 18
            $processedFieldValue = Template::escapeMarkers($processedFieldValue);
245
246 18
            $result[$fieldName] = $processedFieldValue;
247
        }
248
249 18
        return $result;
250
    }
251
252
    /**
253
     * @param array $document
254
     * @return array
255
     */
256 18
    protected function renderDocumentFields(array $document)
257
    {
258 18
        $renderingInstructions = $this->configuration->getSearchResultsFieldRenderingInstructionsConfiguration();
0 ignored issues
show
Bug introduced by
The method getSearchResultsFieldRen...structionsConfiguration cannot be called on $this->configuration (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
259 18
        $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
260 18
        $cObj->start($document);
261
262 18
        foreach ($renderingInstructions as $renderingInstructionName => $renderingInstruction) {
263 18
            if (!is_array($renderingInstruction)) {
264 18
                $renderedField = $cObj->cObjGetSingle(
265 18
                    $renderingInstructions[$renderingInstructionName],
266 18
                    $renderingInstructions[$renderingInstructionName . '.']
267
                );
268
269 18
                $document[$renderingInstructionName] = $renderedField;
270
            }
271
        }
272
273 18
        return $document;
274
    }
275
276
    /**
277
     * @param $numberOfResults
278
     * @return string
279
     */
280 18
    protected function getPageBrowser($numberOfResults)
281
    {
282 18
        $pageBrowserMarkup = '';
283 18
        $solrPageBrowserConfiguration = $this->configuration->getSearchResultsPageBrowserConfiguration();
0 ignored issues
show
Bug introduced by
The method getSearchResultsPageBrowserConfiguration cannot be called on $this->configuration (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
284
285 18
        if ($solrPageBrowserConfiguration['enabled']) {
286 18
            $resultsPerPage = $this->parentPlugin->getSearchResultSetService()->getLastResultSet()->getResultsPerPage();
287 18
            $numberOfPages = intval($numberOfResults / $resultsPerPage)
288 18
                + (($numberOfResults % $resultsPerPage) == 0 ? 0 : 1);
289
290 18
            $solrGetParameters = GeneralUtility::_GET('tx_solr');
291 18
            if (!is_array($solrGetParameters)) {
292 17
                $solrGetParameters = [];
293
            }
294 18
            $currentPage = $solrGetParameters['page'];
295 18
            unset($solrGetParameters['page']);
296
297 18
            $pageBrowserConfiguration = array_merge(
298
                $solrPageBrowserConfiguration,
299
                [
300 18
                    'numberOfPages' => $numberOfPages,
301 18
                    'currentPage' => $currentPage,
302 18
                    'extraQueryString' => GeneralUtility::implodeArrayForUrl('tx_solr',
303
                        $solrGetParameters),
304 18
                    'templateFile' => $this->configuration->getTemplateByFileKey('pagebrowser')
0 ignored issues
show
Bug introduced by
The method getTemplateByFileKey cannot be called on $this->configuration (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
305
                ]
306
            );
307
308 18
            $pageBrowser = GeneralUtility::makeInstance(
309 18
                PageBrowser::class,
310
                $pageBrowserConfiguration,
311 18
                $this->getPageBrowserLabels()
312
            );
313
314 18
            $pageBrowserMarkup = $pageBrowser->render();
315
        }
316
317 18
        return $pageBrowserMarkup;
318
    }
319
320
    /**
321
     * Gets the labels for us in the page browser
322
     *
323
     * @return array page browser labels
324
     */
325 18
    protected function getPageBrowserLabels()
326
    {
327
        $labelKeys = [
328 18
            'pagebrowser_first',
329
            'pagebrowser_next',
330
            'pagebrowser_prev',
331
            'pagebrowser_last'
332
        ];
333
334 18
        $labels = [];
335 18
        foreach ($labelKeys as $labelKey) {
336 18
            $labels[$labelKey] = $this->parentPlugin->pi_getLL($labelKey);
337
        }
338
339 18
        return $labels;
340
    }
341
342
    /**
343
     * @return string
344
     */
345 18
    protected function getPageBrowserRange()
346
    {
347 18
        $resultsFrom = $this->search->getResponseBody()->start + 1;
348 18
        $resultsTo = $resultsFrom + count($this->search->getResultDocumentsEscaped()) - 1;
349 18
        $resultsTotal = $this->search->getNumberOfResults();
350
351 18
        $label = strtr(
352 18
            $this->parentPlugin->pi_getLL('results_range'),
353
            [
354 18
                '@resultsFrom' => $resultsFrom,
355 18
                '@resultsTo' => $resultsTo,
356 18
                '@resultsTotal' => $resultsTotal
357
            ]
358
        );
359
360 18
        return $label;
361
    }
362
363
    /**
364
     * Gets the parent plugin.
365
     *
366
     * @return Results
367
     */
368 18
    public function getParentPlugin()
369
    {
370 18
        return $this->parentPlugin;
371
    }
372
373
    /**
374
     * Determines whether filters have been applied to the query or not.
375
     *
376
     * @return string 1 if filters are applied, 0 if not (for use in templates)
377
     */
378 18
    protected function isFiltered()
379
    {
380 18
        $filters = $this->search->getQuery()->getFilters();
381 18
        $filtered = !empty($filters);
382
383 18
        return ($filtered ? '1' : '0');
384
    }
385
386
    /**
387
     * Determines whether filters have been applied by the user (facets for
388
     * example) to the query or not.
389
     *
390
     * @return string 1 if filters are applied, 0 if not (for use in templates)
391
     */
392 18
    protected function isFilteredByUser()
393
    {
394 18
        $userFiltered = false;
395 18
        $resultParameters = GeneralUtility::_GET('tx_solr');
396
397 18
        if (isset($resultParameters['filter'])) {
398 1
            $userFiltered = true;
399
        }
400
401 18
        return ($userFiltered ? '1' : '0');
402
    }
403
}
404