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 ]); 178 } 179 180 /** 181 * Extra search fields to add to the advanced search 182 * 183 * @param string[] $fields 184 * 185 * @return string[] 186 */ 187 private function otherFields(array $fields): array 188 { 189 $unused = array_diff(self::OTHER_ADVANCED_FIELDS, array_keys($fields)); 190 191 $other_fields = []; 192 193 foreach ($unused as $tag) { 194 $other_fields[$tag] = GedcomTag::getLabel($tag); 195 } 196 197 return $other_fields; 198 } 199 200 /** 201 * For the advanced search 202 * 203 * @return string[] 204 */ 205 private function dateOptions(): array 206 { 207 return [ 208 0 => I18N::translate('Exact date'), 209 2 => I18N::plural('±%s year', '±%s years', 2, I18N::number(2)), 210 5 => I18N::plural('±%s year', '±%s years', 5, I18N::number(5)), 211 10 => I18N::plural('±%s year', '±%s years', 10, I18N::number(10)), 212 ]; 213 } 214 215 /** 216 * For the advanced search 217 * 218 * @return string[] 219 */ 220 private function nameOptions(): array 221 { 222 return [ 223 'EXACT' => I18N::translate('Exact'), 224 'BEGINS' => I18N::translate('Begins with'), 225 'CONTAINS' => I18N::translate('Contains'), 226 'SDX' => I18N::translate('Sounds like'), 227 ]; 228 } 229} 230