Livesuggest::parsePagination()   F
last analyzed

Complexity

Conditions 27
Paths 3032

Size

Total Lines 153
Code Lines 79

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 756

Importance

Changes 0
Metric Value
cc 27
eloc 79
nc 3032
nop 0
dl 0
loc 153
ccs 0
cts 99
cp 0
crap 756
rs 0
c 0
b 0
f 0

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
2
3
namespace Frontend\Modules\Search\Ajax;
4
5
use DateInterval;
6
use Psr\Cache\CacheItemPoolInterface;
7
use Frontend\Core\Engine\Base\AjaxAction as FrontendBaseAJAXAction;
8
use Frontend\Core\Engine\Exception as FrontendException;
9
use Frontend\Core\Engine\Navigation as FrontendNavigation;
10
use Frontend\Core\Engine\Theme;
11
use Frontend\Core\Engine\TwigTemplate;
12
use Frontend\Modules\Search\Engine\Model as FrontendSearchModel;
13
use Symfony\Component\HttpFoundation\Response;
14
15
/**
16
 * This is the live suggest-action, it will output a list of results for a certain search
17
 */
18
class Livesuggest extends FrontendBaseAJAXAction
19
{
20
    /** @var array */
21
    private $searchResults;
22
23
    /** @var int */
24
    private $limit;
25
26
    /** @var int */
27
    private $offset;
28
29
    /** @var int */
30
    private $requestedPage;
31
32
    /** @var array */
33
    private $pagination;
34
35
    /** @var string */
36
    private $searchTerm = '';
37
38
    /** @var CacheItemPoolInterface */
39
    private $cache;
40
41
    /** @var string */
42
    private $cacheKey;
43
44
    /** @var TwigTemplate */
45
    private $template;
46
47
    private function display(): void
48
    {
49
        $this->requestedPage = 1;
50
        $this->limit = (int) $this->get('fork.settings')->get('Search', 'overview_num_items', 20);
51
        $this->offset = ($this->requestedPage * $this->limit) - $this->limit;
52
        $this->cache = $this->get('cache.search');
53
        $this->cacheKey = implode(
54
            '_',
55
            [$this->getModule(), LANGUAGE, md5($this->searchTerm), $this->offset, $this->limit]
56
        );
57
58
        if (!$this->getCachedData()) {
59
            // no valid cache so we get fresh data
60
            $this->getRealData();
61
        }
62
63
        $this->parse();
64
65
        $this->output(
66
            Response::HTTP_OK,
67
            $this->template->render(
68
                '/Search/Layout/Templates/Results.html.twig',
69
                $this->template->getAssignedVariables()
0 ignored issues
show
Bug introduced by
The method getAssignedVariables() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

69
                $this->template->/** @scrutinizer ignore-call */ 
70
                                 getAssignedVariables()

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
70
            )
71
        );
72
    }
73
74
    public function execute(): void
75
    {
76
        parent::execute();
77
        $this->validateForm();
78
79
        if ($this->searchTerm === '') {
80
            $this->output(Response::HTTP_BAD_REQUEST, null, 'term-parameter is missing.');
81
82
            return;
83
        }
84
85
        $this->display();
86
    }
87
88
    private function getCachedData(): bool
89
    {
90
        if (!$this->searchTerm || $this->getContainer()->getParameter('kernel.debug')) {
91
            return false;
92
        }
93
94
        $cacheItem = $this->cache->getItem($this->cacheKey);
95
        if (!$cacheItem->isHit()) {
96
            return false;
97
        }
98
99
        ['pagination' => $this->pagination, 'items' => $this->searchResults] = $cacheItem->get();
100
101
        return true;
102
    }
103
104
    private function getRealData(): void
105
    {
106
        if (!$this->searchTerm) {
107
            return;
108
        }
109
110
        $this->searchResults = FrontendSearchModel::search($this->searchTerm, $this->limit, $this->offset);
111
112
        // populate count fields in pagination
113
        // this is done after actual search because some items might be
114
        // activated/deactivated (getTotal only does rough checking)
115
        $numberOfItems = FrontendSearchModel::getTotal($this->searchTerm);
116
        $this->pagination = [
117
            'url' => FrontendNavigation::getUrlForBlock('Search') . '?form=search&q=' . $this->searchTerm,
118
            'limit' => $this->limit,
119
            'offset' => $this->offset,
120
            'requested_page' => $this->requestedPage,
121
            'num_items' => FrontendSearchModel::getTotal($this->searchTerm),
122
            'num_pages' => (int) ceil($numberOfItems / $this->limit)
123
        ];
124
125
        // num pages is always equal to at least 1
126
        if ($this->pagination['num_pages'] === 0) {
127
            $this->pagination['num_pages'] = 1;
128
        }
129
130
        // Don't save the result in the cache when debug is enabled
131
        if ($this->getContainer()->getParameter('kernel.debug')) {
132
            return;
133
        }
134
135
        $cacheItem = $this->cache->getItem($this->cacheKey);
136
        $cacheItem->expiresAfter(new DateInterval('PT1H'));
137
        $cacheItem->set(['pagination' => $this->pagination, 'items' => $this->searchResults]);
138
        $this->cache->save($cacheItem);
139
    }
140
141
    private function parse(): void
142
    {
143
        $this->template = $this->get('templating');
144
145
        if (!$this->searchTerm) {
146
            return;
147
        }
148
149
        $this->template->assign('searchResults', $this->searchResults);
150
        $this->template->assign('searchTerm', $this->searchTerm);
151
152
        $this->parsePagination();
153
    }
154
155
    private function parsePagination(): void
156
    {
157
        // init var
158
        $pagination = [];
159
        $showFirstPages = false;
160
        $showLastPages = false;
161
        $useQuestionMark = true;
162
163
        // validate pagination array
164
        switch (true) {
165
            case (!isset($this->pagination['limit'])):
166
                throw new FrontendException('no limit in the pagination-property.');
167
            case (!isset($this->pagination['offset'])):
168
                throw new FrontendException('no offset in the pagination-property.');
169
            case (!isset($this->pagination['requested_page'])):
170
                throw new FrontendException('no requested_page available in the pagination-property.');
171
            case (!isset($this->pagination['num_items'])):
172
                throw new FrontendException('no num_items available in the pagination-property.');
173
            case (!isset($this->pagination['num_pages'])):
174
                throw new FrontendException('no num_pages available in the pagination-property.');
175
            case (!isset($this->pagination['url'])):
176
                throw new FrontendException('no url available in the pagination-property.');
177
        }
178
179
        // should we use a questionmark or an ampersand
180
        if (mb_strpos($this->pagination['url'], '?') !== false) {
181
            $useQuestionMark = false;
182
        }
183
184
        // no pagination needed
185
        if ($this->pagination['num_pages'] < 1) {
186
            return;
187
        }
188
189
        // populate count fields
190
        $pagination['num_pages'] = $this->pagination['num_pages'];
191
        $pagination['current_page'] = $this->pagination['requested_page'];
192
193
        // as long as we are below page 5 we should show all pages starting from 1
194
        if ($this->pagination['requested_page'] < 6) {
195
            $pagesStart = 1;
196
            $pagesEnd = ($this->pagination['num_pages'] >= 6) ? 6 : $this->pagination['num_pages'];
197
198
            // show last pages
199
            if ($this->pagination['num_pages'] > 5) {
200
                $showLastPages = true;
201
            }
202
        } elseif ($this->pagination['requested_page'] >= ($this->pagination['num_pages'] - 4)) {
203
            // as long as we are 5 pages from the end we should show all pages till the end
204
            $pagesStart = ($this->pagination['num_pages'] - 5);
205
            $pagesEnd = $this->pagination['num_pages'];
206
207
            // show first pages
208
            if ($this->pagination['num_pages'] > 5) {
209
                $showFirstPages = true;
210
            }
211
        } else {
212
            // page 7
213
            $pagesStart = $this->pagination['requested_page'] - 2;
214
            $pagesEnd = $this->pagination['requested_page'] + 2;
215
            $showFirstPages = true;
216
            $showLastPages = true;
217
        }
218
219
        // show previous
220
        if ($this->pagination['requested_page'] > 1) {
221
            // build URL
222
            if ($useQuestionMark) {
223
                $url = $this->pagination['url'] . '?page=' . ($this->pagination['requested_page'] - 1);
224
            } else {
225
                $url = $this->pagination['url'] . '&page=' . ($this->pagination['requested_page'] - 1);
226
            }
227
228
            // set
229
            $pagination['show_previous'] = true;
230
            $pagination['previous_url'] = $url;
231
        }
232
233
        // show first pages?
234
        if ($showFirstPages) {
235
            // init var
236
            $pagesFirstStart = 1;
237
            $pagesFirstEnd = 1;
238
239
            // loop pages
240
            for ($i = $pagesFirstStart; $i <= $pagesFirstEnd; ++$i) {
241
                // build URL
242
                if ($useQuestionMark) {
243
                    $url = $this->pagination['url'] . '?page=' . $i;
244
                } else {
245
                    $url = $this->pagination['url'] . '&page=' . $i;
246
                }
247
248
                // add
249
                $pagination['first'][] = ['url' => $url, 'label' => $i];
250
            }
251
        }
252
253
        // build array
254
        for ($i = $pagesStart; $i <= $pagesEnd; ++$i) {
255
            // init var
256
            $current = ($i == $this->pagination['requested_page']);
257
258
            // build URL
259
            if ($useQuestionMark) {
260
                $url = $this->pagination['url'] . '?page=' . $i;
261
            } else {
262
                $url = $this->pagination['url'] . '&page=' . $i;
263
            }
264
265
            // add
266
            $pagination['pages'][] = ['url' => $url, 'label' => $i, 'current' => $current];
267
        }
268
269
        // show last pages?
270
        if ($showLastPages) {
271
            // init var
272
            $pagesLastStart = $this->pagination['num_pages'];
273
            $pagesLastEnd = $this->pagination['num_pages'];
274
275
            // loop pages
276
            for ($i = $pagesLastStart; $i <= $pagesLastEnd; ++$i) {
277
                // build URL
278
                if ($useQuestionMark) {
279
                    $url = $this->pagination['url'] . '?page=' . $i;
280
                } else {
281
                    $url = $this->pagination['url'] . '&page=' . $i;
282
                }
283
284
                // add
285
                $pagination['last'][] = ['url' => $url, 'label' => $i];
286
            }
287
        }
288
289
        // show next
290
        if ($this->pagination['requested_page'] < $this->pagination['num_pages']) {
291
            // build URL
292
            if ($useQuestionMark) {
293
                $url = $this->pagination['url'] . '?page=' . ($this->pagination['requested_page'] + 1);
294
            } else {
295
                $url = $this->pagination['url'] . '&page=' . ($this->pagination['requested_page'] + 1);
296
            }
297
298
            // set
299
            $pagination['show_next'] = true;
300
            $pagination['next_url'] = $url;
301
        }
302
303
        // multiple pages
304
        $pagination['multiple_pages'] = ($pagination['num_pages'] == 1) ? false : true;
305
306
        // assign pagination
307
        $this->template->assign('pagination', $pagination);
308
    }
309
310
    private function validateForm(): void
311
    {
312
        $charset = $this->getContainer()->getParameter('kernel.charset');
313
        $searchTerm = $this->getRequest()->request->get('term', '');
314
        $this->searchTerm = ($charset === 'utf-8')
315
            ? \SpoonFilter::htmlspecialchars($searchTerm) : \SpoonFilter::htmlentities($searchTerm);
316
    }
317
}
318