Passed
Push — master ( 00a79d...f5402f )
by Greg
05:33
created

SearchGeneralPage::handle()   F

Complexity

Conditions 30
Paths 1320

Size

Total Lines 107
Code Lines 64

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 30
eloc 64
c 1
b 0
f 0
nc 1320
nop 1
dl 0
loc 107
rs 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
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2019 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Http\RequestHandlers;
21
22
use Fisharebest\Webtrees\Http\ViewResponseTrait;
23
use Fisharebest\Webtrees\I18N;
24
use Fisharebest\Webtrees\Services\SearchService;
25
use Fisharebest\Webtrees\Services\TreeService;
26
use Fisharebest\Webtrees\Site;
27
use Fisharebest\Webtrees\Tree;
28
use Illuminate\Support\Collection;
29
use InvalidArgumentException;
30
use Psr\Http\Message\ResponseInterface;
31
use Psr\Http\Message\ServerRequestInterface;
32
use Psr\Http\Server\RequestHandlerInterface;
33
34
use function assert;
35
use function preg_match;
36
use function str_replace;
37
use function trim;
38
39
/**
40
 * Search for genealogy data
41
 */
42
class SearchGeneralPage implements RequestHandlerInterface
43
{
44
    use ViewResponseTrait;
45
46
    /** @var SearchService */
47
    private $search_service;
48
49
    /** @var TreeService */
50
    private $tree_service;
51
52
    /**
53
     * SearchController constructor.
54
     *
55
     * @param SearchService $search_service
56
     * @param TreeService   $tree_service
57
     */
58
    public function __construct(SearchService $search_service, TreeService $tree_service)
59
    {
60
        $this->search_service = $search_service;
61
        $this->tree_service   = $tree_service;
62
    }
63
64
    /**
65
     * The standard search.
66
     *
67
     * @param ServerRequestInterface $request
68
     *
69
     * @return ResponseInterface
70
     */
71
    public function handle(ServerRequestInterface $request): ResponseInterface
72
    {
73
        $tree = $request->getAttribute('tree');
74
        assert($tree instanceof Tree, new InvalidArgumentException());
75
76
        $params = $request->getQueryParams();
77
        $query  = $params['query'] ?? '';
78
79
        // What type of records to search?
80
        $search_individuals  = (bool) ($params['search_individuals'] ?? false);
81
        $search_families     = (bool) ($params['search_families'] ?? false);
82
        $search_repositories = (bool) ($params['search_repositories'] ?? false);
83
        $search_sources      = (bool) ($params['search_sources'] ?? false);
84
        $search_notes        = (bool) ($params['search_notes'] ?? false);
85
86
        // Default to individuals only
87
        if (!$search_individuals && !$search_families && !$search_repositories && !$search_sources && !$search_notes) {
88
            $search_individuals = true;
89
        }
90
91
        // What to search for?
92
        $search_terms = $this->extractSearchTerms($query);
93
94
        // What trees to seach?
95
        if (Site::getPreference('ALLOW_CHANGE_GEDCOM') === '1') {
96
            $all_trees = $this->tree_service->all()->all();
97
        } else {
98
            $all_trees = [$tree];
99
        }
100
101
        $search_tree_names = $params['search_trees'] ?? [];
102
103
        $search_trees = array_filter($all_trees, static function (Tree $tree) use ($search_tree_names): bool {
104
            return in_array($tree->name(), $search_tree_names, true);
105
        });
106
107
        if ($search_trees === []) {
108
            $search_trees = [$tree];
109
        }
110
111
        // Do the search
112
        $individuals  = new Collection();
113
        $families     = new Collection();
114
        $repositories = new Collection();
115
        $sources      = new Collection();
116
        $notes        = new Collection();
117
118
        if ($search_terms !== []) {
119
            if ($search_individuals) {
120
                $individuals = $this->search_service->searchIndividuals($search_trees, $search_terms);
121
            }
122
123
            if ($search_families) {
124
                $tmp1 = $this->search_service->searchFamilies($search_trees, $search_terms);
125
                $tmp2 = $this->search_service->searchFamilyNames($search_trees, $search_terms);
126
127
                $families = $tmp1->merge($tmp2)->unique();
128
            }
129
130
            if ($search_repositories) {
131
                $repositories = $this->search_service->searchRepositories($search_trees, $search_terms);
132
            }
133
134
            if ($search_sources) {
135
                $sources = $this->search_service->searchSources($search_trees, $search_terms);
136
            }
137
138
            if ($search_notes) {
139
                $notes = $this->search_service->searchNotes($search_trees, $search_terms);
140
            }
141
        }
142
143
        // If only 1 item is returned, automatically forward to that item
144
        if ($individuals->count() === 1 && $families->isEmpty() && $sources->isEmpty() && $notes->isEmpty()) {
145
            return redirect($individuals->first()->url());
146
        }
147
148
        if ($individuals->isEmpty() && $families->count() === 1 && $sources->isEmpty() && $notes->isEmpty()) {
149
            return redirect($families->first()->url());
150
        }
151
152
        if ($individuals->isEmpty() && $families->isEmpty() && $sources->count() === 1 && $notes->isEmpty()) {
153
            return redirect($sources->first()->url());
154
        }
155
156
        if ($individuals->isEmpty() && $families->isEmpty() && $sources->isEmpty() && $notes->count() === 1) {
157
            return redirect($notes->first()->url());
158
        }
159
160
        $title = I18N::translate('General search');
161
162
        return $this->viewResponse('search-general-page', [
163
            'all_trees'           => $all_trees,
164
            'families'            => $families,
165
            'individuals'         => $individuals,
166
            'notes'               => $notes,
167
            'query'               => $query,
168
            'repositories'        => $repositories,
169
            'search_families'     => $search_families,
170
            'search_individuals'  => $search_individuals,
171
            'search_notes'        => $search_notes,
172
            'search_repositories' => $search_repositories,
173
            'search_sources'      => $search_sources,
174
            'search_trees'        => $search_trees,
175
            'sources'             => $sources,
176
            'title'               => $title,
177
            'tree'                => $tree,
178
        ]);
179
    }
180
181
    /**
182
     * Convert the query into an array of search terms
183
     *
184
     * @param string $query
185
     *
186
     * @return string[]
187
     */
188
    private function extractSearchTerms(string $query): array
189
    {
190
        $search_terms = [];
191
192
        // Words in double quotes stay together
193
        while (preg_match('/"([^"]+)"/', $query, $match)) {
194
            $search_terms[] = trim($match[1]);
195
            $query          = str_replace($match[0], '', $query);
196
        }
197
198
        // Other words get treated separately
199
        while (preg_match('/[\S]+/', $query, $match)) {
200
            $search_terms[] = trim($match[0]);
201
            $query          = str_replace($match[0], '', $query);
202
        }
203
204
        return $search_terms;
205
    }
206
}
207