18add1155SRico Sonntag<?php 23976b470SGreg Roach 38add1155SRico Sonntag/** 48add1155SRico Sonntag * webtrees: online genealogy 5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team 68add1155SRico Sonntag * This program is free software: you can redistribute it and/or modify 78add1155SRico Sonntag * it under the terms of the GNU General Public License as published by 88add1155SRico Sonntag * the Free Software Foundation, either version 3 of the License, or 98add1155SRico Sonntag * (at your option) any later version. 108add1155SRico Sonntag * This program is distributed in the hope that it will be useful, 118add1155SRico Sonntag * but WITHOUT ANY WARRANTY; without even the implied warranty of 128add1155SRico Sonntag * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 138add1155SRico Sonntag * GNU General Public License for more details. 148add1155SRico Sonntag * 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/>. 168add1155SRico Sonntag */ 17fcfa147eSGreg Roach 188add1155SRico Sonntagdeclare(strict_types=1); 198add1155SRico Sonntag 208add1155SRico Sonntagnamespace Fisharebest\Webtrees\Statistics\Repository; 218add1155SRico Sonntag 228add1155SRico Sonntaguse Fisharebest\Webtrees\Date; 236f4ec3caSGreg Roachuse Fisharebest\Webtrees\DB; 24820b62dfSGreg Roachuse Fisharebest\Webtrees\Fact; 25ef475b14SGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 268add1155SRico Sonntaguse Fisharebest\Webtrees\I18N; 27f0c88a96SGreg Roachuse Fisharebest\Webtrees\Registry; 288add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Repository\Interfaces\FamilyDatesRepositoryInterface; 298add1155SRico Sonntaguse Fisharebest\Webtrees\Tree; 308add1155SRico Sonntaguse Illuminate\Database\Query\Builder; 318add1155SRico Sonntag 324c78e066SGreg Roachuse function abs; 334c78e066SGreg Roachuse function e; 344c78e066SGreg Roach 358add1155SRico Sonntag/** 368add1155SRico Sonntag * A repository providing methods for family dates related statistics (birth, death, marriage, divorce). 378add1155SRico Sonntag */ 388add1155SRico Sonntagclass FamilyDatesRepository implements FamilyDatesRepositoryInterface 398add1155SRico Sonntag{ 408add1155SRico Sonntag /** 418add1155SRico Sonntag * Sorting directions. 428add1155SRico Sonntag */ 438add1155SRico Sonntag private const SORT_MIN = 'MIN'; 448add1155SRico Sonntag private const SORT_MAX = 'MAX'; 458add1155SRico Sonntag 468add1155SRico Sonntag /** 478add1155SRico Sonntag * Event facts. 488add1155SRico Sonntag */ 498add1155SRico Sonntag private const EVENT_BIRTH = 'BIRT'; 508add1155SRico Sonntag private const EVENT_DEATH = 'DEAT'; 518add1155SRico Sonntag private const EVENT_MARRIAGE = 'MARR'; 528add1155SRico Sonntag private const EVENT_DIVORCE = 'DIV'; 538add1155SRico Sonntag 544c78e066SGreg Roach private Tree $tree; 558add1155SRico Sonntag 568add1155SRico Sonntag /** 578add1155SRico Sonntag * @param Tree $tree 588add1155SRico Sonntag */ 598add1155SRico Sonntag public function __construct(Tree $tree) 608add1155SRico Sonntag { 618add1155SRico Sonntag $this->tree = $tree; 628add1155SRico Sonntag } 638add1155SRico Sonntag 648add1155SRico Sonntag /** 658add1155SRico Sonntag * Returns the first/last event record for the given event fact. 668add1155SRico Sonntag * 678add1155SRico Sonntag * @param string $fact 688add1155SRico Sonntag * @param string $operation 698add1155SRico Sonntag * 702cd72788SGreg Roach * @return object{id:string,year:int,fact:string,type:string}|null 718add1155SRico Sonntag */ 72*1ff45046SGreg Roach private function eventQuery(string $fact, string $operation): object|null 738add1155SRico Sonntag { 748add1155SRico Sonntag return DB::table('dates') 758add1155SRico Sonntag ->select(['d_gid as id', 'd_year as year', 'd_fact AS fact', 'd_type AS type']) 768add1155SRico Sonntag ->where('d_file', '=', $this->tree->id()) 778add1155SRico Sonntag ->where('d_fact', '=', $fact) 7825d7fe95SGreg Roach ->where('d_julianday1', '=', function (Builder $query) use ($operation, $fact): void { 798add1155SRico Sonntag $query->selectRaw($operation . '(d_julianday1)') 808add1155SRico Sonntag ->from('dates') 818add1155SRico Sonntag ->where('d_file', '=', $this->tree->id()) 828add1155SRico Sonntag ->where('d_fact', '=', $fact) 838add1155SRico Sonntag ->where('d_julianday1', '<>', 0); 848add1155SRico Sonntag }) 852cd72788SGreg Roach ->limit(1) 862cd72788SGreg Roach ->get() 872cd72788SGreg Roach ->map(static fn (object $row): object => (object) [ 882cd72788SGreg Roach 'id' => $row->id, 892cd72788SGreg Roach 'year' => (int) $row->year, 902cd72788SGreg Roach 'fact' => $row->fact, 912cd72788SGreg Roach 'type' => $row->type, 922cd72788SGreg Roach ]) 938add1155SRico Sonntag ->first(); 948add1155SRico Sonntag } 958add1155SRico Sonntag 968add1155SRico Sonntag /** 9752f124b0SAlejandro Criado-Pérez * Returns the formatted year of the first/last occurring event. 988add1155SRico Sonntag * 998add1155SRico Sonntag * @param string $type The fact to query 1008add1155SRico Sonntag * @param string $operation The sorting operation 1018add1155SRico Sonntag * 1028add1155SRico Sonntag * @return string 1038add1155SRico Sonntag */ 1048add1155SRico Sonntag private function getFirstLastEvent(string $type, string $operation): string 1058add1155SRico Sonntag { 1068add1155SRico Sonntag $row = $this->eventQuery($type, $operation); 107dd7dd2a1SRico Sonntag $result = I18N::translate('This information is not available.'); 1088add1155SRico Sonntag 109ef475b14SGreg Roach if ($row !== null) { 1106b9cb339SGreg Roach $record = Registry::gedcomRecordFactory()->make($row->id, $this->tree); 1118add1155SRico Sonntag 112ef475b14SGreg Roach if ($record instanceof GedcomRecord && $record->canShow()) { 1138add1155SRico Sonntag $result = $record->formatList(); 1148add1155SRico Sonntag } else { 1158add1155SRico Sonntag $result = I18N::translate('This information is private and cannot be shown.'); 1168add1155SRico Sonntag } 1178add1155SRico Sonntag } 1188add1155SRico Sonntag 1198add1155SRico Sonntag return $result; 1208add1155SRico Sonntag } 1218add1155SRico Sonntag 1228add1155SRico Sonntag /** 1230dcd9387SGreg Roach * @return string 1248add1155SRico Sonntag */ 1258add1155SRico Sonntag public function firstBirth(): string 1268add1155SRico Sonntag { 1278add1155SRico Sonntag return $this->getFirstLastEvent(self::EVENT_BIRTH, self::SORT_MIN); 1288add1155SRico Sonntag } 1298add1155SRico Sonntag 1308add1155SRico Sonntag /** 1310dcd9387SGreg Roach * @return string 1328add1155SRico Sonntag */ 1338add1155SRico Sonntag public function lastBirth(): string 1348add1155SRico Sonntag { 1358add1155SRico Sonntag return $this->getFirstLastEvent(self::EVENT_BIRTH, self::SORT_MAX); 1368add1155SRico Sonntag } 1378add1155SRico Sonntag 1388add1155SRico Sonntag /** 1390dcd9387SGreg Roach * @return string 1408add1155SRico Sonntag */ 1418add1155SRico Sonntag public function firstDeath(): string 1428add1155SRico Sonntag { 1438add1155SRico Sonntag return $this->getFirstLastEvent(self::EVENT_DEATH, self::SORT_MIN); 1448add1155SRico Sonntag } 1458add1155SRico Sonntag 1468add1155SRico Sonntag /** 1470dcd9387SGreg Roach * @return string 1488add1155SRico Sonntag */ 1498add1155SRico Sonntag public function lastDeath(): string 1508add1155SRico Sonntag { 1518add1155SRico Sonntag return $this->getFirstLastEvent(self::EVENT_DEATH, self::SORT_MAX); 1528add1155SRico Sonntag } 1538add1155SRico Sonntag 1548add1155SRico Sonntag /** 1550dcd9387SGreg Roach * @return string 1568add1155SRico Sonntag */ 1578add1155SRico Sonntag public function firstMarriage(): string 1588add1155SRico Sonntag { 1598add1155SRico Sonntag return $this->getFirstLastEvent(self::EVENT_MARRIAGE, self::SORT_MIN); 1608add1155SRico Sonntag } 1618add1155SRico Sonntag 1628add1155SRico Sonntag /** 1630dcd9387SGreg Roach * @return string 1648add1155SRico Sonntag */ 1658add1155SRico Sonntag public function lastMarriage(): string 1668add1155SRico Sonntag { 1678add1155SRico Sonntag return $this->getFirstLastEvent(self::EVENT_MARRIAGE, self::SORT_MAX); 1688add1155SRico Sonntag } 1698add1155SRico Sonntag 1708add1155SRico Sonntag /** 1710dcd9387SGreg Roach * @return string 1728add1155SRico Sonntag */ 1738add1155SRico Sonntag public function firstDivorce(): string 1748add1155SRico Sonntag { 1758add1155SRico Sonntag return $this->getFirstLastEvent(self::EVENT_DIVORCE, self::SORT_MIN); 1768add1155SRico Sonntag } 1778add1155SRico Sonntag 1788add1155SRico Sonntag /** 1790dcd9387SGreg Roach * @return string 1808add1155SRico Sonntag */ 1818add1155SRico Sonntag public function lastDivorce(): string 1828add1155SRico Sonntag { 1838add1155SRico Sonntag return $this->getFirstLastEvent(self::EVENT_DIVORCE, self::SORT_MAX); 1848add1155SRico Sonntag } 1858add1155SRico Sonntag 1868add1155SRico Sonntag /** 18752f124b0SAlejandro Criado-Pérez * Returns the formatted year of the first/last occurring event. 1888add1155SRico Sonntag * 1898add1155SRico Sonntag * @param string $type The fact to query 1908add1155SRico Sonntag * @param string $operation The sorting operation 1918add1155SRico Sonntag * 1928add1155SRico Sonntag * @return string 1938add1155SRico Sonntag */ 1948add1155SRico Sonntag private function getFirstLastEventYear(string $type, string $operation): string 1958add1155SRico Sonntag { 1968add1155SRico Sonntag $row = $this->eventQuery($type, $operation); 1978add1155SRico Sonntag 198ef475b14SGreg Roach if ($row === null) { 1998add1155SRico Sonntag return ''; 2008add1155SRico Sonntag } 2018add1155SRico Sonntag 2028add1155SRico Sonntag if ($row->year < 0) { 2038add1155SRico Sonntag $row->year = abs($row->year) . ' B.C.'; 2048add1155SRico Sonntag } 2058add1155SRico Sonntag 2068add1155SRico Sonntag return (new Date($row->type . ' ' . $row->year)) 2078add1155SRico Sonntag ->display(); 2088add1155SRico Sonntag } 2098add1155SRico Sonntag 2108add1155SRico Sonntag /** 2110dcd9387SGreg Roach * @return string 2128add1155SRico Sonntag */ 2138add1155SRico Sonntag public function firstBirthYear(): string 2148add1155SRico Sonntag { 2158add1155SRico Sonntag return $this->getFirstLastEventYear(self::EVENT_BIRTH, self::SORT_MIN); 2168add1155SRico Sonntag } 2178add1155SRico Sonntag 2188add1155SRico Sonntag /** 2190dcd9387SGreg Roach * @return string 2208add1155SRico Sonntag */ 2218add1155SRico Sonntag public function lastBirthYear(): string 2228add1155SRico Sonntag { 2238add1155SRico Sonntag return $this->getFirstLastEventYear(self::EVENT_BIRTH, self::SORT_MAX); 2248add1155SRico Sonntag } 2258add1155SRico Sonntag 2268add1155SRico Sonntag /** 2270dcd9387SGreg Roach * @return string 2288add1155SRico Sonntag */ 2298add1155SRico Sonntag public function firstDeathYear(): string 2308add1155SRico Sonntag { 2318add1155SRico Sonntag return $this->getFirstLastEventYear(self::EVENT_DEATH, self::SORT_MIN); 2328add1155SRico Sonntag } 2338add1155SRico Sonntag 2348add1155SRico Sonntag /** 2350dcd9387SGreg Roach * @return string 2368add1155SRico Sonntag */ 2378add1155SRico Sonntag public function lastDeathYear(): string 2388add1155SRico Sonntag { 2398add1155SRico Sonntag return $this->getFirstLastEventYear(self::EVENT_DEATH, self::SORT_MAX); 2408add1155SRico Sonntag } 2418add1155SRico Sonntag 2428add1155SRico Sonntag /** 2430dcd9387SGreg Roach * @return string 2448add1155SRico Sonntag */ 2458add1155SRico Sonntag public function firstMarriageYear(): string 2468add1155SRico Sonntag { 2478add1155SRico Sonntag return $this->getFirstLastEventYear(self::EVENT_MARRIAGE, self::SORT_MIN); 2488add1155SRico Sonntag } 2498add1155SRico Sonntag 2508add1155SRico Sonntag /** 2510dcd9387SGreg Roach * @return string 2528add1155SRico Sonntag */ 2538add1155SRico Sonntag public function lastMarriageYear(): string 2548add1155SRico Sonntag { 2558add1155SRico Sonntag return $this->getFirstLastEventYear(self::EVENT_MARRIAGE, self::SORT_MAX); 2568add1155SRico Sonntag } 2578add1155SRico Sonntag 2588add1155SRico Sonntag /** 2590dcd9387SGreg Roach * @return string 2608add1155SRico Sonntag */ 2618add1155SRico Sonntag public function firstDivorceYear(): string 2628add1155SRico Sonntag { 2638add1155SRico Sonntag return $this->getFirstLastEventYear(self::EVENT_DIVORCE, self::SORT_MIN); 2648add1155SRico Sonntag } 2658add1155SRico Sonntag 2668add1155SRico Sonntag /** 2670dcd9387SGreg Roach * @return string 2688add1155SRico Sonntag */ 2698add1155SRico Sonntag public function lastDivorceYear(): string 2708add1155SRico Sonntag { 2718add1155SRico Sonntag return $this->getFirstLastEventYear(self::EVENT_DIVORCE, self::SORT_MAX); 2728add1155SRico Sonntag } 2738add1155SRico Sonntag 2748add1155SRico Sonntag /** 27552f124b0SAlejandro Criado-Pérez * Returns the formatted name of the first/last occurring event. 2768add1155SRico Sonntag * 2778add1155SRico Sonntag * @param string $type The fact to query 2788add1155SRico Sonntag * @param string $operation The sorting operation 2798add1155SRico Sonntag * 2808add1155SRico Sonntag * @return string 2818add1155SRico Sonntag */ 2828add1155SRico Sonntag private function getFirstLastEventName(string $type, string $operation): string 2838add1155SRico Sonntag { 2848add1155SRico Sonntag $row = $this->eventQuery($type, $operation); 2858add1155SRico Sonntag 286ef475b14SGreg Roach if ($row !== null) { 2876b9cb339SGreg Roach $record = Registry::gedcomRecordFactory()->make($row->id, $this->tree); 2888add1155SRico Sonntag 289ef475b14SGreg Roach if ($record instanceof GedcomRecord) { 29039ca88baSGreg Roach return '<a href="' . e($record->url()) . '">' . $record->fullName() . '</a>'; 2918add1155SRico Sonntag } 2928add1155SRico Sonntag } 2938add1155SRico Sonntag 2948add1155SRico Sonntag return ''; 2958add1155SRico Sonntag } 2968add1155SRico Sonntag 2978add1155SRico Sonntag /** 2980dcd9387SGreg Roach * @return string 2998add1155SRico Sonntag */ 3008add1155SRico Sonntag public function firstBirthName(): string 3018add1155SRico Sonntag { 3028add1155SRico Sonntag return $this->getFirstLastEventName(self::EVENT_BIRTH, self::SORT_MIN); 3038add1155SRico Sonntag } 3048add1155SRico Sonntag 3058add1155SRico Sonntag /** 3060dcd9387SGreg Roach * @return string 3078add1155SRico Sonntag */ 3088add1155SRico Sonntag public function lastBirthName(): string 3098add1155SRico Sonntag { 3108add1155SRico Sonntag return $this->getFirstLastEventName(self::EVENT_BIRTH, self::SORT_MAX); 3118add1155SRico Sonntag } 3128add1155SRico Sonntag 3138add1155SRico Sonntag /** 3140dcd9387SGreg Roach * @return string 3158add1155SRico Sonntag */ 3168add1155SRico Sonntag public function firstDeathName(): string 3178add1155SRico Sonntag { 3188add1155SRico Sonntag return $this->getFirstLastEventName(self::EVENT_DEATH, self::SORT_MIN); 3198add1155SRico Sonntag } 3208add1155SRico Sonntag 3218add1155SRico Sonntag /** 3220dcd9387SGreg Roach * @return string 3238add1155SRico Sonntag */ 3248add1155SRico Sonntag public function lastDeathName(): string 3258add1155SRico Sonntag { 3268add1155SRico Sonntag return $this->getFirstLastEventName(self::EVENT_DEATH, self::SORT_MAX); 3278add1155SRico Sonntag } 3288add1155SRico Sonntag 3298add1155SRico Sonntag /** 3300dcd9387SGreg Roach * @return string 3318add1155SRico Sonntag */ 3328add1155SRico Sonntag public function firstMarriageName(): string 3338add1155SRico Sonntag { 3348add1155SRico Sonntag return $this->getFirstLastEventName(self::EVENT_MARRIAGE, self::SORT_MIN); 3358add1155SRico Sonntag } 3368add1155SRico Sonntag 3378add1155SRico Sonntag /** 3380dcd9387SGreg Roach * @return string 3398add1155SRico Sonntag */ 3408add1155SRico Sonntag public function lastMarriageName(): string 3418add1155SRico Sonntag { 3428add1155SRico Sonntag return $this->getFirstLastEventName(self::EVENT_MARRIAGE, self::SORT_MAX); 3438add1155SRico Sonntag } 3448add1155SRico Sonntag 3458add1155SRico Sonntag /** 3460dcd9387SGreg Roach * @return string 3478add1155SRico Sonntag */ 3488add1155SRico Sonntag public function firstDivorceName(): string 3498add1155SRico Sonntag { 3508add1155SRico Sonntag return $this->getFirstLastEventName(self::EVENT_DIVORCE, self::SORT_MIN); 3518add1155SRico Sonntag } 3528add1155SRico Sonntag 3538add1155SRico Sonntag /** 3540dcd9387SGreg Roach * @return string 3558add1155SRico Sonntag */ 3568add1155SRico Sonntag public function lastDivorceName(): string 3578add1155SRico Sonntag { 3588add1155SRico Sonntag return $this->getFirstLastEventName(self::EVENT_DIVORCE, self::SORT_MAX); 3598add1155SRico Sonntag } 3608add1155SRico Sonntag 3618add1155SRico Sonntag /** 36252f124b0SAlejandro Criado-Pérez * Returns the formatted place of the first/last occurring event. 3638add1155SRico Sonntag * 3648add1155SRico Sonntag * @param string $type The fact to query 3658add1155SRico Sonntag * @param string $operation The sorting operation 3668add1155SRico Sonntag * 3678add1155SRico Sonntag * @return string 3688add1155SRico Sonntag */ 3698add1155SRico Sonntag private function getFirstLastEventPlace(string $type, string $operation): string 3708add1155SRico Sonntag { 3718add1155SRico Sonntag $row = $this->eventQuery($type, $operation); 3728add1155SRico Sonntag 373ef475b14SGreg Roach if ($row != null) { 3746b9cb339SGreg Roach $record = Registry::gedcomRecordFactory()->make($row->id, $this->tree); 3758add1155SRico Sonntag $fact = null; 3768add1155SRico Sonntag 377ef475b14SGreg Roach if ($record instanceof GedcomRecord) { 378820b62dfSGreg Roach $fact = $record->facts([$row->fact])->first(); 3798add1155SRico Sonntag } 3808add1155SRico Sonntag 381820b62dfSGreg Roach if ($fact instanceof Fact) { 382b315f3e1SGreg Roach return $fact->place()->shortName(); 3838add1155SRico Sonntag } 3848add1155SRico Sonntag } 3858add1155SRico Sonntag 386dd7dd2a1SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 3878add1155SRico Sonntag } 3888add1155SRico Sonntag 3898add1155SRico Sonntag /** 3900dcd9387SGreg Roach * @return string 3918add1155SRico Sonntag */ 3928add1155SRico Sonntag public function firstBirthPlace(): string 3938add1155SRico Sonntag { 3948add1155SRico Sonntag return $this->getFirstLastEventPlace(self::EVENT_BIRTH, self::SORT_MIN); 3958add1155SRico Sonntag } 3968add1155SRico Sonntag 3978add1155SRico Sonntag /** 3980dcd9387SGreg Roach * @return string 3998add1155SRico Sonntag */ 4008add1155SRico Sonntag public function lastBirthPlace(): string 4018add1155SRico Sonntag { 4028add1155SRico Sonntag return $this->getFirstLastEventPlace(self::EVENT_BIRTH, self::SORT_MAX); 4038add1155SRico Sonntag } 4048add1155SRico Sonntag 4058add1155SRico Sonntag /** 4060dcd9387SGreg Roach * @return string 4078add1155SRico Sonntag */ 4088add1155SRico Sonntag public function firstDeathPlace(): string 4098add1155SRico Sonntag { 4108add1155SRico Sonntag return $this->getFirstLastEventPlace(self::EVENT_DEATH, self::SORT_MIN); 4118add1155SRico Sonntag } 4128add1155SRico Sonntag 4138add1155SRico Sonntag /** 4140dcd9387SGreg Roach * @return string 4158add1155SRico Sonntag */ 4168add1155SRico Sonntag public function lastDeathPlace(): string 4178add1155SRico Sonntag { 4188add1155SRico Sonntag return $this->getFirstLastEventPlace(self::EVENT_DEATH, self::SORT_MAX); 4198add1155SRico Sonntag } 4208add1155SRico Sonntag 4218add1155SRico Sonntag /** 4220dcd9387SGreg Roach * @return string 4238add1155SRico Sonntag */ 4248add1155SRico Sonntag public function firstMarriagePlace(): string 4258add1155SRico Sonntag { 4268add1155SRico Sonntag return $this->getFirstLastEventPlace(self::EVENT_MARRIAGE, self::SORT_MIN); 4278add1155SRico Sonntag } 4288add1155SRico Sonntag 4298add1155SRico Sonntag /** 4300dcd9387SGreg Roach * @return string 4318add1155SRico Sonntag */ 4328add1155SRico Sonntag public function lastMarriagePlace(): string 4338add1155SRico Sonntag { 4348add1155SRico Sonntag return $this->getFirstLastEventPlace(self::EVENT_MARRIAGE, self::SORT_MAX); 4358add1155SRico Sonntag } 4368add1155SRico Sonntag 4378add1155SRico Sonntag /** 4380dcd9387SGreg Roach * @return string 4398add1155SRico Sonntag */ 4408add1155SRico Sonntag public function firstDivorcePlace(): string 4418add1155SRico Sonntag { 4428add1155SRico Sonntag return $this->getFirstLastEventPlace(self::EVENT_DIVORCE, self::SORT_MIN); 4438add1155SRico Sonntag } 4448add1155SRico Sonntag 4458add1155SRico Sonntag /** 4460dcd9387SGreg Roach * @return string 4478add1155SRico Sonntag */ 4488add1155SRico Sonntag public function lastDivorcePlace(): string 4498add1155SRico Sonntag { 4508add1155SRico Sonntag return $this->getFirstLastEventPlace(self::EVENT_DIVORCE, self::SORT_MAX); 4518add1155SRico Sonntag } 4528add1155SRico Sonntag} 453