18add1155SRico Sonntag<?php 23976b470SGreg Roach 38add1155SRico Sonntag/** 48add1155SRico Sonntag * webtrees: online genealogy 50dcd9387SGreg Roach * Copyright (C) 2020 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 158add1155SRico Sonntag * along with this program. If not, see <http://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; 23820b62dfSGreg Roachuse Fisharebest\Webtrees\Fact; 24a091ac74SGreg Roachuse Fisharebest\Webtrees\Factory; 258add1155SRico Sonntaguse Fisharebest\Webtrees\Functions\FunctionsPrint; 268add1155SRico Sonntaguse Fisharebest\Webtrees\Gedcom; 278add1155SRico Sonntaguse Fisharebest\Webtrees\GedcomTag; 281635452cSGreg Roachuse Fisharebest\Webtrees\Header; 298add1155SRico Sonntaguse Fisharebest\Webtrees\I18N; 308add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Repository\Interfaces\EventRepositoryInterface; 318add1155SRico Sonntaguse Fisharebest\Webtrees\Tree; 328add1155SRico Sonntaguse Illuminate\Database\Capsule\Manager as DB; 338add1155SRico Sonntaguse Illuminate\Database\Eloquent\Model; 348add1155SRico Sonntaguse Illuminate\Database\Query\Builder; 35*9123453fSGreg Roachuse stdClass; 368add1155SRico Sonntag 378add1155SRico Sonntag/** 388add1155SRico Sonntag * A repository providing methods for event related statistics. 398add1155SRico Sonntag */ 408add1155SRico Sonntagclass EventRepository implements EventRepositoryInterface 418add1155SRico Sonntag{ 428add1155SRico Sonntag /** 438add1155SRico Sonntag * Sorting directions. 448add1155SRico Sonntag */ 458add1155SRico Sonntag private const SORT_ASC = 'ASC'; 468add1155SRico Sonntag private const SORT_DESC = 'DESC'; 478add1155SRico Sonntag 488add1155SRico Sonntag /** 498add1155SRico Sonntag * Event facts. 508add1155SRico Sonntag */ 518add1155SRico Sonntag private const EVENT_BIRTH = 'BIRT'; 528add1155SRico Sonntag private const EVENT_DEATH = 'DEAT'; 538add1155SRico Sonntag private const EVENT_MARRIAGE = 'MARR'; 548add1155SRico Sonntag private const EVENT_DIVORCE = 'DIV'; 558add1155SRico Sonntag private const EVENT_ADOPTION = 'ADOP'; 568add1155SRico Sonntag private const EVENT_BURIAL = 'BURI'; 578add1155SRico Sonntag private const EVENT_CENSUS = 'CENS'; 588add1155SRico Sonntag 598add1155SRico Sonntag /** 608add1155SRico Sonntag * @var Tree 618add1155SRico Sonntag */ 628add1155SRico Sonntag private $tree; 638add1155SRico Sonntag 648add1155SRico Sonntag /** 658add1155SRico Sonntag * Constructor. 668add1155SRico Sonntag * 678add1155SRico Sonntag * @param Tree $tree 688add1155SRico Sonntag */ 698add1155SRico Sonntag public function __construct(Tree $tree) 708add1155SRico Sonntag { 718add1155SRico Sonntag $this->tree = $tree; 728add1155SRico Sonntag } 738add1155SRico Sonntag 748add1155SRico Sonntag /** 758add1155SRico Sonntag * Returns the total number of a given list of events (with dates). 768add1155SRico Sonntag * 77*9123453fSGreg Roach * @param array<string> $events The list of events to count (e.g. BIRT, DEAT, ...) 788add1155SRico Sonntag * 798add1155SRico Sonntag * @return int 808add1155SRico Sonntag */ 81c0112ce8SGreg Roach private function getEventCount(array $events): int 828add1155SRico Sonntag { 838add1155SRico Sonntag $query = DB::table('dates') 848add1155SRico Sonntag ->where('d_file', '=', $this->tree->id()); 858add1155SRico Sonntag 868add1155SRico Sonntag $no_types = [ 878add1155SRico Sonntag 'HEAD', 888add1155SRico Sonntag 'CHAN', 898add1155SRico Sonntag ]; 908add1155SRico Sonntag 91*9123453fSGreg Roach if ($events !== []) { 928add1155SRico Sonntag $types = []; 938add1155SRico Sonntag 948add1155SRico Sonntag foreach ($events as $type) { 958add1155SRico Sonntag if (strncmp($type, '!', 1) === 0) { 968add1155SRico Sonntag $no_types[] = substr($type, 1); 978add1155SRico Sonntag } else { 988add1155SRico Sonntag $types[] = $type; 998add1155SRico Sonntag } 1008add1155SRico Sonntag } 1018add1155SRico Sonntag 102*9123453fSGreg Roach if ($types !== []) { 1038add1155SRico Sonntag $query->whereIn('d_fact', $types); 1048add1155SRico Sonntag } 1058add1155SRico Sonntag } 1068add1155SRico Sonntag 1078add1155SRico Sonntag return $query->whereNotIn('d_fact', $no_types) 1088add1155SRico Sonntag ->count(); 1098add1155SRico Sonntag } 1108add1155SRico Sonntag 1118add1155SRico Sonntag /** 112c9a927ceSGreg Roach * @param string[] $events 1130dcd9387SGreg Roach * 1140dcd9387SGreg Roach * @return string 1158add1155SRico Sonntag */ 1168add1155SRico Sonntag public function totalEvents(array $events = []): string 1178add1155SRico Sonntag { 1188add1155SRico Sonntag return I18N::number( 1198add1155SRico Sonntag $this->getEventCount($events) 1208add1155SRico Sonntag ); 1218add1155SRico Sonntag } 1228add1155SRico Sonntag 1238add1155SRico Sonntag /** 1240dcd9387SGreg Roach * @return string 1258add1155SRico Sonntag */ 1268add1155SRico Sonntag public function totalEventsBirth(): string 1278add1155SRico Sonntag { 1288add1155SRico Sonntag return $this->totalEvents(Gedcom::BIRTH_EVENTS); 1298add1155SRico Sonntag } 1308add1155SRico Sonntag 1318add1155SRico Sonntag /** 1320dcd9387SGreg Roach * @return string 1338add1155SRico Sonntag */ 1348add1155SRico Sonntag public function totalBirths(): string 1358add1155SRico Sonntag { 1368add1155SRico Sonntag return $this->totalEvents([self::EVENT_BIRTH]); 1378add1155SRico Sonntag } 1388add1155SRico Sonntag 1398add1155SRico Sonntag /** 1400dcd9387SGreg Roach * @return string 1418add1155SRico Sonntag */ 1428add1155SRico Sonntag public function totalEventsDeath(): string 1438add1155SRico Sonntag { 1448add1155SRico Sonntag return $this->totalEvents(Gedcom::DEATH_EVENTS); 1458add1155SRico Sonntag } 1468add1155SRico Sonntag 1478add1155SRico Sonntag /** 1480dcd9387SGreg Roach * @return string 1498add1155SRico Sonntag */ 1508add1155SRico Sonntag public function totalDeaths(): string 1518add1155SRico Sonntag { 1528add1155SRico Sonntag return $this->totalEvents([self::EVENT_DEATH]); 1538add1155SRico Sonntag } 1548add1155SRico Sonntag 1558add1155SRico Sonntag /** 1560dcd9387SGreg Roach * @return string 1578add1155SRico Sonntag */ 1588add1155SRico Sonntag public function totalEventsMarriage(): string 1598add1155SRico Sonntag { 1608add1155SRico Sonntag return $this->totalEvents(Gedcom::MARRIAGE_EVENTS); 1618add1155SRico Sonntag } 1628add1155SRico Sonntag 1638add1155SRico Sonntag /** 1640dcd9387SGreg Roach * @return string 1658add1155SRico Sonntag */ 1668add1155SRico Sonntag public function totalMarriages(): string 1678add1155SRico Sonntag { 1688add1155SRico Sonntag return $this->totalEvents([self::EVENT_MARRIAGE]); 1698add1155SRico Sonntag } 1708add1155SRico Sonntag 1718add1155SRico Sonntag /** 1720dcd9387SGreg Roach * @return string 1738add1155SRico Sonntag */ 1748add1155SRico Sonntag public function totalEventsDivorce(): string 1758add1155SRico Sonntag { 1768add1155SRico Sonntag return $this->totalEvents(Gedcom::DIVORCE_EVENTS); 1778add1155SRico Sonntag } 1788add1155SRico Sonntag 1798add1155SRico Sonntag /** 1800dcd9387SGreg Roach * @return string 1818add1155SRico Sonntag */ 1828add1155SRico Sonntag public function totalDivorces(): string 1838add1155SRico Sonntag { 1848add1155SRico Sonntag return $this->totalEvents([self::EVENT_DIVORCE]); 1858add1155SRico Sonntag } 1868add1155SRico Sonntag 1878add1155SRico Sonntag /** 1888add1155SRico Sonntag * Retursn the list of common facts used query the data. 1898add1155SRico Sonntag * 190*9123453fSGreg Roach * @return array<string> 1918add1155SRico Sonntag */ 1928add1155SRico Sonntag private function getCommonFacts(): array 1938add1155SRico Sonntag { 1948add1155SRico Sonntag // The list of facts used to limit the query result 1958add1155SRico Sonntag return array_merge( 1968add1155SRico Sonntag Gedcom::BIRTH_EVENTS, 1978add1155SRico Sonntag Gedcom::MARRIAGE_EVENTS, 1988add1155SRico Sonntag Gedcom::DIVORCE_EVENTS, 1998add1155SRico Sonntag Gedcom::DEATH_EVENTS 2008add1155SRico Sonntag ); 2018add1155SRico Sonntag } 2028add1155SRico Sonntag 2038add1155SRico Sonntag /** 2040dcd9387SGreg Roach * @return string 2058add1155SRico Sonntag */ 2068add1155SRico Sonntag public function totalEventsOther(): string 2078add1155SRico Sonntag { 2088add1155SRico Sonntag $no_facts = array_map( 2096c2179e2SGreg Roach static function (string $fact): string { 2108add1155SRico Sonntag return '!' . $fact; 2118add1155SRico Sonntag }, 2128add1155SRico Sonntag $this->getCommonFacts() 2138add1155SRico Sonntag ); 2148add1155SRico Sonntag 2158add1155SRico Sonntag return $this->totalEvents($no_facts); 2168add1155SRico Sonntag } 2178add1155SRico Sonntag 2188add1155SRico Sonntag /** 2198add1155SRico Sonntag * Returns the first/last event record from the given list of event facts. 2208add1155SRico Sonntag * 2218add1155SRico Sonntag * @param string $direction The sorting direction of the query (To return first or last record) 2228add1155SRico Sonntag * 223*9123453fSGreg Roach * @return stdClass|null 2248add1155SRico Sonntag */ 225*9123453fSGreg Roach private function eventQuery(string $direction): ?stdClass 2268add1155SRico Sonntag { 2278add1155SRico Sonntag return DB::table('dates') 2288add1155SRico Sonntag ->select(['d_gid as id', 'd_year as year', 'd_fact AS fact', 'd_type AS type']) 2298add1155SRico Sonntag ->where('d_file', '=', $this->tree->id()) 2301635452cSGreg Roach ->where('d_gid', '<>', Header::RECORD_TYPE) 2318add1155SRico Sonntag ->whereIn('d_fact', $this->getCommonFacts()) 2328add1155SRico Sonntag ->where('d_julianday1', '<>', 0) 2338add1155SRico Sonntag ->orderBy('d_julianday1', $direction) 2348add1155SRico Sonntag ->orderBy('d_type') 2358add1155SRico Sonntag ->first(); 2368add1155SRico Sonntag } 2378add1155SRico Sonntag 2388add1155SRico Sonntag /** 2398add1155SRico Sonntag * Returns the formatted first/last occuring event. 2408add1155SRico Sonntag * 2418add1155SRico Sonntag * @param string $direction The sorting direction 2428add1155SRico Sonntag * 2438add1155SRico Sonntag * @return string 2448add1155SRico Sonntag */ 2458add1155SRico Sonntag private function getFirstLastEvent(string $direction): string 2468add1155SRico Sonntag { 2478add1155SRico Sonntag $row = $this->eventQuery($direction); 248dd7dd2a1SRico Sonntag $result = I18N::translate('This information is not available.'); 2498add1155SRico Sonntag 2508add1155SRico Sonntag if ($row) { 251a091ac74SGreg Roach $record = Factory::gedcomRecord()->make($row->id, $this->tree); 2528add1155SRico Sonntag 2538add1155SRico Sonntag if ($record && $record->canShow()) { 2548add1155SRico Sonntag $result = $record->formatList(); 2558add1155SRico Sonntag } else { 2568add1155SRico Sonntag $result = I18N::translate('This information is private and cannot be shown.'); 2578add1155SRico Sonntag } 2588add1155SRico Sonntag } 2598add1155SRico Sonntag 2608add1155SRico Sonntag return $result; 2618add1155SRico Sonntag } 2628add1155SRico Sonntag 2638add1155SRico Sonntag /** 2640dcd9387SGreg Roach * @return string 2658add1155SRico Sonntag */ 2668add1155SRico Sonntag public function firstEvent(): string 2678add1155SRico Sonntag { 2688add1155SRico Sonntag return $this->getFirstLastEvent(self::SORT_ASC); 2698add1155SRico Sonntag } 2708add1155SRico Sonntag 2718add1155SRico Sonntag /** 2720dcd9387SGreg Roach * @return string 2738add1155SRico Sonntag */ 2748add1155SRico Sonntag public function lastEvent(): string 2758add1155SRico Sonntag { 2768add1155SRico Sonntag return $this->getFirstLastEvent(self::SORT_DESC); 2778add1155SRico Sonntag } 2788add1155SRico Sonntag 2798add1155SRico Sonntag /** 2808add1155SRico Sonntag * Returns the formatted year of the first/last occuring event. 2818add1155SRico Sonntag * 2828add1155SRico Sonntag * @param string $direction The sorting direction 2838add1155SRico Sonntag * 2848add1155SRico Sonntag * @return string 2858add1155SRico Sonntag */ 2868add1155SRico Sonntag private function getFirstLastEventYear(string $direction): string 2878add1155SRico Sonntag { 2888add1155SRico Sonntag $row = $this->eventQuery($direction); 2898add1155SRico Sonntag 2908add1155SRico Sonntag if (!$row) { 2918add1155SRico Sonntag return ''; 2928add1155SRico Sonntag } 2938add1155SRico Sonntag 2948add1155SRico Sonntag return (new Date($row->type . ' ' . $row->year)) 2958add1155SRico Sonntag ->display(); 2968add1155SRico Sonntag } 2978add1155SRico Sonntag 2988add1155SRico Sonntag /** 2990dcd9387SGreg Roach * @return string 3008add1155SRico Sonntag */ 3018add1155SRico Sonntag public function firstEventYear(): string 3028add1155SRico Sonntag { 3038add1155SRico Sonntag return $this->getFirstLastEventYear(self::SORT_ASC); 3048add1155SRico Sonntag } 3058add1155SRico Sonntag 3068add1155SRico Sonntag /** 3070dcd9387SGreg Roach * @return string 3088add1155SRico Sonntag */ 3098add1155SRico Sonntag public function lastEventYear(): string 3108add1155SRico Sonntag { 3118add1155SRico Sonntag return $this->getFirstLastEventYear(self::SORT_DESC); 3128add1155SRico Sonntag } 3138add1155SRico Sonntag 3148add1155SRico Sonntag /** 3158add1155SRico Sonntag * Returns the formatted type of the first/last occuring event. 3168add1155SRico Sonntag * 3178add1155SRico Sonntag * @param string $direction The sorting direction 3188add1155SRico Sonntag * 3198add1155SRico Sonntag * @return string 3208add1155SRico Sonntag */ 3218add1155SRico Sonntag private function getFirstLastEventType(string $direction): string 3228add1155SRico Sonntag { 3238add1155SRico Sonntag $row = $this->eventQuery($direction); 3248add1155SRico Sonntag 3258add1155SRico Sonntag if ($row) { 3268add1155SRico Sonntag $event_types = [ 3278add1155SRico Sonntag self::EVENT_BIRTH => I18N::translate('birth'), 3288add1155SRico Sonntag self::EVENT_DEATH => I18N::translate('death'), 3298add1155SRico Sonntag self::EVENT_MARRIAGE => I18N::translate('marriage'), 3308add1155SRico Sonntag self::EVENT_ADOPTION => I18N::translate('adoption'), 3318add1155SRico Sonntag self::EVENT_BURIAL => I18N::translate('burial'), 3328add1155SRico Sonntag self::EVENT_CENSUS => I18N::translate('census added'), 3338add1155SRico Sonntag ]; 3348add1155SRico Sonntag 3358add1155SRico Sonntag return $event_types[$row->fact] ?? GedcomTag::getLabel($row->fact); 3368add1155SRico Sonntag } 3378add1155SRico Sonntag 3388add1155SRico Sonntag return ''; 3398add1155SRico Sonntag } 3408add1155SRico Sonntag 3418add1155SRico Sonntag /** 3420dcd9387SGreg Roach * @return string 3438add1155SRico Sonntag */ 3448add1155SRico Sonntag public function firstEventType(): string 3458add1155SRico Sonntag { 3468add1155SRico Sonntag return $this->getFirstLastEventType(self::SORT_ASC); 3478add1155SRico Sonntag } 3488add1155SRico Sonntag 3498add1155SRico Sonntag /** 3500dcd9387SGreg Roach * @return string 3518add1155SRico Sonntag */ 3528add1155SRico Sonntag public function lastEventType(): string 3538add1155SRico Sonntag { 3548add1155SRico Sonntag return $this->getFirstLastEventType(self::SORT_DESC); 3558add1155SRico Sonntag } 3568add1155SRico Sonntag 3578add1155SRico Sonntag /** 3588add1155SRico Sonntag * Returns the formatted name of the first/last occuring event. 3598add1155SRico Sonntag * 3608add1155SRico Sonntag * @param string $direction The sorting direction 3618add1155SRico Sonntag * 3628add1155SRico Sonntag * @return string 3638add1155SRico Sonntag */ 3648add1155SRico Sonntag private function getFirstLastEventName(string $direction): string 3658add1155SRico Sonntag { 3668add1155SRico Sonntag $row = $this->eventQuery($direction); 3678add1155SRico Sonntag 3688add1155SRico Sonntag if ($row) { 369a091ac74SGreg Roach $record = Factory::gedcomRecord()->make($row->id, $this->tree); 3708add1155SRico Sonntag 3718add1155SRico Sonntag if ($record) { 37239ca88baSGreg Roach return '<a href="' . e($record->url()) . '">' . $record->fullName() . '</a>'; 3738add1155SRico Sonntag } 3748add1155SRico Sonntag } 3758add1155SRico Sonntag 3768add1155SRico Sonntag return ''; 3778add1155SRico Sonntag } 3788add1155SRico Sonntag 3798add1155SRico Sonntag /** 3800dcd9387SGreg Roach * @return string 3818add1155SRico Sonntag */ 3828add1155SRico Sonntag public function firstEventName(): string 3838add1155SRico Sonntag { 3848add1155SRico Sonntag return $this->getFirstLastEventName(self::SORT_ASC); 3858add1155SRico Sonntag } 3868add1155SRico Sonntag 3878add1155SRico Sonntag /** 3880dcd9387SGreg Roach * @return string 3898add1155SRico Sonntag */ 3908add1155SRico Sonntag public function lastEventName(): string 3918add1155SRico Sonntag { 3928add1155SRico Sonntag return $this->getFirstLastEventName(self::SORT_DESC); 3938add1155SRico Sonntag } 3948add1155SRico Sonntag 3958add1155SRico Sonntag /** 3968add1155SRico Sonntag * Returns the formatted place of the first/last occuring event. 3978add1155SRico Sonntag * 3988add1155SRico Sonntag * @param string $direction The sorting direction 3998add1155SRico Sonntag * 4008add1155SRico Sonntag * @return string 4018add1155SRico Sonntag */ 4028add1155SRico Sonntag private function getFirstLastEventPlace(string $direction): string 4038add1155SRico Sonntag { 4048add1155SRico Sonntag $row = $this->eventQuery($direction); 4058add1155SRico Sonntag 4068add1155SRico Sonntag if ($row) { 407a091ac74SGreg Roach $record = Factory::gedcomRecord()->make($row->id, $this->tree); 4088add1155SRico Sonntag $fact = null; 4098add1155SRico Sonntag 4108add1155SRico Sonntag if ($record) { 411820b62dfSGreg Roach $fact = $record->facts([$row->fact])->first(); 4128add1155SRico Sonntag } 4138add1155SRico Sonntag 414820b62dfSGreg Roach if ($fact instanceof Fact) { 4158add1155SRico Sonntag return FunctionsPrint::formatFactPlace($fact, true, true, true); 4168add1155SRico Sonntag } 4178add1155SRico Sonntag } 4188add1155SRico Sonntag 4198add1155SRico Sonntag return I18N::translate('Private'); 4208add1155SRico Sonntag } 4218add1155SRico Sonntag 4228add1155SRico Sonntag /** 4230dcd9387SGreg Roach * @return string 4248add1155SRico Sonntag */ 4258add1155SRico Sonntag public function firstEventPlace(): string 4268add1155SRico Sonntag { 4278add1155SRico Sonntag return $this->getFirstLastEventPlace(self::SORT_ASC); 4288add1155SRico Sonntag } 4298add1155SRico Sonntag 4308add1155SRico Sonntag /** 4310dcd9387SGreg Roach * @return string 4328add1155SRico Sonntag */ 4338add1155SRico Sonntag public function lastEventPlace(): string 4348add1155SRico Sonntag { 4358add1155SRico Sonntag return $this->getFirstLastEventPlace(self::SORT_DESC); 4368add1155SRico Sonntag } 4378add1155SRico Sonntag} 438