xref: /webtrees/app/Http/RequestHandlers/SearchAdvancedPage.php (revision c8fd93486d297904a43757cecab7a8694c4f3f21)
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