xref: /webtrees/app/Http/RequestHandlers/SearchAdvancedPage.php (revision 009f366b781401d418fd04ab8ac2eb16daf18bf2)
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
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Http\RequestHandlers;
21
22use Fisharebest\Webtrees\GedcomTag;
23use Fisharebest\Webtrees\Http\ViewResponseTrait;
24use Fisharebest\Webtrees\I18N;
25use Fisharebest\Webtrees\Services\SearchService;
26use Fisharebest\Webtrees\Tree;
27use Psr\Http\Message\ResponseInterface;
28use Psr\Http\Message\ServerRequestInterface;
29use Psr\Http\Server\RequestHandlerInterface;
30
31/**
32 * Search for genealogy data
33 */
34class SearchAdvancedPage implements RequestHandlerInterface
35{
36    use ViewResponseTrait;
37
38    private const DEFAULT_ADVANCED_FIELDS = [
39        'NAME:GIVN',
40        'NAME:SURN',
41        'BIRT:DATE',
42        'BIRT:PLAC',
43        'FAMS:MARR:DATE',
44        'FAMS:MARR:PLAC',
45        'DEAT:DATE',
46        'DEAT:PLAC',
47        'FAMC:HUSB:NAME:GIVN',
48        'FAMC:HUSB:NAME:SURN',
49        'FAMC:WIFE:NAME:GIVN',
50        'FAMC:WIFE:NAME:SURN',
51    ];
52
53    private const OTHER_ADVANCED_FIELDS = [
54        'ADOP:DATE',
55        'ADOP:PLAC',
56        'AFN',
57        'BAPL:DATE',
58        'BAPL:PLAC',
59        'BAPM:DATE',
60        'BAPM:PLAC',
61        'BARM:DATE',
62        'BARM:PLAC',
63        'BASM:DATE',
64        'BASM:PLAC',
65        'BLES:DATE',
66        'BLES:PLAC',
67        'BURI:DATE',
68        'BURI:PLAC',
69        'CAST',
70        'CENS:DATE',
71        'CENS:PLAC',
72        'CHAN:DATE',
73        'CHAN:_WT_USER',
74        'CHR:DATE',
75        'CHR:PLAC',
76        'CREM:DATE',
77        'CREM:PLAC',
78        'DSCR',
79        'EMAIL',
80        'EMIG:DATE',
81        'EMIG:PLAC',
82        'ENDL:DATE',
83        'ENDL:PLAC',
84        'EVEN',
85        'EVEN:TYPE',
86        'EVEN:DATE',
87        'EVEN:PLAC',
88        'FACT',
89        'FACT:TYPE',
90        'FAMS:CENS:DATE',
91        'FAMS:CENS:PLAC',
92        'FAMS:DIV:DATE',
93        'FAMS:NOTE',
94        'FAMS:SLGS:DATE',
95        'FAMS:SLGS:PLAC',
96        'FAX',
97        'FCOM:DATE',
98        'FCOM:PLAC',
99        'IMMI:DATE',
100        'IMMI:PLAC',
101        'NAME:NICK',
102        'NAME:_MARNM',
103        'NAME:_HEB',
104        'NAME:ROMN',
105        'NATI',
106        'NATU:DATE',
107        'NATU:PLAC',
108        'NOTE',
109        'OCCU',
110        'ORDN:DATE',
111        'ORDN:PLAC',
112        'REFN',
113        'RELI',
114        'RESI',
115        'RESI:DATE',
116        'RESI:PLAC',
117        'SLGC:DATE',
118        'SLGC:PLAC',
119        'TITL',
120        '_BRTM:DATE',
121        '_BRTM:PLAC',
122        '_MILI',
123    ];
124
125    /** @var SearchService */
126    private $search_service;
127
128    /**
129     * SearchController constructor.
130     *
131     * @param SearchService $search_service
132     */
133    public function __construct(SearchService $search_service)
134    {
135        $this->search_service = $search_service;
136    }
137
138    /**
139     * A structured search.
140     *
141     * @param ServerRequestInterface $request
142     *
143     * @return ResponseInterface
144     */
145    public function handle(ServerRequestInterface $request): ResponseInterface
146    {
147        $tree = $request->getAttribute('tree');
148        assert($tree instanceof Tree);
149
150        $default_fields = array_fill_keys(self::DEFAULT_ADVANCED_FIELDS, '');
151
152        $params = $request->getQueryParams();
153
154        $fields      = $params['fields'] ?? $default_fields;
155        $modifiers   = $params['modifiers'] ?? [];
156
157        $other_fields = $this->otherFields($fields);
158        $date_options = $this->dateOptions();
159        $name_options = $this->nameOptions();
160
161        if (array_filter($fields) !== []) {
162            $individuals = $this->search_service->searchIndividualsAdvanced([$tree], $fields, $modifiers);
163        } else {
164            $individuals = [];
165        }
166
167        $title = I18N::translate('Advanced search');
168
169        return $this->viewResponse('search-advanced-page', [
170            'date_options' => $date_options,
171            'fields'       => $fields,
172            'individuals'  => $individuals,
173            'modifiers'    => $modifiers,
174            'name_options' => $name_options,
175            'other_fields' => $other_fields,
176            'title'        => $title,
177            'tree'         => $tree,
178        ]);
179    }
180
181    /**
182     * Extra search fields to add to the advanced search
183     *
184     * @param string[] $fields
185     *
186     * @return string[]
187     */
188    private function otherFields(array $fields): array
189    {
190        $unused = array_diff(self::OTHER_ADVANCED_FIELDS, array_keys($fields));
191
192        $other_fields = [];
193
194        foreach ($unused as $tag) {
195            $other_fields[$tag] = GedcomTag::getLabel($tag);
196        }
197
198        return $other_fields;
199    }
200
201    /**
202     * For the advanced search
203     *
204     * @return string[]
205     */
206    private function dateOptions(): array
207    {
208        return [
209            0  => I18N::translate('Exact date'),
210            2  => I18N::plural('±%s year', '±%s years', 2, I18N::number(2)),
211            5  => I18N::plural('±%s year', '±%s years', 5, I18N::number(5)),
212            10 => I18N::plural('±%s year', '±%s years', 10, I18N::number(10)),
213        ];
214    }
215
216    /**
217     * For the advanced search
218     *
219     * @return string[]
220     */
221    private function nameOptions(): array
222    {
223        return [
224            'EXACT'    => I18N::translate('Exact'),
225            'BEGINS'   => I18N::translate('Begins with'),
226            'CONTAINS' => I18N::translate('Contains'),
227            'SDX'      => I18N::translate('Sounds like'),
228        ];
229    }
230}
231