196716c47SGreg Roach<?php 296716c47SGreg Roach 396716c47SGreg Roach/** 496716c47SGreg Roach * webtrees: online genealogy 5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team 696716c47SGreg Roach * This program is free software: you can redistribute it and/or modify 796716c47SGreg Roach * it under the terms of the GNU General Public License as published by 896716c47SGreg Roach * the Free Software Foundation, either version 3 of the License, or 996716c47SGreg Roach * (at your option) any later version. 1096716c47SGreg Roach * This program is distributed in the hope that it will be useful, 1196716c47SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 1296716c47SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1396716c47SGreg Roach * GNU General Public License for more details. 1496716c47SGreg 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/>. 1696716c47SGreg Roach */ 1796716c47SGreg Roach 1896716c47SGreg Roachdeclare(strict_types=1); 1996716c47SGreg Roach 2096716c47SGreg Roachnamespace Fisharebest\Webtrees\Http\RequestHandlers; 2196716c47SGreg Roach 2296716c47SGreg Roachuse Fisharebest\Algorithm\ConnectedComponent; 236f4ec3caSGreg Roachuse Fisharebest\Webtrees\DB; 2496716c47SGreg Roachuse Fisharebest\Webtrees\Http\ViewResponseTrait; 2596716c47SGreg Roachuse Fisharebest\Webtrees\I18N; 2696716c47SGreg Roachuse Fisharebest\Webtrees\Individual; 276b9cb339SGreg Roachuse Fisharebest\Webtrees\Registry; 28b55cbc6bSGreg Roachuse Fisharebest\Webtrees\Validator; 294fd89268SGreg Roachuse Illuminate\Support\Collection; 3096716c47SGreg Roachuse Psr\Http\Message\ResponseInterface; 3196716c47SGreg Roachuse Psr\Http\Message\ServerRequestInterface; 3296716c47SGreg Roachuse Psr\Http\Server\RequestHandlerInterface; 3396716c47SGreg Roach 3410e06497SGreg Roachuse function count; 3510e06497SGreg Roachuse function in_array; 3668c99bc7SGreg Roachuse function strtolower; 3796716c47SGreg Roach 3896716c47SGreg Roach/** 3996716c47SGreg Roach * Find groups of unrelated individuals. 4096716c47SGreg Roach */ 4196716c47SGreg Roachclass UnconnectedPage implements RequestHandlerInterface 4296716c47SGreg Roach{ 4396716c47SGreg Roach use ViewResponseTrait; 4496716c47SGreg Roach 4596716c47SGreg Roach /** 4696716c47SGreg Roach * @param ServerRequestInterface $request 4796716c47SGreg Roach * 4896716c47SGreg Roach * @return ResponseInterface 4996716c47SGreg Roach */ 5096716c47SGreg Roach public function handle(ServerRequestInterface $request): ResponseInterface 5196716c47SGreg Roach { 52b55cbc6bSGreg Roach $tree = Validator::attributes($request)->tree(); 53b55cbc6bSGreg Roach $user = Validator::attributes($request)->user(); 54748dbe15SGreg Roach $aliases = Validator::queryParams($request)->boolean('aliases', false); 55748dbe15SGreg Roach $associates = Validator::queryParams($request)->boolean('associates', false); 5696716c47SGreg Roach 57f925fcc4SGreg Roach // Connect individuals using these links. 5896716c47SGreg Roach $links = ['FAMS', 'FAMC']; 59f925fcc4SGreg Roach 60f925fcc4SGreg Roach if ($aliases) { 61f925fcc4SGreg Roach $links[] = 'ALIA'; 62f925fcc4SGreg Roach } 63f925fcc4SGreg Roach 64f925fcc4SGreg Roach if ($associates) { 65f925fcc4SGreg Roach $links[] = 'ASSO'; 66f925fcc4SGreg Roach $links[] = '_ASSO'; 6796716c47SGreg Roach } 6896716c47SGreg Roach 6996716c47SGreg Roach $rows = DB::table('link') 7096716c47SGreg Roach ->where('l_file', '=', $tree->id()) 7196716c47SGreg Roach ->whereIn('l_type', $links) 7296716c47SGreg Roach ->select(['l_from', 'l_to']) 7396716c47SGreg Roach ->get(); 7496716c47SGreg Roach 7512e03a90SGreg Roach $graph = DB::table('individuals') 7612e03a90SGreg Roach ->where('i_file', '=', $tree->id()) 7712e03a90SGreg Roach ->pluck('i_id') 78*f25fc0f9SGreg Roach ->mapWithKeys(static fn (string $xref): array => [$xref => []]) 7912e03a90SGreg Roach ->all(); 8096716c47SGreg Roach 8196716c47SGreg Roach foreach ($rows as $row) { 8296716c47SGreg Roach $graph[$row->l_from][$row->l_to] = 1; 8396716c47SGreg Roach $graph[$row->l_to][$row->l_from] = 1; 8496716c47SGreg Roach } 8596716c47SGreg Roach 8696716c47SGreg Roach $algorithm = new ConnectedComponent($graph); 8796716c47SGreg Roach $components = $algorithm->findConnectedComponents(); 8896716c47SGreg Roach $root = $tree->significantIndividual($user); 8996716c47SGreg Roach $xref = $root->xref(); 9096716c47SGreg Roach 9196716c47SGreg Roach /** @var Individual[][] */ 9296716c47SGreg Roach $individual_groups = []; 9396716c47SGreg Roach 9496716c47SGreg Roach foreach ($components as $component) { 9568c99bc7SGreg Roach // Allow for upper/lower-case mismatches, and all-numeric XREFs 9668c99bc7SGreg Roach $component = array_map(static fn ($x): string => strtolower((string) $x), $component); 9768c99bc7SGreg Roach 9868c99bc7SGreg Roach if (!in_array(strtolower($xref), $component, true)) { 9970c53a06SGreg Roach $individual_groups[] = DB::table('individuals') 10070c53a06SGreg Roach ->where('i_file', '=', $tree->id()) 10170c53a06SGreg Roach ->whereIn('i_id', $component) 10270c53a06SGreg Roach ->get() 1036b9cb339SGreg Roach ->map(Registry::individualFactory()->mapper($tree)) 10470c53a06SGreg Roach ->filter(); 10596716c47SGreg Roach } 10696716c47SGreg Roach } 10796716c47SGreg Roach 1084fd89268SGreg Roach usort($individual_groups, static fn (Collection $x, Collection $y): int => count($x) <=> count($y)); 1094fd89268SGreg Roach 11096716c47SGreg Roach $title = I18N::translate('Find unrelated individuals') . ' — ' . e($tree->title()); 11196716c47SGreg Roach 11296716c47SGreg Roach $this->layout = 'layouts/administration'; 11396716c47SGreg Roach 11496716c47SGreg Roach return $this->viewResponse('admin/trees-unconnected', [ 115f925fcc4SGreg Roach 'aliases' => $aliases, 11696716c47SGreg Roach 'associates' => $associates, 11796716c47SGreg Roach 'root' => $root, 11896716c47SGreg Roach 'individual_groups' => $individual_groups, 11996716c47SGreg Roach 'title' => $title, 12096716c47SGreg Roach 'tree' => $tree, 12196716c47SGreg Roach ]); 12296716c47SGreg Roach } 12396716c47SGreg Roach} 124