1*8add1155SRico Sonntag<?php 2*8add1155SRico Sonntag/** 3*8add1155SRico Sonntag * webtrees: online genealogy 4*8add1155SRico Sonntag * Copyright (C) 2018 webtrees development team 5*8add1155SRico Sonntag * This program is free software: you can redistribute it and/or modify 6*8add1155SRico Sonntag * it under the terms of the GNU General Public License as published by 7*8add1155SRico Sonntag * the Free Software Foundation, either version 3 of the License, or 8*8add1155SRico Sonntag * (at your option) any later version. 9*8add1155SRico Sonntag * This program is distributed in the hope that it will be useful, 10*8add1155SRico Sonntag * but WITHOUT ANY WARRANTY; without even the implied warranty of 11*8add1155SRico Sonntag * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12*8add1155SRico Sonntag * GNU General Public License for more details. 13*8add1155SRico Sonntag * You should have received a copy of the GNU General Public License 14*8add1155SRico Sonntag * along with this program. If not, see <http://www.gnu.org/licenses/>. 15*8add1155SRico Sonntag */ 16*8add1155SRico Sonntagdeclare(strict_types=1); 17*8add1155SRico Sonntag 18*8add1155SRico Sonntagnamespace Fisharebest\Webtrees\Statistics\Repository; 19*8add1155SRico Sonntag 20*8add1155SRico Sonntaguse Fisharebest\Webtrees\Date; 21*8add1155SRico Sonntaguse Fisharebest\Webtrees\Functions\FunctionsPrint; 22*8add1155SRico Sonntaguse Fisharebest\Webtrees\Gedcom; 23*8add1155SRico Sonntaguse Fisharebest\Webtrees\GedcomRecord; 24*8add1155SRico Sonntaguse Fisharebest\Webtrees\GedcomTag; 25*8add1155SRico Sonntaguse Fisharebest\Webtrees\I18N; 26*8add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Repository\Interfaces\EventRepositoryInterface; 27*8add1155SRico Sonntaguse Fisharebest\Webtrees\Tree; 28*8add1155SRico Sonntaguse Illuminate\Database\Capsule\Manager as DB; 29*8add1155SRico Sonntaguse Illuminate\Database\Eloquent\Model; 30*8add1155SRico Sonntaguse Illuminate\Database\Query\Builder; 31*8add1155SRico Sonntag 32*8add1155SRico Sonntag/** 33*8add1155SRico Sonntag * A repository providing methods for event related statistics. 34*8add1155SRico Sonntag */ 35*8add1155SRico Sonntagclass EventRepository implements EventRepositoryInterface 36*8add1155SRico Sonntag{ 37*8add1155SRico Sonntag /** 38*8add1155SRico Sonntag * Sorting directions. 39*8add1155SRico Sonntag */ 40*8add1155SRico Sonntag private const SORT_ASC = 'ASC'; 41*8add1155SRico Sonntag private const SORT_DESC = 'DESC'; 42*8add1155SRico Sonntag 43*8add1155SRico Sonntag /** 44*8add1155SRico Sonntag * Event facts. 45*8add1155SRico Sonntag */ 46*8add1155SRico Sonntag private const EVENT_BIRTH = 'BIRT'; 47*8add1155SRico Sonntag private const EVENT_DEATH = 'DEAT'; 48*8add1155SRico Sonntag private const EVENT_MARRIAGE = 'MARR'; 49*8add1155SRico Sonntag private const EVENT_DIVORCE = 'DIV'; 50*8add1155SRico Sonntag private const EVENT_ADOPTION = 'ADOP'; 51*8add1155SRico Sonntag private const EVENT_BURIAL = 'BURI'; 52*8add1155SRico Sonntag private const EVENT_CENSUS = 'CENS'; 53*8add1155SRico Sonntag 54*8add1155SRico Sonntag /** 55*8add1155SRico Sonntag * @var Tree 56*8add1155SRico Sonntag */ 57*8add1155SRico Sonntag private $tree; 58*8add1155SRico Sonntag 59*8add1155SRico Sonntag /** 60*8add1155SRico Sonntag * Constructor. 61*8add1155SRico Sonntag * 62*8add1155SRico Sonntag * @param Tree $tree 63*8add1155SRico Sonntag */ 64*8add1155SRico Sonntag public function __construct(Tree $tree) 65*8add1155SRico Sonntag { 66*8add1155SRico Sonntag $this->tree = $tree; 67*8add1155SRico Sonntag } 68*8add1155SRico Sonntag 69*8add1155SRico Sonntag /** 70*8add1155SRico Sonntag * Returns the total number of a given list of events (with dates). 71*8add1155SRico Sonntag * 72*8add1155SRico Sonntag * @param array $events The list of events to count (e.g. BIRT, DEAT, ...) 73*8add1155SRico Sonntag * 74*8add1155SRico Sonntag * @return int 75*8add1155SRico Sonntag */ 76*8add1155SRico Sonntag private function getEventCount(array $events = []): int 77*8add1155SRico Sonntag { 78*8add1155SRico Sonntag $query = DB::table('dates') 79*8add1155SRico Sonntag ->where('d_file', '=', $this->tree->id()); 80*8add1155SRico Sonntag 81*8add1155SRico Sonntag $no_types = [ 82*8add1155SRico Sonntag 'HEAD', 83*8add1155SRico Sonntag 'CHAN', 84*8add1155SRico Sonntag ]; 85*8add1155SRico Sonntag 86*8add1155SRico Sonntag if ($events) { 87*8add1155SRico Sonntag $types = []; 88*8add1155SRico Sonntag 89*8add1155SRico Sonntag foreach ($events as $type) { 90*8add1155SRico Sonntag if (strncmp($type, '!', 1) === 0) { 91*8add1155SRico Sonntag $no_types[] = substr($type, 1); 92*8add1155SRico Sonntag } else { 93*8add1155SRico Sonntag $types[] = $type; 94*8add1155SRico Sonntag } 95*8add1155SRico Sonntag } 96*8add1155SRico Sonntag 97*8add1155SRico Sonntag if ($types) { 98*8add1155SRico Sonntag $query->whereIn('d_fact', $types); 99*8add1155SRico Sonntag } 100*8add1155SRico Sonntag } 101*8add1155SRico Sonntag 102*8add1155SRico Sonntag return $query->whereNotIn('d_fact', $no_types) 103*8add1155SRico Sonntag ->count(); 104*8add1155SRico Sonntag } 105*8add1155SRico Sonntag 106*8add1155SRico Sonntag /** 107*8add1155SRico Sonntag * @inheritDoc 108*8add1155SRico Sonntag */ 109*8add1155SRico Sonntag public function totalEvents(array $events = []): string 110*8add1155SRico Sonntag { 111*8add1155SRico Sonntag return I18N::number( 112*8add1155SRico Sonntag $this->getEventCount($events) 113*8add1155SRico Sonntag ); 114*8add1155SRico Sonntag } 115*8add1155SRico Sonntag 116*8add1155SRico Sonntag /** 117*8add1155SRico Sonntag * @inheritDoc 118*8add1155SRico Sonntag */ 119*8add1155SRico Sonntag public function totalEventsBirth(): string 120*8add1155SRico Sonntag { 121*8add1155SRico Sonntag return $this->totalEvents(Gedcom::BIRTH_EVENTS); 122*8add1155SRico Sonntag } 123*8add1155SRico Sonntag 124*8add1155SRico Sonntag /** 125*8add1155SRico Sonntag * @inheritDoc 126*8add1155SRico Sonntag */ 127*8add1155SRico Sonntag public function totalBirths(): string 128*8add1155SRico Sonntag { 129*8add1155SRico Sonntag return $this->totalEvents([self::EVENT_BIRTH]); 130*8add1155SRico Sonntag } 131*8add1155SRico Sonntag 132*8add1155SRico Sonntag /** 133*8add1155SRico Sonntag * @inheritDoc 134*8add1155SRico Sonntag */ 135*8add1155SRico Sonntag public function totalEventsDeath(): string 136*8add1155SRico Sonntag { 137*8add1155SRico Sonntag return $this->totalEvents(Gedcom::DEATH_EVENTS); 138*8add1155SRico Sonntag } 139*8add1155SRico Sonntag 140*8add1155SRico Sonntag /** 141*8add1155SRico Sonntag * @inheritDoc 142*8add1155SRico Sonntag */ 143*8add1155SRico Sonntag public function totalDeaths(): string 144*8add1155SRico Sonntag { 145*8add1155SRico Sonntag return $this->totalEvents([self::EVENT_DEATH]); 146*8add1155SRico Sonntag } 147*8add1155SRico Sonntag 148*8add1155SRico Sonntag /** 149*8add1155SRico Sonntag * @inheritDoc 150*8add1155SRico Sonntag */ 151*8add1155SRico Sonntag public function totalEventsMarriage(): string 152*8add1155SRico Sonntag { 153*8add1155SRico Sonntag return $this->totalEvents(Gedcom::MARRIAGE_EVENTS); 154*8add1155SRico Sonntag } 155*8add1155SRico Sonntag 156*8add1155SRico Sonntag /** 157*8add1155SRico Sonntag * @inheritDoc 158*8add1155SRico Sonntag */ 159*8add1155SRico Sonntag public function totalMarriages(): string 160*8add1155SRico Sonntag { 161*8add1155SRico Sonntag return $this->totalEvents([self::EVENT_MARRIAGE]); 162*8add1155SRico Sonntag } 163*8add1155SRico Sonntag 164*8add1155SRico Sonntag /** 165*8add1155SRico Sonntag * @inheritDoc 166*8add1155SRico Sonntag */ 167*8add1155SRico Sonntag public function totalEventsDivorce(): string 168*8add1155SRico Sonntag { 169*8add1155SRico Sonntag return $this->totalEvents(Gedcom::DIVORCE_EVENTS); 170*8add1155SRico Sonntag } 171*8add1155SRico Sonntag 172*8add1155SRico Sonntag /** 173*8add1155SRico Sonntag * @inheritDoc 174*8add1155SRico Sonntag */ 175*8add1155SRico Sonntag public function totalDivorces(): string 176*8add1155SRico Sonntag { 177*8add1155SRico Sonntag return $this->totalEvents([self::EVENT_DIVORCE]); 178*8add1155SRico Sonntag } 179*8add1155SRico Sonntag 180*8add1155SRico Sonntag /** 181*8add1155SRico Sonntag * Retursn the list of common facts used query the data. 182*8add1155SRico Sonntag * 183*8add1155SRico Sonntag * @return array 184*8add1155SRico Sonntag */ 185*8add1155SRico Sonntag private function getCommonFacts(): array 186*8add1155SRico Sonntag { 187*8add1155SRico Sonntag // The list of facts used to limit the query result 188*8add1155SRico Sonntag return array_merge( 189*8add1155SRico Sonntag Gedcom::BIRTH_EVENTS, 190*8add1155SRico Sonntag Gedcom::MARRIAGE_EVENTS, 191*8add1155SRico Sonntag Gedcom::DIVORCE_EVENTS, 192*8add1155SRico Sonntag Gedcom::DEATH_EVENTS 193*8add1155SRico Sonntag ); 194*8add1155SRico Sonntag } 195*8add1155SRico Sonntag 196*8add1155SRico Sonntag /** 197*8add1155SRico Sonntag * @inheritDoc 198*8add1155SRico Sonntag */ 199*8add1155SRico Sonntag public function totalEventsOther(): string 200*8add1155SRico Sonntag { 201*8add1155SRico Sonntag $no_facts = array_map( 202*8add1155SRico Sonntag function (string $fact) { 203*8add1155SRico Sonntag return '!' . $fact; 204*8add1155SRico Sonntag }, 205*8add1155SRico Sonntag $this->getCommonFacts() 206*8add1155SRico Sonntag ); 207*8add1155SRico Sonntag 208*8add1155SRico Sonntag return $this->totalEvents($no_facts); 209*8add1155SRico Sonntag } 210*8add1155SRico Sonntag 211*8add1155SRico Sonntag /** 212*8add1155SRico Sonntag * Returns the first/last event record from the given list of event facts. 213*8add1155SRico Sonntag * 214*8add1155SRico Sonntag * @param string $direction The sorting direction of the query (To return first or last record) 215*8add1155SRico Sonntag * 216*8add1155SRico Sonntag * @return Model|Builder|object|null 217*8add1155SRico Sonntag */ 218*8add1155SRico Sonntag private function eventQuery(string $direction) 219*8add1155SRico Sonntag { 220*8add1155SRico Sonntag return DB::table('dates') 221*8add1155SRico Sonntag ->select(['d_gid as id', 'd_year as year', 'd_fact AS fact', 'd_type AS type']) 222*8add1155SRico Sonntag ->where('d_file', '=', $this->tree->id()) 223*8add1155SRico Sonntag ->where('d_gid', '<>', 'HEAD') 224*8add1155SRico Sonntag ->whereIn('d_fact', $this->getCommonFacts()) 225*8add1155SRico Sonntag ->where('d_julianday1', '<>', 0) 226*8add1155SRico Sonntag ->orderBy('d_julianday1', $direction) 227*8add1155SRico Sonntag ->orderBy('d_type') 228*8add1155SRico Sonntag ->first(); 229*8add1155SRico Sonntag } 230*8add1155SRico Sonntag 231*8add1155SRico Sonntag /** 232*8add1155SRico Sonntag * Returns the formatted first/last occuring event. 233*8add1155SRico Sonntag * 234*8add1155SRico Sonntag * @param string $direction The sorting direction 235*8add1155SRico Sonntag * 236*8add1155SRico Sonntag * @return string 237*8add1155SRico Sonntag */ 238*8add1155SRico Sonntag private function getFirstLastEvent(string $direction): string 239*8add1155SRico Sonntag { 240*8add1155SRico Sonntag $row = $this->eventQuery($direction); 241*8add1155SRico Sonntag $result = ''; 242*8add1155SRico Sonntag 243*8add1155SRico Sonntag if ($row) { 244*8add1155SRico Sonntag $record = GedcomRecord::getInstance($row->id, $this->tree); 245*8add1155SRico Sonntag 246*8add1155SRico Sonntag if ($record && $record->canShow()) { 247*8add1155SRico Sonntag $result = $record->formatList(); 248*8add1155SRico Sonntag } else { 249*8add1155SRico Sonntag $result = I18N::translate('This information is private and cannot be shown.'); 250*8add1155SRico Sonntag } 251*8add1155SRico Sonntag } 252*8add1155SRico Sonntag 253*8add1155SRico Sonntag return $result; 254*8add1155SRico Sonntag } 255*8add1155SRico Sonntag 256*8add1155SRico Sonntag /** 257*8add1155SRico Sonntag * @inheritDoc 258*8add1155SRico Sonntag */ 259*8add1155SRico Sonntag public function firstEvent(): string 260*8add1155SRico Sonntag { 261*8add1155SRico Sonntag return $this->getFirstLastEvent(self::SORT_ASC); 262*8add1155SRico Sonntag } 263*8add1155SRico Sonntag 264*8add1155SRico Sonntag /** 265*8add1155SRico Sonntag * @inheritDoc 266*8add1155SRico Sonntag */ 267*8add1155SRico Sonntag public function lastEvent(): string 268*8add1155SRico Sonntag { 269*8add1155SRico Sonntag return $this->getFirstLastEvent(self::SORT_DESC); 270*8add1155SRico Sonntag } 271*8add1155SRico Sonntag 272*8add1155SRico Sonntag /** 273*8add1155SRico Sonntag * Returns the formatted year of the first/last occuring event. 274*8add1155SRico Sonntag * 275*8add1155SRico Sonntag * @param string $direction The sorting direction 276*8add1155SRico Sonntag * 277*8add1155SRico Sonntag * @return string 278*8add1155SRico Sonntag */ 279*8add1155SRico Sonntag private function getFirstLastEventYear(string $direction): string 280*8add1155SRico Sonntag { 281*8add1155SRico Sonntag $row = $this->eventQuery($direction); 282*8add1155SRico Sonntag 283*8add1155SRico Sonntag if (!$row) { 284*8add1155SRico Sonntag return ''; 285*8add1155SRico Sonntag } 286*8add1155SRico Sonntag 287*8add1155SRico Sonntag return (new Date($row->type . ' ' . $row->year)) 288*8add1155SRico Sonntag ->display(); 289*8add1155SRico Sonntag } 290*8add1155SRico Sonntag 291*8add1155SRico Sonntag /** 292*8add1155SRico Sonntag * @inheritDoc 293*8add1155SRico Sonntag */ 294*8add1155SRico Sonntag public function firstEventYear(): string 295*8add1155SRico Sonntag { 296*8add1155SRico Sonntag return $this->getFirstLastEventYear(self::SORT_ASC); 297*8add1155SRico Sonntag } 298*8add1155SRico Sonntag 299*8add1155SRico Sonntag /** 300*8add1155SRico Sonntag * @inheritDoc 301*8add1155SRico Sonntag */ 302*8add1155SRico Sonntag public function lastEventYear(): string 303*8add1155SRico Sonntag { 304*8add1155SRico Sonntag return $this->getFirstLastEventYear(self::SORT_DESC); 305*8add1155SRico Sonntag } 306*8add1155SRico Sonntag 307*8add1155SRico Sonntag /** 308*8add1155SRico Sonntag * Returns the formatted type of the first/last occuring event. 309*8add1155SRico Sonntag * 310*8add1155SRico Sonntag * @param string $direction The sorting direction 311*8add1155SRico Sonntag * 312*8add1155SRico Sonntag * @return string 313*8add1155SRico Sonntag */ 314*8add1155SRico Sonntag private function getFirstLastEventType(string $direction): string 315*8add1155SRico Sonntag { 316*8add1155SRico Sonntag $row = $this->eventQuery($direction); 317*8add1155SRico Sonntag 318*8add1155SRico Sonntag if ($row) { 319*8add1155SRico Sonntag $event_types = [ 320*8add1155SRico Sonntag self::EVENT_BIRTH => I18N::translate('birth'), 321*8add1155SRico Sonntag self::EVENT_DEATH => I18N::translate('death'), 322*8add1155SRico Sonntag self::EVENT_MARRIAGE => I18N::translate('marriage'), 323*8add1155SRico Sonntag self::EVENT_ADOPTION => I18N::translate('adoption'), 324*8add1155SRico Sonntag self::EVENT_BURIAL => I18N::translate('burial'), 325*8add1155SRico Sonntag self::EVENT_CENSUS => I18N::translate('census added'), 326*8add1155SRico Sonntag ]; 327*8add1155SRico Sonntag 328*8add1155SRico Sonntag return $event_types[$row->fact] ?? GedcomTag::getLabel($row->fact); 329*8add1155SRico Sonntag } 330*8add1155SRico Sonntag 331*8add1155SRico Sonntag return ''; 332*8add1155SRico Sonntag } 333*8add1155SRico Sonntag 334*8add1155SRico Sonntag /** 335*8add1155SRico Sonntag * @inheritDoc 336*8add1155SRico Sonntag */ 337*8add1155SRico Sonntag public function firstEventType(): string 338*8add1155SRico Sonntag { 339*8add1155SRico Sonntag return $this->getFirstLastEventType(self::SORT_ASC); 340*8add1155SRico Sonntag } 341*8add1155SRico Sonntag 342*8add1155SRico Sonntag /** 343*8add1155SRico Sonntag * @inheritDoc 344*8add1155SRico Sonntag */ 345*8add1155SRico Sonntag public function lastEventType(): string 346*8add1155SRico Sonntag { 347*8add1155SRico Sonntag return $this->getFirstLastEventType(self::SORT_DESC); 348*8add1155SRico Sonntag } 349*8add1155SRico Sonntag 350*8add1155SRico Sonntag /** 351*8add1155SRico Sonntag * Returns the formatted name of the first/last occuring event. 352*8add1155SRico Sonntag * 353*8add1155SRico Sonntag * @param string $direction The sorting direction 354*8add1155SRico Sonntag * 355*8add1155SRico Sonntag * @return string 356*8add1155SRico Sonntag */ 357*8add1155SRico Sonntag private function getFirstLastEventName(string $direction): string 358*8add1155SRico Sonntag { 359*8add1155SRico Sonntag $row = $this->eventQuery($direction); 360*8add1155SRico Sonntag 361*8add1155SRico Sonntag if ($row) { 362*8add1155SRico Sonntag $record = GedcomRecord::getInstance($row->id, $this->tree); 363*8add1155SRico Sonntag 364*8add1155SRico Sonntag if ($record) { 365*8add1155SRico Sonntag return '<a href="' . e($record->url()) . '">' . $record->getFullName() . '</a>'; 366*8add1155SRico Sonntag } 367*8add1155SRico Sonntag } 368*8add1155SRico Sonntag 369*8add1155SRico Sonntag return ''; 370*8add1155SRico Sonntag } 371*8add1155SRico Sonntag 372*8add1155SRico Sonntag /** 373*8add1155SRico Sonntag * @inheritDoc 374*8add1155SRico Sonntag */ 375*8add1155SRico Sonntag public function firstEventName(): string 376*8add1155SRico Sonntag { 377*8add1155SRico Sonntag return $this->getFirstLastEventName(self::SORT_ASC); 378*8add1155SRico Sonntag } 379*8add1155SRico Sonntag 380*8add1155SRico Sonntag /** 381*8add1155SRico Sonntag * @inheritDoc 382*8add1155SRico Sonntag */ 383*8add1155SRico Sonntag public function lastEventName(): string 384*8add1155SRico Sonntag { 385*8add1155SRico Sonntag return $this->getFirstLastEventName(self::SORT_DESC); 386*8add1155SRico Sonntag } 387*8add1155SRico Sonntag 388*8add1155SRico Sonntag /** 389*8add1155SRico Sonntag * Returns the formatted place of the first/last occuring event. 390*8add1155SRico Sonntag * 391*8add1155SRico Sonntag * @param string $direction The sorting direction 392*8add1155SRico Sonntag * 393*8add1155SRico Sonntag * @return string 394*8add1155SRico Sonntag */ 395*8add1155SRico Sonntag private function getFirstLastEventPlace(string $direction): string 396*8add1155SRico Sonntag { 397*8add1155SRico Sonntag $row = $this->eventQuery($direction); 398*8add1155SRico Sonntag 399*8add1155SRico Sonntag if ($row) { 400*8add1155SRico Sonntag $record = GedcomRecord::getInstance($row->id, $this->tree); 401*8add1155SRico Sonntag $fact = null; 402*8add1155SRico Sonntag 403*8add1155SRico Sonntag if ($record) { 404*8add1155SRico Sonntag $fact = $record->getFirstFact($row->fact); 405*8add1155SRico Sonntag } 406*8add1155SRico Sonntag 407*8add1155SRico Sonntag if ($fact) { 408*8add1155SRico Sonntag return FunctionsPrint::formatFactPlace($fact, true, true, true); 409*8add1155SRico Sonntag } 410*8add1155SRico Sonntag } 411*8add1155SRico Sonntag 412*8add1155SRico Sonntag return I18N::translate('Private'); 413*8add1155SRico Sonntag } 414*8add1155SRico Sonntag 415*8add1155SRico Sonntag /** 416*8add1155SRico Sonntag * @inheritDoc 417*8add1155SRico Sonntag */ 418*8add1155SRico Sonntag public function firstEventPlace(): string 419*8add1155SRico Sonntag { 420*8add1155SRico Sonntag return $this->getFirstLastEventPlace(self::SORT_ASC); 421*8add1155SRico Sonntag } 422*8add1155SRico Sonntag 423*8add1155SRico Sonntag /** 424*8add1155SRico Sonntag * @inheritDoc 425*8add1155SRico Sonntag */ 426*8add1155SRico Sonntag public function lastEventPlace(): string 427*8add1155SRico Sonntag { 428*8add1155SRico Sonntag return $this->getFirstLastEventPlace(self::SORT_DESC); 429*8add1155SRico Sonntag } 430*8add1155SRico Sonntag} 431