1f5402f3dSGreg Roach<?php 2f5402f3dSGreg Roach 3f5402f3dSGreg Roach/** 4f5402f3dSGreg Roach * webtrees: online genealogy 589f7189bSGreg Roach * Copyright (C) 2021 webtrees development team 6f5402f3dSGreg Roach * This program is free software: you can redistribute it and/or modify 7f5402f3dSGreg Roach * it under the terms of the GNU General Public License as published by 8f5402f3dSGreg Roach * the Free Software Foundation, either version 3 of the License, or 9f5402f3dSGreg Roach * (at your option) any later version. 10f5402f3dSGreg Roach * This program is distributed in the hope that it will be useful, 11f5402f3dSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 12f5402f3dSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13f5402f3dSGreg Roach * GNU General Public License for more details. 14f5402f3dSGreg Roach * You should have received a copy of the GNU General Public License 1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>. 16f5402f3dSGreg Roach */ 17f5402f3dSGreg Roach 18f5402f3dSGreg Roachdeclare(strict_types=1); 19f5402f3dSGreg Roach 20f5402f3dSGreg Roachnamespace Fisharebest\Webtrees\Http\RequestHandlers; 21f5402f3dSGreg Roach 22f5402f3dSGreg Roachuse Fisharebest\Webtrees\Http\ViewResponseTrait; 23f5402f3dSGreg Roachuse Fisharebest\Webtrees\I18N; 242f86083cSGreg Roachuse Fisharebest\Webtrees\Registry; 25f5402f3dSGreg Roachuse Fisharebest\Webtrees\Services\SearchService; 26f5402f3dSGreg Roachuse Fisharebest\Webtrees\Tree; 27075d1a05SGreg Roachuse Illuminate\Support\Collection; 28f5402f3dSGreg Roachuse Psr\Http\Message\ResponseInterface; 29f5402f3dSGreg Roachuse Psr\Http\Message\ServerRequestInterface; 30f5402f3dSGreg Roachuse Psr\Http\Server\RequestHandlerInterface; 31f5402f3dSGreg Roach 3277fedc87SGreg Roachuse function array_fill_keys; 3377fedc87SGreg Roachuse function array_filter; 34d72c9f8aSGreg Roachuse function array_key_exists; 35*c8fd9348SGreg Roachuse function array_merge; 3677fedc87SGreg Roachuse function assert; 37*c8fd9348SGreg Roachuse function strtr; 3877fedc87SGreg Roach 39f5402f3dSGreg Roach/** 40f5402f3dSGreg Roach * Search for genealogy data 41f5402f3dSGreg Roach */ 42f5402f3dSGreg Roachclass SearchAdvancedPage implements RequestHandlerInterface 43f5402f3dSGreg Roach{ 44f5402f3dSGreg Roach use ViewResponseTrait; 45f5402f3dSGreg Roach 46f5402f3dSGreg Roach private const DEFAULT_ADVANCED_FIELDS = [ 472f86083cSGreg Roach 'INDI:NAME:GIVN', 482f86083cSGreg Roach 'INDI:NAME:SURN', 492f86083cSGreg Roach 'INDI:BIRT:DATE', 502f86083cSGreg Roach 'INDI:BIRT:PLAC', 512f86083cSGreg Roach 'FAM:MARR:DATE', 522f86083cSGreg Roach 'FAM:MARR:PLAC', 532f86083cSGreg Roach 'INDI:DEAT:DATE', 542f86083cSGreg Roach 'INDI:DEAT:PLAC', 552f86083cSGreg Roach 'FATHER:NAME:GIVN', 562f86083cSGreg Roach 'FATHER:NAME:SURN', 572f86083cSGreg Roach 'MOTHER:NAME:GIVN', 582f86083cSGreg Roach 'MOTHER:NAME:SURN', 59f5402f3dSGreg Roach ]; 60f5402f3dSGreg Roach 61f5402f3dSGreg Roach private const OTHER_ADVANCED_FIELDS = [ 622f86083cSGreg Roach 'INDI:ADOP:DATE', 632f86083cSGreg Roach 'INDI:ADOP:PLAC', 642f86083cSGreg Roach 'INDI:AFN', 652f86083cSGreg Roach 'INDI:BAPL:DATE', 662f86083cSGreg Roach 'INDI:BAPL:PLAC', 672f86083cSGreg Roach 'INDI:BAPM:DATE', 682f86083cSGreg Roach 'INDI:BAPM:PLAC', 692f86083cSGreg Roach 'INDI:BARM:DATE', 702f86083cSGreg Roach 'INDI:BARM:PLAC', 712f86083cSGreg Roach 'INDI:BASM:DATE', 722f86083cSGreg Roach 'INDI:BASM:PLAC', 732f86083cSGreg Roach 'INDI:BLES:DATE', 742f86083cSGreg Roach 'INDI:BLES:PLAC', 752f86083cSGreg Roach 'INDI:BURI:DATE', 762f86083cSGreg Roach 'INDI:BURI:PLAC', 772f86083cSGreg Roach 'INDI:CENS:DATE', 782f86083cSGreg Roach 'INDI:CENS:PLAC', 792f86083cSGreg Roach 'INDI:CHAN:DATE', 802f86083cSGreg Roach 'INDI:CHAN:_WT_USER', 812f86083cSGreg Roach 'INDI:CHR:DATE', 822f86083cSGreg Roach 'INDI:CHR:PLAC', 832f86083cSGreg Roach 'INDI:CREM:DATE', 842f86083cSGreg Roach 'INDI:CREM:PLAC', 852f86083cSGreg Roach 'INDI:DSCR', 862f86083cSGreg Roach 'INDI:EMIG:DATE', 872f86083cSGreg Roach 'INDI:EMIG:PLAC', 882f86083cSGreg Roach 'INDI:ENDL:DATE', 892f86083cSGreg Roach 'INDI:ENDL:PLAC', 902f86083cSGreg Roach 'INDI:EVEN', 912f86083cSGreg Roach 'INDI:EVEN:TYPE', 922f86083cSGreg Roach 'INDI:EVEN:DATE', 932f86083cSGreg Roach 'INDI:EVEN:PLAC', 942f86083cSGreg Roach 'INDI:FACT', 952f86083cSGreg Roach 'INDI:FACT:TYPE', 962f86083cSGreg Roach 'INDI:FCOM:DATE', 972f86083cSGreg Roach 'INDI:FCOM:PLAC', 982f86083cSGreg Roach 'INDI:IMMI:DATE', 992f86083cSGreg Roach 'INDI:IMMI:PLAC', 1002f86083cSGreg Roach 'INDI:NAME:NICK', 1012f86083cSGreg Roach 'INDI:NAME:_MARNM', 1022f86083cSGreg Roach 'INDI:NAME:_HEB', 1032f86083cSGreg Roach 'INDI:NAME:ROMN', 1042f86083cSGreg Roach 'INDI:NATI', 1052f86083cSGreg Roach 'INDI:NATU:DATE', 1062f86083cSGreg Roach 'INDI:NATU:PLAC', 1072f86083cSGreg Roach 'INDI:NOTE', 1082f86083cSGreg Roach 'INDI:OCCU', 1092f86083cSGreg Roach 'INDI:ORDN:DATE', 1102f86083cSGreg Roach 'INDI:ORDN:PLAC', 1112f86083cSGreg Roach 'INDI:REFN', 1122f86083cSGreg Roach 'INDI:RELI', 1132f86083cSGreg Roach 'INDI:RESI:DATE', 1142f86083cSGreg Roach 'INDI:RESI:EMAIL', 1152f86083cSGreg Roach 'INDI:RESI:PLAC', 1162f86083cSGreg Roach 'INDI:SLGC:DATE', 1172f86083cSGreg Roach 'INDI:SLGC:PLAC', 1182f86083cSGreg Roach 'INDI:TITL', 1192f86083cSGreg Roach 'FAM:DIV:DATE', 1202f86083cSGreg Roach 'FAM:SLGS:DATE', 1212f86083cSGreg Roach 'FAM:SLGS:PLAC', 122f5402f3dSGreg Roach ]; 123f5402f3dSGreg Roach 124c4943cffSGreg Roach private SearchService $search_service; 125f5402f3dSGreg Roach 126f5402f3dSGreg Roach /** 127f5402f3dSGreg Roach * SearchController constructor. 128f5402f3dSGreg Roach * 129f5402f3dSGreg Roach * @param SearchService $search_service 130f5402f3dSGreg Roach */ 131f5402f3dSGreg Roach public function __construct(SearchService $search_service) 132f5402f3dSGreg Roach { 133f5402f3dSGreg Roach $this->search_service = $search_service; 134f5402f3dSGreg Roach } 135f5402f3dSGreg Roach 136f5402f3dSGreg Roach /** 137f5402f3dSGreg Roach * A structured search. 138f5402f3dSGreg Roach * 139f5402f3dSGreg Roach * @param ServerRequestInterface $request 140f5402f3dSGreg Roach * 141f5402f3dSGreg Roach * @return ResponseInterface 142f5402f3dSGreg Roach */ 143f5402f3dSGreg Roach public function handle(ServerRequestInterface $request): ResponseInterface 144f5402f3dSGreg Roach { 145f5402f3dSGreg Roach $tree = $request->getAttribute('tree'); 14675964c75SGreg Roach assert($tree instanceof Tree); 147f5402f3dSGreg Roach 148f5402f3dSGreg Roach $default_fields = array_fill_keys(self::DEFAULT_ADVANCED_FIELDS, ''); 149f5402f3dSGreg Roach 150f5402f3dSGreg Roach $params = $request->getQueryParams(); 151f5402f3dSGreg Roach 152f5402f3dSGreg Roach $fields = $params['fields'] ?? $default_fields; 153f5402f3dSGreg Roach $modifiers = $params['modifiers'] ?? []; 154f5402f3dSGreg Roach 1552f86083cSGreg Roach $other_fields = $this->otherFields($fields); 156f5402f3dSGreg Roach $date_options = $this->dateOptions(); 157f5402f3dSGreg Roach $name_options = $this->nameOptions(); 158f5402f3dSGreg Roach 159a91af26aSGreg Roach if (array_filter($fields) !== []) { 160f5402f3dSGreg Roach $individuals = $this->search_service->searchIndividualsAdvanced([$tree], $fields, $modifiers); 161f5402f3dSGreg Roach } else { 162075d1a05SGreg Roach $individuals = new Collection(); 163f5402f3dSGreg Roach } 164f5402f3dSGreg Roach 165f5402f3dSGreg Roach $title = I18N::translate('Advanced search'); 166f5402f3dSGreg Roach 167f5402f3dSGreg Roach return $this->viewResponse('search-advanced-page', [ 168f5402f3dSGreg Roach 'date_options' => $date_options, 169f5402f3dSGreg Roach 'fields' => $fields, 170*c8fd9348SGreg Roach 'field_labels' => $this->fieldLabels(), 171f5402f3dSGreg Roach 'individuals' => $individuals, 172f5402f3dSGreg Roach 'modifiers' => $modifiers, 173f5402f3dSGreg Roach 'name_options' => $name_options, 174f5402f3dSGreg Roach 'other_fields' => $other_fields, 175f5402f3dSGreg Roach 'title' => $title, 176ef5d23f1SGreg Roach 'tree' => $tree, 177f5402f3dSGreg Roach ]); 178f5402f3dSGreg Roach } 179f5402f3dSGreg Roach 180f5402f3dSGreg Roach /** 181f5402f3dSGreg Roach * Extra search fields to add to the advanced search 182f5402f3dSGreg Roach * 183f5402f3dSGreg Roach * @param string[] $fields 184f5402f3dSGreg Roach * 18577fedc87SGreg Roach * @return array<string,string> 186f5402f3dSGreg Roach */ 1872f86083cSGreg Roach private function otherFields(array $fields): array 188f5402f3dSGreg Roach { 18977fedc87SGreg Roach $default_facts = new Collection(self::OTHER_ADVANCED_FIELDS); 1902f86083cSGreg Roach 1912f86083cSGreg Roach $comparator = static function (string $x, string $y): int { 1922f86083cSGreg Roach $element_factory = Registry::elementFactory(); 1932f86083cSGreg Roach 1942f86083cSGreg Roach $label1 = $element_factory->make(strtr($x, [':DATE' => '', ':PLAC' => '', ':TYPE' => '']))->label(); 1952f86083cSGreg Roach $label2 = $element_factory->make(strtr($y, [':DATE' => '', ':PLAC' => '', ':TYPE' => '']))->label(); 1962f86083cSGreg Roach 1972f86083cSGreg Roach return I18N::comparator()($label1, $label2) ?: strcmp($x, $y); 1982f86083cSGreg Roach }; 199f5402f3dSGreg Roach 20077fedc87SGreg Roach return $default_facts 2012f86083cSGreg Roach ->reject(fn (string $field): bool => array_key_exists($field, $fields)) 2022f86083cSGreg Roach ->sort($comparator) 203*c8fd9348SGreg Roach ->mapWithKeys(fn (string $fact): array => [$fact => Registry::elementFactory()->make($fact)->label()]) 20477fedc87SGreg Roach ->all(); 205f5402f3dSGreg Roach } 206f5402f3dSGreg Roach 2076f922bb8SGreg Roach 2086f922bb8SGreg Roach /** 2096f922bb8SGreg Roach * We use some pseudo-GEDCOM tags for some of our fields. 2106f922bb8SGreg Roach * 2116f922bb8SGreg Roach * @return array<string,string> 2126f922bb8SGreg Roach */ 213*c8fd9348SGreg Roach private function fieldLabels(): array 2146f922bb8SGreg Roach { 215*c8fd9348SGreg Roach 216*c8fd9348SGreg Roach $return = []; 217*c8fd9348SGreg Roach 218*c8fd9348SGreg Roach foreach (array_merge(self::OTHER_ADVANCED_FIELDS, self::DEFAULT_ADVANCED_FIELDS) as $field) { 219*c8fd9348SGreg Roach $tmp = strtr($field, ['MOTHER:' => 'INDI:', 'FATHER:' => 'INDI:']); 220*c8fd9348SGreg Roach $return[$field] = Registry::elementFactory()->make($tmp)->label(); 221*c8fd9348SGreg Roach } 222*c8fd9348SGreg Roach 223*c8fd9348SGreg Roach 224*c8fd9348SGreg Roach return $return; 2256f922bb8SGreg Roach } 2266f922bb8SGreg Roach 227f5402f3dSGreg Roach /** 228f5402f3dSGreg Roach * For the advanced search 229f5402f3dSGreg Roach * 23024f2a3afSGreg Roach * @return array<string> 231f5402f3dSGreg Roach */ 232f5402f3dSGreg Roach private function dateOptions(): array 233f5402f3dSGreg Roach { 234f5402f3dSGreg Roach return [ 235f5402f3dSGreg Roach 0 => I18N::translate('Exact date'), 236c127524bSGreg Roach 1 => I18N::plural('±%s year', '±%s years', 1, I18N::number(1)), 237f5402f3dSGreg Roach 2 => I18N::plural('±%s year', '±%s years', 2, I18N::number(2)), 238f5402f3dSGreg Roach 5 => I18N::plural('±%s year', '±%s years', 5, I18N::number(5)), 239f5402f3dSGreg Roach 10 => I18N::plural('±%s year', '±%s years', 10, I18N::number(10)), 240c127524bSGreg Roach 20 => I18N::plural('±%s year', '±%s years', 20, I18N::number(20)), 241f5402f3dSGreg Roach ]; 242f5402f3dSGreg Roach } 243f5402f3dSGreg Roach 244f5402f3dSGreg Roach /** 245f5402f3dSGreg Roach * For the advanced search 246f5402f3dSGreg Roach * 24724f2a3afSGreg Roach * @return array<string> 248f5402f3dSGreg Roach */ 249f5402f3dSGreg Roach private function nameOptions(): array 250f5402f3dSGreg Roach { 251f5402f3dSGreg Roach return [ 252f5402f3dSGreg Roach 'EXACT' => I18N::translate('Exact'), 253f5402f3dSGreg Roach 'BEGINS' => I18N::translate('Begins with'), 254f5402f3dSGreg Roach 'CONTAINS' => I18N::translate('Contains'), 255f5402f3dSGreg Roach 'SDX' => I18N::translate('Sounds like'), 256f5402f3dSGreg Roach ]; 257f5402f3dSGreg Roach } 258f5402f3dSGreg Roach} 259