Passed
Pull Request — master (#1249)
by
unknown
19:13
created

ResultsCommand   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 361
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 75.8%

Importance

Changes 0
Metric Value
wmc 35
lcom 1
cbo 11
dl 0
loc 361
ccs 166
cts 219
cp 0.758
rs 9
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getParentPlugin() 0 4 1
A isFiltered() 0 7 2
A isFilteredByUser() 0 11 3
A __construct() 0 6 1
B execute() 0 42 1
C getResultDocuments() 0 50 8
A renderVariants() 0 15 3
B processDocumentFieldsToArray() 0 39 6
A renderDocumentFields() 0 19 3
B getPageBrowser() 0 39 4
A getPageBrowserLabels() 0 16 2
A getPageBrowserRange() 0 17 1
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\Configuration\TypoScriptConfiguration;
34
use ApacheSolrForTypo3\Solr\System\DateTime\FormatService;
35
use ApacheSolrForTypo3\Solr\Template;
36
use TYPO3\CMS\Core\Utility\GeneralUtility;
37
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
38
39
/**
40
 * Results view command
41
 *
42
 * @author Ingo Renner <[email protected]>
43
 */
44
class ResultsCommand implements PluginCommand
45
{
46
47
    /**
48
     * @var Search
49
     */
50
    protected $search;
51
52
    /**
53
     * Parent plugin
54
     *
55
     * @var CommandPluginBase
56
     */
57
    protected $parentPlugin;
58
59
    /**
60
     * Configuration
61
     *
62
     * @var TypoScriptConfiguration
63
     */
64
    protected $configuration;
65
66
    /**
67
     * Constructor.
68
     *
69
     * @param CommandPluginBase $parentPlugin Parent plugin object.
70
     */
71 18
    public function __construct(CommandPluginBase $parentPlugin)
72
    {
73 18
        $this->parentPlugin = $parentPlugin;
74 18
        $this->configuration = $parentPlugin->typoScriptConfiguration;
75 18
        $this->search = $parentPlugin->getSearchResultSetService()->getSearch();
76 18
    }
77
78
    /**
79
     * @return array
80
     */
81 18
    public function execute()
82
    {
83 18
        $numberOfResults = $this->search->getNumberOfResults();
84
85 18
        $query = $this->search->getQuery();
86 18
        $rawQueryTerms = $query->getKeywordsRaw();
87 18
        $queryTerms = $query->getKeywordsCleaned();
88
89 18
        $searchedFor = strtr(
90 18
            $this->parentPlugin->pi_getLL('results_searched_for'),
91 18
            ['@searchWord' => '<span class="tx-solr-search-word">' . $queryTerms . '</span>']
92 18
        );
93
94 18
        $foundResultsInfo = strtr(
95 18
            $this->parentPlugin->pi_getLL('results_found'),
96
            [
97 18
                '@resultsTotal' => $this->search->getNumberOfResults(),
98 18
                '@resultsTime' => $this->search->getQueryTime()
99 18
            ]
100 18
        );
101
102
        return [
103 18
            'searched_for' => $searchedFor,
104 18
            'query' => $queryTerms,
105 18
            'query_urlencoded' => rawurlencode($rawQueryTerms),
106 18
            'query_raw' => $rawQueryTerms,
107 18
            'found' => $foundResultsInfo,
108 18
            'range' => $this->getPageBrowserRange(),
109 18
            'count' => $this->search->getNumberOfResults(),
110 18
            'offset' => ($this->search->getResultOffset() + 1),
111 18
            'query_time' => $this->search->getQueryTime(),
112 18
            'pagebrowser' => $this->getPageBrowser($numberOfResults),
113 18
            'filtered' => $this->isFiltered(),
114 18
            'filtered_by_user' => $this->isFilteredByUser(),
115
            /* construction of the array key:
116
             * loop_ : tells the plugin that the content of that field should be processed in a loop
117
             * result_documents : is the loop name as in the template
118
             * result_document : is the marker name for the single items in the loop
119
             */
120 18
            'loop_result_documents|result_document' => $this->getResultDocuments()
121 18
        ];
122
    }
123
124
    /**
125
     * @return \Apache_Solr_Document[]
126
     */
127 18
    protected function getResultDocuments()
128
    {
129 18
        $responseDocuments = $this->search->getResultDocumentsEscaped();
130 18
        $resultDocuments = [];
131
132 18
        if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyResultSet'])) {
133 18
            foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyResultSet'] as $classReference) {
134 18
                $resultSetModifier = GeneralUtility::getUserObj($classReference);
135
136 18
                if ($resultSetModifier instanceof ResultSetModifier) {
137 18
                    $responseDocuments = $resultSetModifier->modifyResultSet($this,
138 18
                        $responseDocuments);
139 18
                } else {
140
                    throw new \UnexpectedValueException(
141
                        get_class($resultSetModifier) . ' must implement interface' . ResultSetModifier::class,
142
                        1310386927
143
                    );
144
                }
145 18
            }
146 18
        }
147
148 18
        foreach ($responseDocuments as $resultDocument) {
149
            /** @var  $resultDocument SearchResult */
150 18
            $temporaryResultDocument = $this->processDocumentFieldsToArray($resultDocument);
151
152 18
            if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyResultDocument'])) {
153 18
                foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['solr']['modifyResultDocument'] as $classReference) {
154 18
                    $resultDocumentModifier = GeneralUtility::getUserObj($classReference);
155
156 18
                    if ($resultDocumentModifier instanceof ResultDocumentModifier) {
157 18
                        $temporaryResultDocument = $resultDocumentModifier->modifyResultDocument($this,
158 18
                            $temporaryResultDocument);
159 18
                    } else {
160
                        throw new \UnexpectedValueException(
161
                            get_class($resultDocumentModifier) . ' must implement interface ' . ResultDocumentModifier::class,
162
                            1310386725
163
                        );
164
                    }
165 18
                }
166 18
            }
167
168 18
            $renderedResultDocument = $this->renderDocumentFields($temporaryResultDocument);
169 18
            $renderedResultDocument = $this->renderVariants($resultDocument, $renderedResultDocument);
170
171 18
            $resultDocuments[] = $renderedResultDocument;
172 18
            unset($temporaryResultDocument);
173 18
        }
174
175 18
        return $resultDocuments;
176
    }
177
178
    /**
179
     * Renders the collapsedDocuments/variants of a document and adds them into the virtual field "variants".
180
     *
181
     * @param SearchResult $resultDocument
182
     * @param array $renderedResultDocument
183
     * @return mixed
184
     */
185 18
    protected function renderVariants(SearchResult $resultDocument, $renderedResultDocument)
186
    {
187 18
        $availableVariants = $resultDocument->getVariants();
188
189 18
        if (!is_array($availableVariants)) {
190
            return $renderedResultDocument;
191
        }
192
193 18
        foreach ($availableVariants as $expandedResult) {
194
            $expandedDocumentArray = $this->processDocumentFieldsToArray($expandedResult);
195
            $renderedResultDocument['variants'][] = $expandedDocumentArray;
196 18
        }
197
198 18
        return $renderedResultDocument;
199
    }
200
201
    /**
202
     * takes a search result document and processes its fields according to the
203
     * instructions configured in TS. Currently available instructions are
204
     *    * timestamp - converts a date field into a unix timestamp
205
     *    * serialize - uses serialize() to encode multivalue fields which then can be put out using the MULTIVALUE view helper
206
     *    * skip - skips the whole field so that it is not available in the result, useful for the spell field f.e.
207
     * The default is to do nothing and just add the document's field to the
208
     * resulting array.
209
     *
210
     * @param \Apache_Solr_Document $document the Apache_Solr_Document result document
211
     * @return array An array with field values processed like defined in TS
212
     */
213 18
    protected function processDocumentFieldsToArray(
214
        \Apache_Solr_Document $document
215
    ) {
216 18
        $formatService = GeneralUtility::makeInstance(FormatService::class);
217 18
        $processingInstructions = $this->configuration->getSearchResultsFieldProcessingInstructionsConfiguration();
218 18
        $availableFields = $document->getFieldNames();
219 18
        $result = [];
220
221 18
        foreach ($availableFields as $fieldName) {
222 18
            $processingInstruction = $processingInstructions[$fieldName];
223
224
            // TODO switch to field processors
225
            // TODO allow to have multiple (comma-separated) instructions for each field
226
            switch ($processingInstruction) {
227 18
                case 'timestamp':
228 18
                    $processedFieldValue = $formatService->isoToTimestamp($document->{$fieldName});
229 18
                    break;
230 18
                case 'serialize':
231
                    if (!empty($document->{$fieldName})) {
232
                        $processedFieldValue = serialize($document->{$fieldName});
233
                    } else {
234
                        $processedFieldValue = '';
235
                    }
236
                    break;
237 18
                case 'skip':
238
                    continue 2;
239 18
                default:
240 18
                    $processedFieldValue = $document->{$fieldName};
241 18
            }
242
243
            // escape markers in document fields
244
            // TODO remove after switching to fluid templates
245 18
            $processedFieldValue = Template::escapeMarkers($processedFieldValue);
246
247 18
            $result[$fieldName] = $processedFieldValue;
248 18
        }
249
250 18
        return $result;
251
    }
252
253
    /**
254
     * @param array $document
255
     * @return array
256
     */
257 18
    protected function renderDocumentFields(array $document)
258
    {
259 18
        $renderingInstructions = $this->configuration->getSearchResultsFieldRenderingInstructionsConfiguration();
260 18
        $cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
261 18
        $cObj->start($document);
262
263 18
        foreach ($renderingInstructions as $renderingInstructionName => $renderingInstruction) {
264 18
            if (!is_array($renderingInstruction)) {
265 18
                $renderedField = $cObj->cObjGetSingle(
266 18
                    $renderingInstructions[$renderingInstructionName],
267 18
                    $renderingInstructions[$renderingInstructionName . '.']
268 18
                );
269
270 18
                $document[$renderingInstructionName] = $renderedField;
271 18
            }
272 18
        }
273
274 18
        return $document;
275
    }
276
277
    /**
278
     * @param $numberOfResults
279
     * @return string
280
     */
281 18
    protected function getPageBrowser($numberOfResults)
282
    {
283 18
        $pageBrowserMarkup = '';
284 18
        $solrPageBrowserConfiguration = $this->configuration->getSearchResultsPageBrowserConfiguration();
285
286 18
        if ($solrPageBrowserConfiguration['enabled']) {
287 18
            $resultsPerPage = $this->parentPlugin->getSearchResultSetService()->getLastResultSet()->getResultsPerPage();
288 18
            $numberOfPages = intval($numberOfResults / $resultsPerPage)
289 18
                + (($numberOfResults % $resultsPerPage) == 0 ? 0 : 1);
290
291 18
            $solrGetParameters = GeneralUtility::_GET('tx_solr');
292 18
            if (!is_array($solrGetParameters)) {
293 17
                $solrGetParameters = [];
294 17
            }
295 18
            $currentPage = $solrGetParameters['page'];
296 18
            unset($solrGetParameters['page']);
297
298 18
            $pageBrowserConfiguration = array_merge(
299 18
                $solrPageBrowserConfiguration,
300
                [
301 18
                    'numberOfPages' => $numberOfPages,
302 18
                    'currentPage' => $currentPage,
303 18
                    'extraQueryString' => GeneralUtility::implodeArrayForUrl('tx_solr',
304 18
                        $solrGetParameters),
305 18
                    'templateFile' => $this->configuration->getTemplateByFileKey('pagebrowser')
306 18
                ]
307 18
            );
308
309 18
            $pageBrowser = GeneralUtility::makeInstance(
310 18
                PageBrowser::class,
311 18
                $pageBrowserConfiguration,
312 18
                $this->getPageBrowserLabels()
313 18
            );
314
315 18
            $pageBrowserMarkup = $pageBrowser->render();
316 18
        }
317
318 18
        return $pageBrowserMarkup;
319
    }
320
321
    /**
322
     * Gets the labels for us in the page browser
323
     *
324
     * @return array page browser labels
325
     */
326 18
    protected function getPageBrowserLabels()
327
    {
328
        $labelKeys = [
329 18
            'pagebrowser_first',
330 18
            'pagebrowser_next',
331 18
            'pagebrowser_prev',
332
            'pagebrowser_last'
333 18
        ];
334
335 18
        $labels = [];
336 18
        foreach ($labelKeys as $labelKey) {
337 18
            $labels[$labelKey] = $this->parentPlugin->pi_getLL($labelKey);
338 18
        }
339
340 18
        return $labels;
341
    }
342
343
    /**
344
     * @return string
345
     */
346 18
    protected function getPageBrowserRange()
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
            [
355 18
                '@resultsFrom' => $resultsFrom,
356 18
                '@resultsTo' => $resultsTo,
357
                '@resultsTotal' => $resultsTotal
358 18
            ]
359 18
        );
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 1
        }
401
402 18
        return ($userFiltered ? '1' : '0');
403
    }
404
}
405