SearchPageController::renderSearchResults()   F
last analyzed

Complexity

Conditions 17
Paths 450

Size

Total Lines 160
Code Lines 90

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 90
c 1
b 0
f 0
dl 0
loc 160
rs 1.6692
cc 17
nc 450
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php declare(strict_types = 1);
2
3
namespace Suilven\FreeTextSearch\Page;
4
5
use SilverStripe\ORM\ArrayList;
6
use SilverStripe\ORM\DataObject;
7
use SilverStripe\ORM\PaginatedList;
8
use SilverStripe\View\ArrayData;
9
use Suilven\FreeTextSearch\Container\SearchResults;
10
use Suilven\FreeTextSearch\Factory\SearcherFactory;
11
use Suilven\FreeTextSearch\Helper\FacetLinkHelper;
12
use Suilven\FreeTextSearch\Indexes;
13
14
// @phpcs:disable SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingTraversableTypeHintSpecification
15
16
/**
17
 * Class SearchPageController
18
 *
19
 * @package Suilven\FreeTextSearch\Page
20
 * @property int $ID Page ID
21
 * @property int $PageSize the number of results to show on each page
22
 */
23
24
class SearchPageController extends \PageController
25
{
26
    /** @var array<string,string|float|int|bool> */
27
    protected static $selected = [];
28
29
    /** @var array<string,int> */
30
    private $tagCloud = [];
0 ignored issues
show
introduced by
The private property $tagCloud is not used, and could be removed.
Loading history...
31
32
    /** @var array<string> */
33
    private static $allowed_actions = ['index', 'similar'];
34
35
36
    public function similar(): \SilverStripe\View\ViewableData_Customised
37
    {
38
         /** @var \Suilven\FreeTextSearch\Page\SearchPage $model */
39
        $model = SearchPage::get_by_id(SearchPage::class, $this->ID);
40
41
        $indexes = new Indexes();
42
        $index = $indexes->getIndex($model->IndexToSearch);
43
44
        /** @var string $clazz */
45
        $clazz = $index->getClass();
46
47
        $dataObjectID = $this->getRequest()->param('ID');
48
        $dataObjectID = \intval($dataObjectID);
49
50
51
        $objectInContext = DataObject::get_by_id($clazz, $dataObjectID);
52
53
        $factory = new SearcherFactory();
54
        $searcher = $factory->getSearcher();
55
        $searcher->setIndexName($index->getName());
56
        $this->paginateSearcher($searcher);
57
58
        $results = $searcher->searchForSimilar($objectInContext);
59
60
        // tweak results set for rendering purposes, we do not want all the OR constructs
61
        $results->setQuery('');
62
        $results->setSimilarTo($objectInContext);
63
64
        return $this->renderSearchResults($model, $results);
65
    }
66
67
68
    public function index(): \SilverStripe\View\ViewableData_Customised
69
    {
70
        // @todo search indexes addition
71
        $q = $this->getRequest()->getVar('q');
72
73
        $this->selected = $this->getRequest()->getVars();
74
75
        /** @var \Suilven\FreeTextSearch\Page\SearchPage $model */
76
        $model = SearchPage::get_by_id(SearchPage::class, $this->ID);
77
78
        $results = new SearchResults();
79
80
        if (isset($q) || $model->PageLandingMode !== 'DoNothing' || isset($this->selected['q'])) {
81
            $results = $this->performSearchIncludingFacets($model, $q);
82
83
            if ($model->PageLandingMode === 'TagCloud') {
84
                $this->buildTagCloud();
85
            }
86
        }
87
88
        unset($this->selected['q']);
89
        unset($this->selected['start']);
90
        unset($this->selected['flush']);
91
92
        return $this->renderSearchResults($model, $results);
93
    }
94
95
96
    public function performSearchIncludingFacets(SearchPage $searchPage, ?string $q): SearchResults
97
    {
98
        $factory = new SearcherFactory();
99
100
        /** @var \Suilven\FreeTextSearch\Interfaces\Searcher $searcher */
101
        $searcher = $factory->getSearcher();
102
        $searcher->setFilters($this->selected);
103
        $searcher->setIndexName($searchPage->IndexToSearch);
104
105
        $facets = $searchPage->getFacetFields();
106
        $hasManyFields = $searchPage->getHasManyFields();
107
108
        // @todo ShutterSpeed breaks, no idea why
109
110
        $searcher->setFacettedTokens($facets);
111
        $searcher->setHasManyTokens($hasManyFields);
112
        $this->paginateSearcher($searcher);
113
114
        return $searcher->search($q);
115
    }
116
117
118
    private function buildTagCloud(): void
119
    {
120
        /*
121
122
        $facetted = isset($results['AllFacets']);
123
124
125
126
        $targetFacet = new ArrayList();
127
        if (isset($model->ShowTagCloudFor)) {
128
            // get the tag cloud from calculated facets, but if not calculated, ie the arrive on the page case,
129
            // calculate them
130
            if ($facetted) {
131
                $facets = $results['AllFacets'];
132
            } else {
133
                $proxyResults = $this->performSearchIncludingFacets($selected, $model, $q);
134
                $facets = $proxyResults['AllFacets'];
135
            }
136
137
            foreach ($facets as $facet) {
138
                $name = $facet->getField('Name');
139
                if ($name === $model->ShowTagCloudFor) {
140
                    $targetFacet = $facet->getField('Facets');
141
142
                    break;
143
                }
144
            }
145
146
            $facetArray = $targetFacet->toArray();
147
            $minSize = 10;
148
            $maxSize = 40;
149
            $maxCount = 0;
150
            foreach ($facetArray as $tag) {
151
                $count = $tag['Count'];
152
                $maxCount = $count > $maxCount
153
                    ? $count
154
                    : $maxCount;
155
            }
156
157
            $tagCloud = new ArrayList();
158
            foreach ($facetArray as $tag) {
159
                $size = $minSize + ($maxSize - $minSize) * $tag['Count'] / $maxCount;
160
                $size = \round($size);
161
                $row = new ArrayData([
162
                    'Name' => $tag['Value'],
163
                    'Size' => $size,
164
                    'Params' => $tag['Params'],
165
                ]);
166
                $tagCloud->push($row);
167
            }
168
169
            $results['TagCloud'] = $tagCloud;
170
        }
171
172
173
        //for($i = 3; $i < 40; $i++) {
174
           // echo "li.tag{$i} { font-size: {$i}px;};\n";
175
        //}
176
177
        */
178
    }
179
180
181
    /** @throws \Exception */
182
    private function renderSearchResults(
183
        SearchPage $model,
184
        SearchResults $results
185
    ): \SilverStripe\View\ViewableData_Customised {
186
        $indexes = new Indexes();
187
        $index = $indexes->getIndex($model->IndexToSearch);
188
189
        $hasManyFieldsDetails = $index->getHasManyFields();
190
        $hasManyFieldsNames = \array_keys($hasManyFieldsDetails);
191
192
        /** @var string $clazz */
193
        $clazz = $index->getClass();
194
195
        $templateName = 'Suilven/FreeTextSearch/' . \str_replace('\\', '/', $clazz);
196
        $splits = \explode('/', $templateName);
197
        $last = \array_pop($splits);
198
        $templateName = \implode('/', $splits) . '/Includes/' . $last;
199
200
201
        $records = $results->getRecords();
202
203
        $newRecords = new ArrayList();
204
        foreach ($records as $record) {
205
            $highsList = new ArrayList();
206
            $highlightsArray = $record->Highlights;
207
208
            if (isset($highlightsArray['Title'])) {
209
                $record->ResultTitle = $highlightsArray['Title'][0];
210
                unset($highlightsArray['Title']);
211
            }
212
213
            $record->HighlightedLink = $record->Link;
214
            if (isset($highlightsArray['Link']) && \count($highlightsArray['Link']) > 0) {
215
                $record->HighlightedLink = $highlightsArray['Link'][0];
216
                unset($highlightsArray['Link']);
217
            }
218
219
            // this simply repeats the title most times
220
            unset($highlightsArray['MenuTitle']);
221
222
            $keys = \is_null($highlightsArray)
223
                ? []
224
                : \array_keys($highlightsArray);
225
            foreach ($keys as $highlightedField) {
226
                foreach ($highlightsArray[$highlightedField] as $highlightsForField) {
227
                    $do = new DataObject();
228
                    // @phpstan-ignore-next-line
229
                    $do->Snippet = '...' . $highlightsForField . '...';
230
231
                    $highsList->push($do);
232
                }
233
            }
234
235
            $record->Highlights = $highsList;
236
237
            $html = $this->renderWith(
238
                [
239
                    $templateName,
240
                    'Suilven/FreeTextSearch/SilverStripe/CMS/Model/Includes/SiteTree',
241
                ],
242
                [
243
                    'Record' => $record,
244
                    'SimilarLink' => $this->Link('similar') . '/' . $record->ID,
245
                ]
246
            );
247
            $record->HTML = $html;
248
            $newRecords->push($record);
249
        }
250
251
        $paginatedList = new PaginatedList($records);
252
        $paginatedList->setLimitItems(false);
253
        $paginatedList->setPageLength($results->getPageSize());
254
        $paginatedList->setTotalItems($results->getTotaNumberOfResults());
255
        $paginatedList->setCurrentPage($results->getPage());
256
257
258
        $facets = $results->getFacets();
259
        $selectedFacetNames = \array_keys($this->selected);
260
        $displayFacets = new ArrayList();
261
262
        $helper = new FacetLinkHelper();
263
264
265
        /** @var \Suilven\FreeTextSearch\Container\Facet $facet */
266
        foreach ($facets as $facet) {
267
            $displayFacet = new DataObject();
268
            $facetName= $facet->getName();
269
270
            /** @phpstan-ignore-next-line */
271
            $displayFacet->Name = $facetName;
272
            $helper->setFacetInContext($facetName);
273
            $isHasManyFacet = \in_array($facetName, $hasManyFieldsNames, true);
274
            $isSelectedFacet = \in_array($facetName, $selectedFacetNames, true);
275
276
277
            $counts = new ArrayList();
278
            /** @var \Suilven\FreeTextSearch\Container\FacetCount $facetCount */
279
            foreach ($facet->getFacetCounts() as $facetCount) {
280
                // @todo Make this an object
281
                $count = new DataObject();
282
                $key = $facetCount->getKey();
283
284
                /** @phpstan-ignore-next-line */
285
                $count->Key = $key;
286
287
                /** @phpstan-ignore-next-line */
288
                $count->Count = $facetCount->getCount();
289
                $link = $helper->isSelectedFacet($key) ? null: $helper->getDrillDownFacetLink(
290
                    $model->Link(),
291
                    $count->Key
292
                );
293
294
                $clearFacetLink = $helper->isSelectedFacet($key)
295
                    ? $helper->getClearFacetLink($model->Link(), $facet->getName())
296
                    : null;
297
298
                // @phpstan-ignore-next-line
299
                $count->Link = $link;
300
301
                // @phpstan-ignore-next-line
302
                $count->ClearFacetLink = $clearFacetLink;
303
304
                // @phpstan-ignore-next-line
305
                $count->IsSelected = $isSelectedFacet;
306
307
                // @phpstan-ignore-next-line
308
                $count->KeySelected = $helper->isSelectedFacet($key);
309
310
311
                // decide whether or not to show this facet count
312
                if ($isHasManyFacet) {
313
                    if ($isSelectedFacet && !\is_null($count->ClearFacetLink)) {
314
                        $counts->push($count);
315
                    } elseif (!$isSelectedFacet && \is_null($count->ClearFacetLink)) {
316
                        $counts->push($count);
317
                    }
318
                } else {
319
                    // token facets
320
                    $counts->push($count);
321
                }
322
            }
323
324
            // @phpstan-ignore-next-line
325
            $displayFacet->FacetCounts = $counts;
326
327
            $displayFacets->push($displayFacet);
328
        }
329
330
        return $this->customise(new ArrayData([
331
            'NumberOfResults' => $results->getTotaNumberOfResults(),
332
            'Query' => $results->getQuery(),
333
            'Records' => $newRecords,
334
            'Page' => $results->getPage(),
335
            'PageSize' => $results->getPageSize(),
336
            'Pages' => $results->getTotalPages(),
337
            'Suggestions' => new ArrayList($results->getSuggestions()),
338
            'Time' => $results->getTime(),
339
            'Pagination' => $paginatedList,
340
            'SimilarTo' => $results->getSimilarTo(),
341
            'Facets' => $displayFacets,
342
        ]));
343
    }
344
345
346
    private function paginateSearcher(\Suilven\FreeTextSearch\Interfaces\Searcher &$searcher): void
347
    {
348
        $searcher->setPageSize($this->PageSize);
349
        $start = $this->getRequest()->getVar('start');
350
351
352
        // page 1 is the first page
353
        $page = isset($start)
354
            ? \ceil($start / $this->PageSize) + 1
355
            : 1;
356
357
        $page = \intval($page);
358
        $searcher->setPage($page);
359
    }
360
}
361