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