SearchController   B
last analyzed

Complexity

Total Complexity 47

Size/Duplication

Total Lines 221
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 47
eloc 145
c 0
b 0
f 0
dl 0
loc 221
rs 8.64

2 Methods

Rating   Name   Duplication   Size   Complexity  
F search() 0 199 46
A __construct() 0 9 1

How to fix   Complexity   

Complex Class

Complex classes like SearchController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SearchController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace App\Http\Controllers;
4
5
use App\Models\Category;
6
use App\Models\UsenetGroup;
7
use App\Services\Releases\ReleaseBrowseService;
8
use App\Services\Releases\ReleaseSearchService;
9
use App\Services\Search\Contracts\SearchServiceInterface;
10
use Illuminate\Http\Request;
11
12
class SearchController extends BasePageController
13
{
14
    private SearchServiceInterface $searchService;
15
16
    private ReleaseSearchService $releaseSearchService;
17
18
    private ReleaseBrowseService $releaseBrowseService;
19
20
    public function __construct(
21
        SearchServiceInterface $searchService,
22
        ReleaseSearchService $releaseSearchService,
23
        ReleaseBrowseService $releaseBrowseService
24
    ) {
25
        parent::__construct();
26
        $this->searchService = $searchService;
27
        $this->releaseSearchService = $releaseSearchService;
28
        $this->releaseBrowseService = $releaseBrowseService;
29
    }
30
31
    /**
32
     * @throws \Exception
33
     */
34
    public function search(Request $request)
35
    {
36
37
        $results = [];
38
39
        $searchType = 'basic';
40
        if ($request->has('search_type') && $request->input('search_type') === 'adv') {
41
            $searchType = 'advanced';
42
        }
43
44
        $ordering = $this->releaseBrowseService->getBrowseOrdering();
45
        $orderBy = ($request->has('ob') && \in_array($request->input('ob'), $ordering, false) ? $request->input('ob') : '');
46
        $page = $request->has('page') && is_numeric($request->input('page')) ? $request->input('page') : 1;
47
        $offset = ($page - 1) * config('nntmux.items_per_page');
48
49
        $subject = '';
50
        $search = '';
51
        $id = '';
52
        $category = [0];
53
        $lastvisit = $this->userdata->lastlogin;
54
55
        if ($searchType === 'basic' && $request->missing('searchadvr') && ($request->has('id') || $request->has('subject') || $request->has('search'))) {
56
            $searchString = [];
57
            switch (true) {
58
                case $request->filled('subject'):
59
                    $searchString['searchname'] = (string) $request->input('subject') ?? [];
60
                    $subject = $searchString['searchname'];
61
                    break;
62
                case $request->filled('id'):
63
                    $searchString['searchname'] = (string) $request->input('id') ?? [];
64
                    $id = $searchString['searchname'];
65
                    break;
66
                case $request->filled('search'):
67
                    $searchString['searchname'] = (string) $request->input('search') ?? [];
68
                    $search = $searchString['searchname'];
69
                    break;
70
                default:
71
                    $searchString['searchname'] = '';
72
            }
73
74
            $categoryID = [-1];
75
            if ($request->has('t')) {
76
                $categoryID = array_map('intval', explode(',', $request->input('t')));
77
            }
78
79
            $orderByUrls = [];
80
            foreach ($this->releaseBrowseService->getBrowseOrdering() as $orderType) {
81
                $orderByUrls['orderby'.$orderType] = url('/search?search='.htmlentities($searchString['searchname'], ENT_QUOTES | ENT_HTML5).'&t='.implode(',', $categoryID).'&ob='.$orderType);
82
            }
83
84
            $rslt = $this->releaseSearchService->search(
85
                $searchString,
86
                -1,
87
                -1,
88
                -1,
89
                -1,
90
                -1,
91
                $offset,
92
                config('nntmux.items_per_page'),
93
                $orderBy,
94
                -1,
95
                $this->userdata->categoryexclusions ?? [],
96
                'basic',
97
                $categoryID);
98
99
            $results = $this->paginate($rslt ?? [], $rslt[0]->_totalrows ?? 0, config('nntmux.items_per_page'), $page, $request->url(), $request->query());
100
            $category = $categoryID;
101
        } else {
102
            $orderByUrls = [];
103
        }
104
105
        $searchVars = [
106
            'searchadvr' => '',
107
            'searchadvsubject' => '',
108
            'searchadvposter' => '',
109
            'searchadvfilename' => '',
110
            'searchadvdaysnew' => '',
111
            'searchadvdaysold' => '',
112
            'searchadvgroups' => '',
113
            'searchadvcat' => '',
114
            'searchadvsizefrom' => '',
115
            'searchadvsizeto' => '',
116
            'searchadvhasnfo' => '',
117
            'searchadvhascomments' => '',
118
        ];
119
120
        foreach ($searchVars as $searchVarKey => $searchVar) {
121
            $searchVars[$searchVarKey] = ($request->has($searchVarKey) ? (string) $request->input($searchVarKey) : '');
122
        }
123
124
        // Map new form field names to old internal names
125
        if ($request->has('minage')) {
126
            $searchVars['searchadvdaysnew'] = (string) $request->input('minage');
127
        }
128
        if ($request->has('maxage')) {
129
            $searchVars['searchadvdaysold'] = (string) $request->input('maxage');
130
        }
131
        if ($request->has('group')) {
132
            $searchVars['searchadvgroups'] = (string) $request->input('group');
133
        }
134
        if ($request->has('minsize')) {
135
            $searchVars['searchadvsizefrom'] = (string) $request->input('minsize');
136
        }
137
        if ($request->has('maxsize')) {
138
            $searchVars['searchadvsizeto'] = (string) $request->input('maxsize');
139
        }
140
        // Map basic search field to advanced search when in advanced mode
141
        if ($request->has('search') && $searchType === 'advanced') {
142
            $searchVars['searchadvr'] = (string) $request->input('search');
143
        }
144
        // Map basic category field to advanced category when in advanced mode
145
        if ($request->has('t') && $searchType === 'advanced') {
146
            $searchVars['searchadvcat'] = (string) $request->input('t');
147
        }
148
149
        $searchVars['selectedgroup'] = $searchVars['searchadvgroups'];
150
        $searchVars['selectedcat'] = $searchVars['searchadvcat'];
151
        $searchVars['selectedsizefrom'] = $searchVars['searchadvsizefrom'];
152
        $searchVars['selectedsizeto'] = $searchVars['searchadvsizeto'];
153
154
        // Get spell correction suggestions if we have a search query but few/no results
155
        $spellSuggestion = null;
156
        $searchQuery = $search ?: ($searchVars['searchadvr'] ?? '');
157
        if (! empty($searchQuery) && $this->searchService->isSuggestEnabled()) {
158
            // Get suggestions from search service
159
            $suggestions = $this->searchService->suggest($searchQuery);
160
            if (! empty($suggestions)) {
161
                // Sort by doc count descending to get best suggestion
162
                usort($suggestions, fn ($a, $b) => $b['docs'] - $a['docs']);
163
                // Only show suggestion if it's different from the query
164
                if ($suggestions[0]['suggest'] !== $searchQuery) {
165
                    $spellSuggestion = $suggestions[0]['suggest'];
166
                }
167
            }
168
        }
169
170
        if ($searchType !== 'basic' && $request->missing('id') && $request->missing('subject') && $request->anyFilled(['searchadvr', 'searchadvsubject', 'searchadvfilename', 'searchadvposter', 'minage', 'maxage', 'group', 'minsize', 'maxsize', 'search'])) {
171
            $orderByString = '';
172
            foreach ($searchVars as $searchVarKey => $searchVar) {
173
                $orderByString .= "&$searchVarKey=".htmlentities($searchVar, ENT_QUOTES | ENT_HTML5);
174
            }
175
            $orderByString = ltrim($orderByString, '&');
176
177
            $orderByUrls = [];
178
            foreach ($ordering as $orderType) {
179
                $orderByUrls['orderby'.$orderType] = url('/search?'.$orderByString.'&search_type=adv&ob='.$orderType);
180
            }
181
182
            $searchArr = [
183
                'searchname' => $searchVars['searchadvr'] === '' ? -1 : $searchVars['searchadvr'],
184
                'name' => $searchVars['searchadvsubject'] === '' ? -1 : $searchVars['searchadvsubject'],
185
                'fromname' => $searchVars['searchadvposter'] === '' ? -1 : $searchVars['searchadvposter'],
186
                'filename' => $searchVars['searchadvfilename'] === '' ? -1 : $searchVars['searchadvfilename'],
187
            ];
188
189
            $rslt = $this->releaseSearchService->search(
190
                $searchArr,
191
                $searchVars['searchadvgroups'],
192
                $searchVars['searchadvsizefrom'],
193
                $searchVars['searchadvsizeto'],
194
                ($searchVars['searchadvdaysnew'] === '' ? -1 : $searchVars['searchadvdaysnew']),
195
                ($searchVars['searchadvdaysold'] === '' ? -1 : $searchVars['searchadvdaysold']),
196
                $offset,
197
                config('nntmux.items_per_page'),
198
                $orderBy,
199
                -1,
200
                $this->userdata->categoryexclusions ?? [],
201
                'advanced',
202
                [$searchVars['searchadvcat'] === '' ? -1 : $searchVars['searchadvcat']]
203
            );
204
205
            $results = $this->paginate($rslt ?? [], $rslt[0]->_totalrows ?? 0, config('nntmux.items_per_page'), $page, $request->url(), $request->query());
206
        }
207
208
        $this->viewData = array_merge($this->viewData, $searchVars, $orderByUrls, [
209
            'subject' => $subject,
210
            'search' => $search,
211
            'id' => $id,
212
            'category' => $category,
213
            'covgroup' => '',
214
            'lastvisit' => $lastvisit,
215
            'sizelist' => [
216
                -1 => '--Select--', 1 => '100MB', 2 => '250MB', 3 => '500MB', 4 => '1GB', 5 => '2GB',
217
                6 => '3GB', 7 => '4GB', 8 => '8GB', 9 => '16GB', 10 => '32GB', 11 => '64GB',
218
            ],
219
            'results' => $results,
220
            'sadvanced' => $searchType !== 'basic',
221
            'grouplist' => UsenetGroup::getGroupsForSelect(),
222
            'catlist' => Category::getForSelect(),
223
            'meta_title' => 'Search Nzbs',
224
            'meta_keywords' => 'search,nzb,description,details',
225
            'meta_description' => 'Search for Nzbs',
226
            // Search enhanced features
227
            'spellSuggestion' => $spellSuggestion,
228
            'autocompleteEnabled' => $this->searchService->isAutocompleteEnabled(),
229
            'suggestEnabled' => $this->searchService->isSuggestEnabled(),
230
        ]);
231
232
        return view('search.index', $this->viewData);
233
    }
234
}
235