Completed
Branch master (33af51)
by Timo
04:12
created

ResultsCommand::renderVariants()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4.7861

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 5
cts 12
cp 0.4167
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 2
crap 4.7861
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\Template;
34
use ApacheSolrForTypo3\Solr\Util;
35
use TYPO3\CMS\Core\Utility\GeneralUtility;
36
37
/**
38
 * Results view command
39
 *
40
 * @author Ingo Renner <[email protected]>
41
 */
42
class ResultsCommand implements PluginCommand
43
{
44
45
    /**
46
     * @var Search
47
     */
48
    protected $search;
49
50
    /**
51
     * Parent plugin
52
     *
53
     * @var Results
54
     */
55
    protected $parentPlugin;
56
57
    /**
58
     * Configuration
59
     *
60
     * @var array
61
     */
62
    protected $configuration;
63
64
    /**
65
     * Constructor.
66
     *
67
     * @param CommandPluginBase $parentPlugin Parent plugin object.
68
     */
69 18
    public function __construct(CommandPluginBase $parentPlugin)
70
    {
71 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...
72 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...
73 18
        $this->search = $parentPlugin->getSearchResultSetService()->getSearch();
74 18
    }
75
76
    /**
77
     * @return array
78
     */
79 18
    public function execute()
80
    {
81 18
        $numberOfResults = $this->search->getNumberOfResults();
82
83 18
        $query = $this->search->getQuery();
84 18
        $rawQueryTerms = $query->getKeywordsRaw();
85 18
        $queryTerms = $query->getKeywordsCleaned();
86
87 18
        $searchedFor = strtr(
88 18
            $this->parentPlugin->pi_getLL('results_searched_for'),
89 18
            array('@searchWord' => '<span class="tx-solr-search-word">' . $queryTerms . '</span>')
90
        );
91
92 18
        $foundResultsInfo = strtr(
93 18
            $this->parentPlugin->pi_getLL('results_found'),
94
            array(
95 18
                '@resultsTotal' => $this->search->getNumberOfResults(),
96 18
                '@resultsTime' => $this->search->getQueryTime()
97
            )
98
        );
99
100
        return array(
101 18
            'searched_for' => $searchedFor,
102 18
            'query' => $queryTerms,
103 18
            'query_urlencoded' => rawurlencode($rawQueryTerms),
104 18
            'query_raw' => $rawQueryTerms,
105 18
            'found' => $foundResultsInfo,
106 18
            'range' => $this->getPageBrowserRange(),
107 18
            'count' => $this->search->getNumberOfResults(),
108 18
            'offset' => ($this->search->getResultOffset() + 1),
109 18
            'query_time' => $this->search->getQueryTime(),
110 18
            'pagebrowser' => $this->getPageBrowser($numberOfResults),
111 18
            'filtered' => $this->isFiltered(),
112 18
            'filtered_by_user' => $this->isFilteredByUser(),
113
            /* construction of the array key:
114
             * loop_ : tells the plugin that the content of that field should be processed in a loop
115
             * result_documents : is the loop name as in the template
116
             * result_document : is the marker name for the single items in the loop
117
             */
118 18
            'loop_result_documents|result_document' => $this->getResultDocuments()
119
        );
120
    }
121
122
    /**
123
     * @return \Apache_Solr_Document[]
124
     */
125 18
    protected function getResultDocuments()
126
    {
127 18
        $responseDocuments = $this->search->getResultDocumentsEscaped();
128 18
        $resultDocuments = array();
129
130 18
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyResultSet'])) {
131 18
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyResultSet'] as $classReference) {
132 18
                $resultSetModifier = GeneralUtility::getUserObj($classReference);
133
134 18
                if ($resultSetModifier instanceof ResultSetModifier) {
135 18
                    $responseDocuments = $resultSetModifier->modifyResultSet($this,
136
                        $responseDocuments);
137
                } else {
138
                    throw new \UnexpectedValueException(
139
                        get_class($resultSetModifier) . ' must implement interface ApacheSolrForTypo3\Solr\ResultsetModifier\ResultSetModifier',
140 18
                        1310386927
141
                    );
142
                }
143
            }
144
        }
145
146 18
        foreach ($responseDocuments as $resultDocument) {
147
            /** @var  $resultDocument SearchResult */
148 18
            $temporaryResultDocument = array();
0 ignored issues
show
Unused Code introduced by
$temporaryResultDocument is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
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 ApacheSolrForTypo3\Solr\ResultDocumentModifier\ResultDocumentModifier',
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
        $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...
216 18
        $availableFields = $document->getFieldNames();
217 18
        $result = array();
218
219 18
        foreach ($availableFields as $fieldName) {
220 18
            $processingInstruction = $processingInstructions[$fieldName];
221
222
            // TODO switch to field processors
223
            // TODO allow to have multiple (comma-separated) instructions for each field
224
            switch ($processingInstruction) {
225 18
                case 'timestamp':
226 18
                    $processedFieldValue = Util::isoToTimestamp($document->{$fieldName});
227 18
                    break;
228 18
                case 'serialize':
229
                    if (!empty($document->{$fieldName})) {
230
                        $processedFieldValue = serialize($document->{$fieldName});
231
                    } else {
232
                        $processedFieldValue = '';
233
                    }
234
                    break;
235 18
                case 'skip':
236
                    continue 2;
237
                default:
238 18
                    $processedFieldValue = $document->{$fieldName};
239
            }
240
241
            // escape markers in document fields
242
            // TODO remove after switching to fluid templates
243 18
            $processedFieldValue = Template::escapeMarkers($processedFieldValue);
244
245 18
            $result[$fieldName] = $processedFieldValue;
246
        }
247
248 18
        return $result;
249
    }
250
251
    /**
252
     * @param array $document
253
     * @return array
254
     */
255 18
    protected function renderDocumentFields(array $document)
256
    {
257 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...
258 18
        $cObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Frontend\\ContentObject\\ContentObjectRenderer');
259 18
        $cObj->start($document);
260
261 18
        foreach ($renderingInstructions as $renderingInstructionName => $renderingInstruction) {
262 18
            if (!is_array($renderingInstruction)) {
263 18
                $renderedField = $cObj->cObjGetSingle(
264 18
                    $renderingInstructions[$renderingInstructionName],
265 18
                    $renderingInstructions[$renderingInstructionName . '.']
266
                );
267
268 18
                $document[$renderingInstructionName] = $renderedField;
269
            }
270
        }
271
272 18
        return $document;
273
    }
274
275
    /**
276
     * @param $numberOfResults
277
     * @return string
278
     */
279 18
    protected function getPageBrowser($numberOfResults)
280
    {
281 18
        $pageBrowserMarkup = '';
282 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...
283
284 18
        if ($solrPageBrowserConfiguration['enabled']) {
285 18
            $resultsPerPage = $this->parentPlugin->getSearchResultSetService()->getLastResultSet()->getResultsPerPage();
286 18
            $numberOfPages = intval($numberOfResults / $resultsPerPage)
287 18
                + (($numberOfResults % $resultsPerPage) == 0 ? 0 : 1);
288
289 18
            $solrGetParameters = GeneralUtility::_GET('tx_solr');
290 18
            if (!is_array($solrGetParameters)) {
291 17
                $solrGetParameters = array();
292
            }
293 18
            $currentPage = $solrGetParameters['page'];
294 18
            unset($solrGetParameters['page']);
295
296 18
            $pageBrowserConfiguration = array_merge(
297
                $solrPageBrowserConfiguration,
298
                array(
299 18
                    'numberOfPages' => $numberOfPages,
300 18
                    'currentPage' => $currentPage,
301 18
                    'extraQueryString' => GeneralUtility::implodeArrayForUrl('tx_solr',
302
                        $solrGetParameters),
303 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...
304
                )
305
            );
306
307 18
            $pageBrowser = GeneralUtility::makeInstance(
308 18
                'ApacheSolrForTypo3\\Solr\\Plugin\\Results\\PageBrowser',
309
                $pageBrowserConfiguration,
310 18
                $this->getPageBrowserLabels()
311
            );
312
313 18
            $pageBrowserMarkup = $pageBrowser->render();
314
        }
315
316 18
        return $pageBrowserMarkup;
317
    }
318
319
    /**
320
     * Gets the labels for us in the page browser
321
     *
322
     * @return array page browser labels
323
     */
324 18
    protected function getPageBrowserLabels()
325
    {
326
        $labelKeys = array(
327 18
            'pagebrowser_first',
328
            'pagebrowser_next',
329
            'pagebrowser_prev',
330
            'pagebrowser_last'
331
        );
332
333 18
        $labels = array();
334 18
        foreach ($labelKeys as $labelKey) {
335 18
            $labels[$labelKey] = $this->parentPlugin->pi_getLL($labelKey);
336
        }
337
338 18
        return $labels;
339
    }
340
341
    /**
342
     * @return string
343
     */
344 18
    protected function getPageBrowserRange()
345
    {
346 18
        $label = '';
0 ignored issues
show
Unused Code introduced by
$label is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
347
348 18
        $resultsFrom = $this->search->getResponseBody()->start + 1;
349 18
        $resultsTo = $resultsFrom + count($this->search->getResultDocumentsEscaped()) - 1;
350 18
        $resultsTotal = $this->search->getNumberOfResults();
351
352 18
        $label = strtr(
353 18
            $this->parentPlugin->pi_getLL('results_range'),
354
            array(
355 18
                '@resultsFrom' => $resultsFrom,
356 18
                '@resultsTo' => $resultsTo,
357 18
                '@resultsTotal' => $resultsTotal
358
            )
359
        );
360
361 18
        return $label;
362
    }
363
364
    /**
365
     * Gets the parent plugin.
366
     *
367
     * @return Results
368
     */
369 18
    public function getParentPlugin()
370
    {
371 18
        return $this->parentPlugin;
372
    }
373
374
    /**
375
     * Determines whether filters have been applied to the query or not.
376
     *
377
     * @return string 1 if filters are applied, 0 if not (for use in templates)
378
     */
379 18
    protected function isFiltered()
380
    {
381 18
        $filters = $this->search->getQuery()->getFilters();
382 18
        $filtered = !empty($filters);
383
384 18
        return ($filtered ? '1' : '0');
385
    }
386
387
    /**
388
     * Determines whether filters have been applied by the user (facets for
389
     * example) to the query or not.
390
     *
391
     * @return string 1 if filters are applied, 0 if not (for use in templates)
392
     */
393 18
    protected function isFilteredByUser()
394
    {
395 18
        $userFiltered = false;
396 18
        $resultParameters = GeneralUtility::_GET('tx_solr');
397
398 18
        if (isset($resultParameters['filter'])) {
399 1
            $userFiltered = true;
400
        }
401
402 18
        return ($userFiltered ? '1' : '0');
403
    }
404
}
405