18add1155SRico Sonntag<?php 28add1155SRico Sonntag/** 38add1155SRico Sonntag * webtrees: online genealogy 4*242a7862SGreg Roach * Copyright (C) 2019 webtrees development team 58add1155SRico Sonntag * This program is free software: you can redistribute it and/or modify 68add1155SRico Sonntag * it under the terms of the GNU General Public License as published by 78add1155SRico Sonntag * the Free Software Foundation, either version 3 of the License, or 88add1155SRico Sonntag * (at your option) any later version. 98add1155SRico Sonntag * This program is distributed in the hope that it will be useful, 108add1155SRico Sonntag * but WITHOUT ANY WARRANTY; without even the implied warranty of 118add1155SRico Sonntag * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 128add1155SRico Sonntag * GNU General Public License for more details. 138add1155SRico Sonntag * You should have received a copy of the GNU General Public License 148add1155SRico Sonntag * along with this program. If not, see <http://www.gnu.org/licenses/>. 158add1155SRico Sonntag */ 168add1155SRico Sonntagdeclare(strict_types=1); 178add1155SRico Sonntag 188add1155SRico Sonntagnamespace Fisharebest\Webtrees\Statistics\Repository; 198add1155SRico Sonntag 208add1155SRico Sonntaguse Fisharebest\Webtrees\Database; 218add1155SRico Sonntaguse Fisharebest\Webtrees\Family; 228add1155SRico Sonntaguse Fisharebest\Webtrees\Functions\FunctionsDate; 238add1155SRico Sonntaguse Fisharebest\Webtrees\I18N; 248add1155SRico Sonntaguse Fisharebest\Webtrees\Individual; 258add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartChildren; 268add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartDivorce; 278add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartFamilyLargest; 288add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartMarriage; 298add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartMarriageAge; 308add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartNoChildrenFamilies; 318add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Helper\Sql; 328add1155SRico Sonntaguse Fisharebest\Webtrees\Tree; 3344cdc21eSGreg Roachuse Illuminate\Database\Capsule\Manager as DB; 348add1155SRico Sonntaguse stdClass; 358add1155SRico Sonntag 368add1155SRico Sonntag/** 378add1155SRico Sonntag * 388add1155SRico Sonntag */ 398add1155SRico Sonntagclass FamilyRepository 408add1155SRico Sonntag{ 418add1155SRico Sonntag /** 428add1155SRico Sonntag * @var Tree 438add1155SRico Sonntag */ 448add1155SRico Sonntag private $tree; 458add1155SRico Sonntag 468add1155SRico Sonntag /** 478add1155SRico Sonntag * Constructor. 488add1155SRico Sonntag * 498add1155SRico Sonntag * @param Tree $tree 508add1155SRico Sonntag */ 518add1155SRico Sonntag public function __construct(Tree $tree) 528add1155SRico Sonntag { 538add1155SRico Sonntag $this->tree = $tree; 548add1155SRico Sonntag } 558add1155SRico Sonntag 568add1155SRico Sonntag /** 578add1155SRico Sonntag * General query on family. 588add1155SRico Sonntag * 598add1155SRico Sonntag * @param string $type 608add1155SRico Sonntag * 618add1155SRico Sonntag * @return string 628add1155SRico Sonntag */ 638add1155SRico Sonntag private function familyQuery(string $type): string 648add1155SRico Sonntag { 6544cdc21eSGreg Roach $row = DB::table('families') 6644cdc21eSGreg Roach ->where('f_file', '=', $this->tree->id()) 6744cdc21eSGreg Roach ->orderBy('f_numchil', 'desc') 6844cdc21eSGreg Roach ->first(); 698add1155SRico Sonntag 7044cdc21eSGreg Roach if ($row === null) { 718add1155SRico Sonntag return ''; 728add1155SRico Sonntag } 738add1155SRico Sonntag 7444cdc21eSGreg Roach /** @var Family $family */ 7544cdc21eSGreg Roach $family = Family::rowMapper()($row); 768add1155SRico Sonntag 7744cdc21eSGreg Roach if (!$family->canShow()) { 7844cdc21eSGreg Roach return I18N::translate('This information is private and cannot be shown.'); 798add1155SRico Sonntag } 808add1155SRico Sonntag 818add1155SRico Sonntag switch ($type) { 828add1155SRico Sonntag default: 838add1155SRico Sonntag case 'full': 8444cdc21eSGreg Roach return $family->formatList(); 858add1155SRico Sonntag 8644cdc21eSGreg Roach case 'size': 8744cdc21eSGreg Roach return I18N::number((int) $row->f_numchil); 8844cdc21eSGreg Roach 8944cdc21eSGreg Roach case 'name': 9044cdc21eSGreg Roach return '<a href="' . e($family->url()) . '">' . $family->getFullName() . '</a>'; 9144cdc21eSGreg Roach } 928add1155SRico Sonntag } 938add1155SRico Sonntag 948add1155SRico Sonntag /** 958add1155SRico Sonntag * Run an SQL query and cache the result. 968add1155SRico Sonntag * 978add1155SRico Sonntag * @param string $sql 988add1155SRico Sonntag * 998add1155SRico Sonntag * @return stdClass[] 1008add1155SRico Sonntag */ 1018add1155SRico Sonntag private function runSql($sql): array 1028add1155SRico Sonntag { 1038add1155SRico Sonntag return Sql::runSql($sql); 1048add1155SRico Sonntag } 1058add1155SRico Sonntag 1068add1155SRico Sonntag /** 1078add1155SRico Sonntag * Find the family with the most children. 1088add1155SRico Sonntag * 1098add1155SRico Sonntag * @return string 1108add1155SRico Sonntag */ 1118add1155SRico Sonntag public function largestFamily(): string 1128add1155SRico Sonntag { 1138add1155SRico Sonntag return $this->familyQuery('full'); 1148add1155SRico Sonntag } 1158add1155SRico Sonntag 1168add1155SRico Sonntag /** 1178add1155SRico Sonntag * Find the number of children in the largest family. 1188add1155SRico Sonntag * 1198add1155SRico Sonntag * @return string 1208add1155SRico Sonntag */ 1218add1155SRico Sonntag public function largestFamilySize(): string 1228add1155SRico Sonntag { 1238add1155SRico Sonntag return $this->familyQuery('size'); 1248add1155SRico Sonntag } 1258add1155SRico Sonntag 1268add1155SRico Sonntag /** 1278add1155SRico Sonntag * Find the family with the most children. 1288add1155SRico Sonntag * 1298add1155SRico Sonntag * @return string 1308add1155SRico Sonntag */ 1318add1155SRico Sonntag public function largestFamilyName(): string 1328add1155SRico Sonntag { 1338add1155SRico Sonntag return $this->familyQuery('name'); 1348add1155SRico Sonntag } 1358add1155SRico Sonntag 1368add1155SRico Sonntag /** 1378add1155SRico Sonntag * Find the couple with the most grandchildren. 1388add1155SRico Sonntag * 1398add1155SRico Sonntag * @param int $total 1408add1155SRico Sonntag * 1418add1155SRico Sonntag * @return array 1428add1155SRico Sonntag */ 1438add1155SRico Sonntag private function topTenGrandFamilyQuery(int $total): array 1448add1155SRico Sonntag { 1458add1155SRico Sonntag $rows = $this->runSql( 1468add1155SRico Sonntag "SELECT COUNT(*) AS tot, f_id AS id" . 1478add1155SRico Sonntag " FROM `##families`" . 1488add1155SRico Sonntag " JOIN `##link` AS children ON children.l_file = {$this->tree->id()}" . 1498add1155SRico Sonntag " JOIN `##link` AS mchildren ON mchildren.l_file = {$this->tree->id()}" . 1508add1155SRico Sonntag " JOIN `##link` AS gchildren ON gchildren.l_file = {$this->tree->id()}" . 1518add1155SRico Sonntag " WHERE" . 1528add1155SRico Sonntag " f_file={$this->tree->id()} AND" . 1538add1155SRico Sonntag " children.l_from=f_id AND" . 1548add1155SRico Sonntag " children.l_type='CHIL' AND" . 1558add1155SRico Sonntag " children.l_to=mchildren.l_from AND" . 1568add1155SRico Sonntag " mchildren.l_type='FAMS' AND" . 1578add1155SRico Sonntag " mchildren.l_to=gchildren.l_from AND" . 1588add1155SRico Sonntag " gchildren.l_type='CHIL'" . 1598add1155SRico Sonntag " GROUP BY id" . 1608add1155SRico Sonntag " ORDER BY tot DESC" . 1618add1155SRico Sonntag " LIMIT " . $total 1628add1155SRico Sonntag ); 1638add1155SRico Sonntag 1648add1155SRico Sonntag if (!isset($rows[0])) { 1658add1155SRico Sonntag return []; 1668add1155SRico Sonntag } 1678add1155SRico Sonntag 1688add1155SRico Sonntag $top10 = []; 1698add1155SRico Sonntag 1708add1155SRico Sonntag foreach ($rows as $row) { 1718add1155SRico Sonntag $family = Family::getInstance($row->id, $this->tree); 1728add1155SRico Sonntag 1738add1155SRico Sonntag if ($family && $family->canShow()) { 1748add1155SRico Sonntag $total = (int) $row->tot; 1758add1155SRico Sonntag 1768add1155SRico Sonntag $top10[] = [ 1778add1155SRico Sonntag 'family' => $family, 1788add1155SRico Sonntag 'count' => $total, 1798add1155SRico Sonntag ]; 1808add1155SRico Sonntag } 1818add1155SRico Sonntag } 1828add1155SRico Sonntag 1838add1155SRico Sonntag // TODO 1848add1155SRico Sonntag // if (I18N::direction() === 'rtl') { 1858add1155SRico Sonntag // $top10 = str_replace([ 1868add1155SRico Sonntag // '[', 1878add1155SRico Sonntag // ']', 1888add1155SRico Sonntag // '(', 1898add1155SRico Sonntag // ')', 1908add1155SRico Sonntag // '+', 1918add1155SRico Sonntag // ], [ 1928add1155SRico Sonntag // '‏[', 1938add1155SRico Sonntag // '‏]', 1948add1155SRico Sonntag // '‏(', 1958add1155SRico Sonntag // '‏)', 1968add1155SRico Sonntag // '‏+', 1978add1155SRico Sonntag // ], $top10); 1988add1155SRico Sonntag // } 1998add1155SRico Sonntag 2008add1155SRico Sonntag return $top10; 2018add1155SRico Sonntag } 2028add1155SRico Sonntag 2038add1155SRico Sonntag /** 2048add1155SRico Sonntag * Find the couple with the most grandchildren. 2058add1155SRico Sonntag * 2068add1155SRico Sonntag * @param int $total 2078add1155SRico Sonntag * 2088add1155SRico Sonntag * @return string 2098add1155SRico Sonntag */ 2108add1155SRico Sonntag public function topTenLargestGrandFamily(int $total = 10): string 2118add1155SRico Sonntag { 2128add1155SRico Sonntag $records = $this->topTenGrandFamilyQuery($total); 2138add1155SRico Sonntag 2148add1155SRico Sonntag return view( 2158add1155SRico Sonntag 'statistics/families/top10-nolist-grand', 2168add1155SRico Sonntag [ 2178add1155SRico Sonntag 'records' => $records, 2188add1155SRico Sonntag ] 2198add1155SRico Sonntag ); 2208add1155SRico Sonntag } 2218add1155SRico Sonntag 2228add1155SRico Sonntag /** 2238add1155SRico Sonntag * Find the couple with the most grandchildren. 2248add1155SRico Sonntag * 2258add1155SRico Sonntag * @param int $total 2268add1155SRico Sonntag * 2278add1155SRico Sonntag * @return string 2288add1155SRico Sonntag */ 2298add1155SRico Sonntag public function topTenLargestGrandFamilyList(int $total = 10): string 2308add1155SRico Sonntag { 2318add1155SRico Sonntag $records = $this->topTenGrandFamilyQuery($total); 2328add1155SRico Sonntag 2338add1155SRico Sonntag return view( 2348add1155SRico Sonntag 'statistics/families/top10-list-grand', 2358add1155SRico Sonntag [ 2368add1155SRico Sonntag 'records' => $records, 2378add1155SRico Sonntag ] 2388add1155SRico Sonntag ); 2398add1155SRico Sonntag } 2408add1155SRico Sonntag 2418add1155SRico Sonntag /** 2428add1155SRico Sonntag * Find the families with no children. 2438add1155SRico Sonntag * 2448add1155SRico Sonntag * @return int 2458add1155SRico Sonntag */ 2468add1155SRico Sonntag private function noChildrenFamiliesQuery(): int 2478add1155SRico Sonntag { 2488add1155SRico Sonntag $rows = $this->runSql( 2498add1155SRico Sonntag " SELECT COUNT(*) AS tot" . 2508add1155SRico Sonntag " FROM `##families`" . 2518add1155SRico Sonntag " WHERE f_numchil = 0 AND f_file = {$this->tree->id()}" 2528add1155SRico Sonntag ); 2538add1155SRico Sonntag 2548add1155SRico Sonntag return (int) $rows[0]->tot; 2558add1155SRico Sonntag } 2568add1155SRico Sonntag 2578add1155SRico Sonntag /** 2588add1155SRico Sonntag * Find the families with no children. 2598add1155SRico Sonntag * 2608add1155SRico Sonntag * @return string 2618add1155SRico Sonntag */ 2628add1155SRico Sonntag public function noChildrenFamilies(): string 2638add1155SRico Sonntag { 2648add1155SRico Sonntag return I18N::number($this->noChildrenFamiliesQuery()); 2658add1155SRico Sonntag } 2668add1155SRico Sonntag 2678add1155SRico Sonntag /** 2688add1155SRico Sonntag * Find the families with no children. 2698add1155SRico Sonntag * 2708add1155SRico Sonntag * @param string $type 2718add1155SRico Sonntag * 2728add1155SRico Sonntag * @return string 2738add1155SRico Sonntag */ 2748add1155SRico Sonntag public function noChildrenFamiliesList($type = 'list'): string 2758add1155SRico Sonntag { 2768add1155SRico Sonntag $rows = $this->runSql( 2778add1155SRico Sonntag " SELECT f_id AS family" . 2788add1155SRico Sonntag " FROM `##families` AS fam" . 2798add1155SRico Sonntag " WHERE f_numchil = 0 AND fam.f_file = {$this->tree->id()}" 2808add1155SRico Sonntag ); 2818add1155SRico Sonntag 2828add1155SRico Sonntag if (!isset($rows[0])) { 2838add1155SRico Sonntag return ''; 2848add1155SRico Sonntag } 2858add1155SRico Sonntag 2868add1155SRico Sonntag $top10 = []; 2878add1155SRico Sonntag foreach ($rows as $row) { 2888add1155SRico Sonntag $family = Family::getInstance($row->family, $this->tree); 2898add1155SRico Sonntag if ($family->canShow()) { 2908add1155SRico Sonntag if ($type === 'list') { 2918add1155SRico Sonntag $top10[] = '<li><a href="' . e($family->url()) . '">' . $family->getFullName() . '</a></li>'; 2928add1155SRico Sonntag } else { 2938add1155SRico Sonntag $top10[] = '<a href="' . e($family->url()) . '">' . $family->getFullName() . '</a>'; 2948add1155SRico Sonntag } 2958add1155SRico Sonntag } 2968add1155SRico Sonntag } 2978add1155SRico Sonntag 2988add1155SRico Sonntag if ($type === 'list') { 2998add1155SRico Sonntag $top10 = implode('', $top10); 3008add1155SRico Sonntag } else { 3018add1155SRico Sonntag $top10 = implode('; ', $top10); 3028add1155SRico Sonntag } 3038add1155SRico Sonntag 3048add1155SRico Sonntag if (I18N::direction() === 'rtl') { 3058add1155SRico Sonntag $top10 = str_replace([ 3068add1155SRico Sonntag '[', 3078add1155SRico Sonntag ']', 3088add1155SRico Sonntag '(', 3098add1155SRico Sonntag ')', 3108add1155SRico Sonntag '+', 3118add1155SRico Sonntag ], [ 3128add1155SRico Sonntag '‏[', 3138add1155SRico Sonntag '‏]', 3148add1155SRico Sonntag '‏(', 3158add1155SRico Sonntag '‏)', 3168add1155SRico Sonntag '‏+', 3178add1155SRico Sonntag ], $top10); 3188add1155SRico Sonntag } 3198add1155SRico Sonntag if ($type === 'list') { 3208add1155SRico Sonntag return '<ul>' . $top10 . '</ul>'; 3218add1155SRico Sonntag } 3228add1155SRico Sonntag 3238add1155SRico Sonntag return $top10; 3248add1155SRico Sonntag } 3258add1155SRico Sonntag 3268add1155SRico Sonntag /** 3278add1155SRico Sonntag * Create a chart of children with no families. 3288add1155SRico Sonntag * 3298add1155SRico Sonntag * @param int $year1 3308add1155SRico Sonntag * @param int $year2 3318add1155SRico Sonntag * 3328add1155SRico Sonntag * @return string 3338add1155SRico Sonntag */ 33488de55fdSRico Sonntag public function chartNoChildrenFamilies(int $year1 = -1, int $year2 = -1): string 3358add1155SRico Sonntag { 3368add1155SRico Sonntag $no_child_fam = $this->noChildrenFamiliesQuery(); 3378add1155SRico Sonntag 3388add1155SRico Sonntag return (new ChartNoChildrenFamilies($this->tree)) 33988de55fdSRico Sonntag ->chartNoChildrenFamilies($no_child_fam, $year1, $year2); 3408add1155SRico Sonntag } 3418add1155SRico Sonntag 3428add1155SRico Sonntag /** 3438add1155SRico Sonntag * Returns the ages between siblings. 3448add1155SRico Sonntag * 3458add1155SRico Sonntag * @param int $total The total number of records to query 3468add1155SRico Sonntag * 3478add1155SRico Sonntag * @return array 3488add1155SRico Sonntag */ 3498add1155SRico Sonntag private function ageBetweenSiblingsQuery(int $total): array 3508add1155SRico Sonntag { 3518add1155SRico Sonntag $rows = $this->runSql( 3528add1155SRico Sonntag " SELECT DISTINCT" . 3538add1155SRico Sonntag " link1.l_from AS family," . 3548add1155SRico Sonntag " link1.l_to AS ch1," . 3558add1155SRico Sonntag " link2.l_to AS ch2," . 3568add1155SRico Sonntag " child1.d_julianday2-child2.d_julianday2 AS age" . 3578add1155SRico Sonntag " FROM `##link` AS link1" . 3588add1155SRico Sonntag " LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->id()}" . 3598add1155SRico Sonntag " LEFT JOIN `##dates` AS child2 ON child2.d_file = {$this->tree->id()}" . 3608add1155SRico Sonntag " LEFT JOIN `##link` AS link2 ON link2.l_file = {$this->tree->id()}" . 3618add1155SRico Sonntag " WHERE" . 3628add1155SRico Sonntag " link1.l_file = {$this->tree->id()} AND" . 3638add1155SRico Sonntag " link1.l_from = link2.l_from AND" . 3648add1155SRico Sonntag " link1.l_type = 'CHIL' AND" . 3658add1155SRico Sonntag " child1.d_gid = link1.l_to AND" . 3668add1155SRico Sonntag " child1.d_fact = 'BIRT' AND" . 3678add1155SRico Sonntag " link2.l_type = 'CHIL' AND" . 3688add1155SRico Sonntag " child2.d_gid = link2.l_to AND" . 3698add1155SRico Sonntag " child2.d_fact = 'BIRT' AND" . 3708add1155SRico Sonntag " child1.d_julianday2 > child2.d_julianday2 AND" . 3718add1155SRico Sonntag " child2.d_julianday2 <> 0 AND" . 3728add1155SRico Sonntag " child1.d_gid <> child2.d_gid" . 3738add1155SRico Sonntag " ORDER BY age DESC" . 3748add1155SRico Sonntag " LIMIT " . $total 3758add1155SRico Sonntag ); 3768add1155SRico Sonntag 3778add1155SRico Sonntag if (!isset($rows[0])) { 3788add1155SRico Sonntag return []; 3798add1155SRico Sonntag } 3808add1155SRico Sonntag 3818add1155SRico Sonntag return $rows; 3828add1155SRico Sonntag } 3838add1155SRico Sonntag 3848add1155SRico Sonntag /** 3858add1155SRico Sonntag * Returns the calculated age the time of event. 3868add1155SRico Sonntag * 3878add1155SRico Sonntag * @param int $age The age from the database record 3888add1155SRico Sonntag * 3898add1155SRico Sonntag * @return string 3908add1155SRico Sonntag */ 3918add1155SRico Sonntag private function calculateAge(int $age): string 3928add1155SRico Sonntag { 3938add1155SRico Sonntag if ((int) ($age / 365.25) > 0) { 3948add1155SRico Sonntag $result = (int) ($age / 365.25) . 'y'; 3958add1155SRico Sonntag } elseif ((int) ($age / 30.4375) > 0) { 3968add1155SRico Sonntag $result = (int) ($age / 30.4375) . 'm'; 3978add1155SRico Sonntag } else { 3988add1155SRico Sonntag $result = $age . 'd'; 3998add1155SRico Sonntag } 4008add1155SRico Sonntag 4018add1155SRico Sonntag return FunctionsDate::getAgeAtEvent($result); 4028add1155SRico Sonntag } 4038add1155SRico Sonntag 4048add1155SRico Sonntag /** 4058add1155SRico Sonntag * Find the ages between siblings. 4068add1155SRico Sonntag * 4078add1155SRico Sonntag * @param int $total The total number of records to query 4088add1155SRico Sonntag * 4098add1155SRico Sonntag * @return array 4108add1155SRico Sonntag * @throws \Exception 4118add1155SRico Sonntag */ 4128add1155SRico Sonntag private function ageBetweenSiblingsNoList(int $total): array 4138add1155SRico Sonntag { 4148add1155SRico Sonntag $rows = $this->ageBetweenSiblingsQuery($total); 4158add1155SRico Sonntag 4168add1155SRico Sonntag foreach ($rows as $fam) { 4178add1155SRico Sonntag $family = Family::getInstance($fam->family, $this->tree); 4188add1155SRico Sonntag $child1 = Individual::getInstance($fam->ch1, $this->tree); 4198add1155SRico Sonntag $child2 = Individual::getInstance($fam->ch2, $this->tree); 4208add1155SRico Sonntag 4218add1155SRico Sonntag if ($child1->canShow() && $child2->canShow()) { 4228add1155SRico Sonntag // ! Single array (no list) 4238add1155SRico Sonntag return [ 4248add1155SRico Sonntag 'child1' => $child1, 4258add1155SRico Sonntag 'child2' => $child2, 4268add1155SRico Sonntag 'family' => $family, 4278add1155SRico Sonntag 'age' => $this->calculateAge((int) $fam->age), 4288add1155SRico Sonntag ]; 4298add1155SRico Sonntag } 4308add1155SRico Sonntag } 4318add1155SRico Sonntag 4328add1155SRico Sonntag return []; 4338add1155SRico Sonntag } 4348add1155SRico Sonntag 4358add1155SRico Sonntag /** 4368add1155SRico Sonntag * Find the ages between siblings. 4378add1155SRico Sonntag * 4388add1155SRico Sonntag * @param int $total The total number of records to query 4398add1155SRico Sonntag * @param bool $one Include each family only once if true 4408add1155SRico Sonntag * 4418add1155SRico Sonntag * @return array 4428add1155SRico Sonntag * @throws \Exception 4438add1155SRico Sonntag */ 4448add1155SRico Sonntag private function ageBetweenSiblingsList(int $total, bool $one): array 4458add1155SRico Sonntag { 4468add1155SRico Sonntag $rows = $this->ageBetweenSiblingsQuery($total); 4478add1155SRico Sonntag $top10 = []; 4488add1155SRico Sonntag $dist = []; 4498add1155SRico Sonntag 4508add1155SRico Sonntag foreach ($rows as $fam) { 4518add1155SRico Sonntag $family = Family::getInstance($fam->family, $this->tree); 4528add1155SRico Sonntag $child1 = Individual::getInstance($fam->ch1, $this->tree); 4538add1155SRico Sonntag $child2 = Individual::getInstance($fam->ch2, $this->tree); 4548add1155SRico Sonntag 4558add1155SRico Sonntag $age = $this->calculateAge((int) $fam->age); 4568add1155SRico Sonntag 4578add1155SRico Sonntag if ($one && !\in_array($fam->family, $dist, true)) { 4588add1155SRico Sonntag if ($child1->canShow() && $child2->canShow()) { 4598add1155SRico Sonntag $top10[] = [ 4608add1155SRico Sonntag 'child1' => $child1, 4618add1155SRico Sonntag 'child2' => $child2, 4628add1155SRico Sonntag 'family' => $family, 4638add1155SRico Sonntag 'age' => $age, 4648add1155SRico Sonntag ]; 4658add1155SRico Sonntag 4668add1155SRico Sonntag $dist[] = $fam->family; 4678add1155SRico Sonntag } 4688add1155SRico Sonntag } elseif (!$one && $child1->canShow() && $child2->canShow()) { 4698add1155SRico Sonntag $top10[] = [ 4708add1155SRico Sonntag 'child1' => $child1, 4718add1155SRico Sonntag 'child2' => $child2, 4728add1155SRico Sonntag 'family' => $family, 4738add1155SRico Sonntag 'age' => $age, 4748add1155SRico Sonntag ]; 4758add1155SRico Sonntag } 4768add1155SRico Sonntag } 4778add1155SRico Sonntag 4788add1155SRico Sonntag // TODO 4798add1155SRico Sonntag // if (I18N::direction() === 'rtl') { 4808add1155SRico Sonntag // $top10 = str_replace([ 4818add1155SRico Sonntag // '[', 4828add1155SRico Sonntag // ']', 4838add1155SRico Sonntag // '(', 4848add1155SRico Sonntag // ')', 4858add1155SRico Sonntag // '+', 4868add1155SRico Sonntag // ], [ 4878add1155SRico Sonntag // '‏[', 4888add1155SRico Sonntag // '‏]', 4898add1155SRico Sonntag // '‏(', 4908add1155SRico Sonntag // '‏)', 4918add1155SRico Sonntag // '‏+', 4928add1155SRico Sonntag // ], $top10); 4938add1155SRico Sonntag // } 4948add1155SRico Sonntag 4958add1155SRico Sonntag return $top10; 4968add1155SRico Sonntag } 4978add1155SRico Sonntag 4988add1155SRico Sonntag /** 4998add1155SRico Sonntag * Find the ages between siblings. 5008add1155SRico Sonntag * 5018add1155SRico Sonntag * @param int $total The total number of records to query 5028add1155SRico Sonntag * 5038add1155SRico Sonntag * @return string 5048add1155SRico Sonntag */ 5058add1155SRico Sonntag private function ageBetweenSiblingsAge(int $total): string 5068add1155SRico Sonntag { 5078add1155SRico Sonntag $rows = $this->ageBetweenSiblingsQuery($total); 5088add1155SRico Sonntag 5098add1155SRico Sonntag foreach ($rows as $fam) { 5108add1155SRico Sonntag return $this->calculateAge((int) $fam->age); 5118add1155SRico Sonntag } 5128add1155SRico Sonntag 5138add1155SRico Sonntag return ''; 5148add1155SRico Sonntag } 5158add1155SRico Sonntag 5168add1155SRico Sonntag /** 5178add1155SRico Sonntag * Find the ages between siblings. 5188add1155SRico Sonntag * 5198add1155SRico Sonntag * @param int $total The total number of records to query 5208add1155SRico Sonntag * 5218add1155SRico Sonntag * @return string 5228add1155SRico Sonntag * @throws \Exception 5238add1155SRico Sonntag */ 5248add1155SRico Sonntag private function ageBetweenSiblingsName(int $total): string 5258add1155SRico Sonntag { 5268add1155SRico Sonntag $rows = $this->ageBetweenSiblingsQuery($total); 5278add1155SRico Sonntag 5288add1155SRico Sonntag foreach ($rows as $fam) { 5298add1155SRico Sonntag $family = Family::getInstance($fam->family, $this->tree); 5308add1155SRico Sonntag $child1 = Individual::getInstance($fam->ch1, $this->tree); 5318add1155SRico Sonntag $child2 = Individual::getInstance($fam->ch2, $this->tree); 5328add1155SRico Sonntag 5338add1155SRico Sonntag if ($child1->canShow() && $child2->canShow()) { 5348add1155SRico Sonntag $return = '<a href="' . e($child2->url()) . '">' . $child2->getFullName() . '</a> '; 5358add1155SRico Sonntag $return .= I18N::translate('and') . ' '; 5368add1155SRico Sonntag $return .= '<a href="' . e($child1->url()) . '">' . $child1->getFullName() . '</a>'; 5378add1155SRico Sonntag $return .= ' <a href="' . e($family->url()) . '">[' . I18N::translate('View this family') . ']</a>'; 5388add1155SRico Sonntag } else { 5398add1155SRico Sonntag $return = I18N::translate('This information is private and cannot be shown.'); 5408add1155SRico Sonntag } 5418add1155SRico Sonntag 5428add1155SRico Sonntag return $return; 5438add1155SRico Sonntag } 5448add1155SRico Sonntag 5458add1155SRico Sonntag return ''; 5468add1155SRico Sonntag } 5478add1155SRico Sonntag 5488add1155SRico Sonntag /** 5498add1155SRico Sonntag * Find the names of siblings with the widest age gap. 5508add1155SRico Sonntag * 5518add1155SRico Sonntag * @param int $total 5528add1155SRico Sonntag * 5538add1155SRico Sonntag * @return string 5548add1155SRico Sonntag */ 5558add1155SRico Sonntag public function topAgeBetweenSiblingsName(int $total = 10): string 5568add1155SRico Sonntag { 5578add1155SRico Sonntag return $this->ageBetweenSiblingsName($total); 5588add1155SRico Sonntag } 5598add1155SRico Sonntag 5608add1155SRico Sonntag /** 5618add1155SRico Sonntag * Find the widest age gap between siblings. 5628add1155SRico Sonntag * 5638add1155SRico Sonntag * @param int $total 5648add1155SRico Sonntag * 5658add1155SRico Sonntag * @return string 5668add1155SRico Sonntag */ 5678add1155SRico Sonntag public function topAgeBetweenSiblings(int $total = 10): string 5688add1155SRico Sonntag { 5698add1155SRico Sonntag return $this->ageBetweenSiblingsAge($total); 5708add1155SRico Sonntag } 5718add1155SRico Sonntag 5728add1155SRico Sonntag /** 5738add1155SRico Sonntag * Find the name of siblings with the widest age gap. 5748add1155SRico Sonntag * 5758add1155SRico Sonntag * @param int $total 5768add1155SRico Sonntag * 5778add1155SRico Sonntag * @return string 5788add1155SRico Sonntag */ 5798add1155SRico Sonntag public function topAgeBetweenSiblingsFullName(int $total = 10): string 5808add1155SRico Sonntag { 5818add1155SRico Sonntag $record = $this->ageBetweenSiblingsNoList($total); 5828add1155SRico Sonntag 583cb2263dcSGreg Roach if (empty($record)) { 584dd7dd2a1SRico Sonntag return I18N::translate('This information is not available.'); 585cb2263dcSGreg Roach } 586cb2263dcSGreg Roach 587cb2263dcSGreg Roach return view('statistics/families/top10-nolist-age', [ 5888add1155SRico Sonntag 'record' => $record, 589cb2263dcSGreg Roach ]); 5908add1155SRico Sonntag } 5918add1155SRico Sonntag 5928add1155SRico Sonntag /** 5938add1155SRico Sonntag * Find the siblings with the widest age gaps. 5948add1155SRico Sonntag * 5958add1155SRico Sonntag * @param int $total 5968add1155SRico Sonntag * @param string $one 5978add1155SRico Sonntag * 5988add1155SRico Sonntag * @return string 5998add1155SRico Sonntag */ 6008add1155SRico Sonntag public function topAgeBetweenSiblingsList(int $total = 10, string $one = ''): string 6018add1155SRico Sonntag { 6028add1155SRico Sonntag $records = $this->ageBetweenSiblingsList($total, (bool) $one); 6038add1155SRico Sonntag 604cb2263dcSGreg Roach return view('statistics/families/top10-list-age', [ 6058add1155SRico Sonntag 'records' => $records, 606cb2263dcSGreg Roach ]); 6078add1155SRico Sonntag } 6088add1155SRico Sonntag 6098add1155SRico Sonntag /** 6108add1155SRico Sonntag * General query on familes/children. 6118add1155SRico Sonntag * 6128add1155SRico Sonntag * @param string $sex 6138add1155SRico Sonntag * @param int $year1 6148add1155SRico Sonntag * @param int $year2 6158add1155SRico Sonntag * 6168add1155SRico Sonntag * @return stdClass[] 6178add1155SRico Sonntag */ 6188add1155SRico Sonntag public function statsChildrenQuery(string $sex = 'BOTH', int $year1 = -1, int $year2 = -1): array 6198add1155SRico Sonntag { 6208add1155SRico Sonntag if ($sex === 'M') { 6218add1155SRico Sonntag $sql = 6228add1155SRico Sonntag "SELECT num, COUNT(*) AS total FROM " . 6238add1155SRico Sonntag "(SELECT count(i_sex) AS num FROM `##link` " . 6248add1155SRico Sonntag "LEFT OUTER JOIN `##individuals` " . 6258add1155SRico Sonntag "ON l_from=i_id AND l_file=i_file AND i_sex='M' AND l_type='FAMC' " . 6268add1155SRico Sonntag "JOIN `##families` ON f_file=l_file AND f_id=l_to WHERE f_file={$this->tree->id()} GROUP BY l_to" . 6278add1155SRico Sonntag ") boys" . 6288add1155SRico Sonntag " GROUP BY num" . 6298add1155SRico Sonntag " ORDER BY num"; 6308add1155SRico Sonntag } elseif ($sex === 'F') { 6318add1155SRico Sonntag $sql = 6328add1155SRico Sonntag "SELECT num, COUNT(*) AS total FROM " . 6338add1155SRico Sonntag "(SELECT count(i_sex) AS num FROM `##link` " . 6348add1155SRico Sonntag "LEFT OUTER JOIN `##individuals` " . 6358add1155SRico Sonntag "ON l_from=i_id AND l_file=i_file AND i_sex='F' AND l_type='FAMC' " . 6368add1155SRico Sonntag "JOIN `##families` ON f_file=l_file AND f_id=l_to WHERE f_file={$this->tree->id()} GROUP BY l_to" . 6378add1155SRico Sonntag ") girls" . 6388add1155SRico Sonntag " GROUP BY num" . 6398add1155SRico Sonntag " ORDER BY num"; 6408add1155SRico Sonntag } else { 6418add1155SRico Sonntag $sql = "SELECT f_numchil, COUNT(*) AS total FROM `##families` "; 6428add1155SRico Sonntag 6438add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 6448add1155SRico Sonntag $sql .= 6458add1155SRico Sonntag "AS fam LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" 6468add1155SRico Sonntag . " WHERE" 6478add1155SRico Sonntag . " married.d_gid = fam.f_id AND" 6488add1155SRico Sonntag . " fam.f_file = {$this->tree->id()} AND" 6498add1155SRico Sonntag . " married.d_fact = 'MARR' AND" 6508add1155SRico Sonntag . " married.d_year BETWEEN '{$year1}' AND '{$year2}'"; 6518add1155SRico Sonntag } else { 6528add1155SRico Sonntag $sql .= "WHERE f_file={$this->tree->id()}"; 6538add1155SRico Sonntag } 6548add1155SRico Sonntag 6558add1155SRico Sonntag $sql .= ' GROUP BY f_numchil'; 6568add1155SRico Sonntag } 6578add1155SRico Sonntag 6588add1155SRico Sonntag return $this->runSql($sql); 6598add1155SRico Sonntag } 6608add1155SRico Sonntag 6618add1155SRico Sonntag /** 6628add1155SRico Sonntag * Genearl query on families/children. 6638add1155SRico Sonntag * 6648add1155SRico Sonntag * @return string 6658add1155SRico Sonntag */ 66688de55fdSRico Sonntag public function statsChildren(): string 6678add1155SRico Sonntag { 6688add1155SRico Sonntag return (new ChartChildren($this->tree)) 66988de55fdSRico Sonntag ->chartChildren(); 6708add1155SRico Sonntag } 6718add1155SRico Sonntag 6728add1155SRico Sonntag /** 6738add1155SRico Sonntag * Count the total children. 6748add1155SRico Sonntag * 6758add1155SRico Sonntag * @return string 6768add1155SRico Sonntag */ 6778add1155SRico Sonntag public function totalChildren(): string 6788add1155SRico Sonntag { 6798add1155SRico Sonntag $total = (int) Database::prepare( 6808add1155SRico Sonntag "SELECT SUM(f_numchil) FROM `##families` WHERE f_file = :tree_id" 6818add1155SRico Sonntag )->execute([ 6828add1155SRico Sonntag 'tree_id' => $this->tree->id(), 6838add1155SRico Sonntag ])->fetchOne(); 6848add1155SRico Sonntag 6858add1155SRico Sonntag return I18N::number($total); 6868add1155SRico Sonntag } 6878add1155SRico Sonntag 6888add1155SRico Sonntag /** 6898add1155SRico Sonntag * Find the average number of children in families. 6908add1155SRico Sonntag * 6918add1155SRico Sonntag * @return string 6928add1155SRico Sonntag */ 6938add1155SRico Sonntag public function averageChildren(): string 6948add1155SRico Sonntag { 6958add1155SRico Sonntag $average = (float) Database::prepare( 6968add1155SRico Sonntag "SELECT AVG(f_numchil) AS tot FROM `##families` WHERE f_file = :tree_id" 6978add1155SRico Sonntag )->execute([ 6988add1155SRico Sonntag 'tree_id' => $this->tree->id(), 6998add1155SRico Sonntag ])->fetchOne(); 7008add1155SRico Sonntag 7018add1155SRico Sonntag return I18N::number($average, 2); 7028add1155SRico Sonntag } 7038add1155SRico Sonntag 7048add1155SRico Sonntag /** 7058add1155SRico Sonntag * General query on families. 7068add1155SRico Sonntag * 7078add1155SRico Sonntag * @param int $total 7088add1155SRico Sonntag * 7098add1155SRico Sonntag * @return array 7108add1155SRico Sonntag */ 7118add1155SRico Sonntag private function topTenFamilyQuery(int $total): array 7128add1155SRico Sonntag { 7138add1155SRico Sonntag $rows = $this->runSql( 7148add1155SRico Sonntag "SELECT f_numchil AS tot, f_id AS id" . 7158add1155SRico Sonntag " FROM `##families`" . 7168add1155SRico Sonntag " WHERE" . 7178add1155SRico Sonntag " f_file={$this->tree->id()}" . 7188add1155SRico Sonntag " ORDER BY tot DESC" . 7198add1155SRico Sonntag " LIMIT " . $total 7208add1155SRico Sonntag ); 7218add1155SRico Sonntag 7228add1155SRico Sonntag if (empty($rows)) { 7238add1155SRico Sonntag return []; 7248add1155SRico Sonntag } 7258add1155SRico Sonntag 7268add1155SRico Sonntag $top10 = []; 7278add1155SRico Sonntag foreach ($rows as $row) { 7288add1155SRico Sonntag $family = Family::getInstance($row->id, $this->tree); 7298add1155SRico Sonntag 7308add1155SRico Sonntag if ($family && $family->canShow()) { 7318add1155SRico Sonntag $top10[] = [ 7328add1155SRico Sonntag 'family' => $family, 7338add1155SRico Sonntag 'count' => (int) $row->tot, 7348add1155SRico Sonntag ]; 7358add1155SRico Sonntag } 7368add1155SRico Sonntag } 7378add1155SRico Sonntag 7388add1155SRico Sonntag // TODO 7398add1155SRico Sonntag // if (I18N::direction() === 'rtl') { 7408add1155SRico Sonntag // $top10 = str_replace([ 7418add1155SRico Sonntag // '[', 7428add1155SRico Sonntag // ']', 7438add1155SRico Sonntag // '(', 7448add1155SRico Sonntag // ')', 7458add1155SRico Sonntag // '+', 7468add1155SRico Sonntag // ], [ 7478add1155SRico Sonntag // '‏[', 7488add1155SRico Sonntag // '‏]', 7498add1155SRico Sonntag // '‏(', 7508add1155SRico Sonntag // '‏)', 7518add1155SRico Sonntag // '‏+', 7528add1155SRico Sonntag // ], $top10); 7538add1155SRico Sonntag // } 7548add1155SRico Sonntag 7558add1155SRico Sonntag return $top10; 7568add1155SRico Sonntag } 7578add1155SRico Sonntag 7588add1155SRico Sonntag /** 7598add1155SRico Sonntag * The the families with the most children. 7608add1155SRico Sonntag * 7618add1155SRico Sonntag * @param int $total 7628add1155SRico Sonntag * 7638add1155SRico Sonntag * @return string 7648add1155SRico Sonntag */ 7658add1155SRico Sonntag public function topTenLargestFamily(int $total = 10): string 7668add1155SRico Sonntag { 7678add1155SRico Sonntag $records = $this->topTenFamilyQuery($total); 7688add1155SRico Sonntag 7698add1155SRico Sonntag return view( 7708add1155SRico Sonntag 'statistics/families/top10-nolist', 7718add1155SRico Sonntag [ 7728add1155SRico Sonntag 'records' => $records, 7738add1155SRico Sonntag ] 7748add1155SRico Sonntag ); 7758add1155SRico Sonntag } 7768add1155SRico Sonntag 7778add1155SRico Sonntag /** 7788add1155SRico Sonntag * Find the families with the most children. 7798add1155SRico Sonntag * 7808add1155SRico Sonntag * @param int $total 7818add1155SRico Sonntag * 7828add1155SRico Sonntag * @return string 7838add1155SRico Sonntag */ 7848add1155SRico Sonntag public function topTenLargestFamilyList(int $total = 10): string 7858add1155SRico Sonntag { 7868add1155SRico Sonntag $records = $this->topTenFamilyQuery($total); 7878add1155SRico Sonntag 7888add1155SRico Sonntag return view( 7898add1155SRico Sonntag 'statistics/families/top10-list', 7908add1155SRico Sonntag [ 7918add1155SRico Sonntag 'records' => $records, 7928add1155SRico Sonntag ] 7938add1155SRico Sonntag ); 7948add1155SRico Sonntag } 7958add1155SRico Sonntag 7968add1155SRico Sonntag /** 7978add1155SRico Sonntag * Create a chart of the largest families. 7988add1155SRico Sonntag * 7998add1155SRico Sonntag * @param string|null $color_from 8008add1155SRico Sonntag * @param string|null $color_to 8018add1155SRico Sonntag * @param int $total 8028add1155SRico Sonntag * 8038add1155SRico Sonntag * @return string 8048add1155SRico Sonntag */ 8058add1155SRico Sonntag public function chartLargestFamilies( 8068add1155SRico Sonntag string $color_from = null, 8078add1155SRico Sonntag string $color_to = null, 8088add1155SRico Sonntag int $total = 10 809e2cbf57aSGreg Roach ): string { 8108add1155SRico Sonntag return (new ChartFamilyLargest($this->tree)) 81188de55fdSRico Sonntag ->chartLargestFamilies($color_from, $color_to, $total); 8128add1155SRico Sonntag } 8138add1155SRico Sonntag 8148add1155SRico Sonntag /** 8158add1155SRico Sonntag * Find the month in the year of the birth of the first child. 8168add1155SRico Sonntag * 8178add1155SRico Sonntag * @param bool $sex 8188add1155SRico Sonntag * 8198add1155SRico Sonntag * @return stdClass[] 8208add1155SRico Sonntag */ 8218add1155SRico Sonntag public function monthFirstChildQuery(bool $sex = false): array 8228add1155SRico Sonntag { 8238add1155SRico Sonntag if ($sex) { 8248add1155SRico Sonntag $sql_sex1 = ', i_sex'; 8258add1155SRico Sonntag $sql_sex2 = " JOIN `##individuals` AS child ON child1.d_file = i_file AND child1.d_gid = child.i_id "; 8268add1155SRico Sonntag } else { 8278add1155SRico Sonntag $sql_sex1 = ''; 8288add1155SRico Sonntag $sql_sex2 = ''; 8298add1155SRico Sonntag } 8308add1155SRico Sonntag 8318add1155SRico Sonntag $sql = 8328add1155SRico Sonntag "SELECT d_month{$sql_sex1}, COUNT(*) AS total " . 8338add1155SRico Sonntag "FROM (" . 8348add1155SRico Sonntag " SELECT family{$sql_sex1}, MIN(date) AS d_date, d_month" . 8358add1155SRico Sonntag " FROM (" . 8368add1155SRico Sonntag " SELECT" . 8378add1155SRico Sonntag " link1.l_from AS family," . 8388add1155SRico Sonntag " link1.l_to AS child," . 8398add1155SRico Sonntag " child1.d_julianday2 AS date," . 8408add1155SRico Sonntag " child1.d_month as d_month" . 8418add1155SRico Sonntag $sql_sex1 . 8428add1155SRico Sonntag " FROM `##link` AS link1" . 8438add1155SRico Sonntag " LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->id()}" . 8448add1155SRico Sonntag $sql_sex2 . 8458add1155SRico Sonntag " WHERE" . 8468add1155SRico Sonntag " link1.l_file = {$this->tree->id()} AND" . 8478add1155SRico Sonntag " link1.l_type = 'CHIL' AND" . 8488add1155SRico Sonntag " child1.d_gid = link1.l_to AND" . 8498add1155SRico Sonntag " child1.d_fact = 'BIRT' AND" . 8508add1155SRico Sonntag " child1.d_month IN ('JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC')" . 8518add1155SRico Sonntag " ORDER BY date" . 8528add1155SRico Sonntag " ) AS children" . 8538add1155SRico Sonntag " GROUP BY family, d_month{$sql_sex1}" . 8548add1155SRico Sonntag ") AS first_child " . 8558add1155SRico Sonntag "GROUP BY d_month"; 8568add1155SRico Sonntag 8578add1155SRico Sonntag if ($sex) { 8588add1155SRico Sonntag $sql .= ', i_sex'; 8598add1155SRico Sonntag } 8608add1155SRico Sonntag 8618add1155SRico Sonntag return $this->runSql($sql); 8628add1155SRico Sonntag } 8638add1155SRico Sonntag 8648add1155SRico Sonntag /** 8658add1155SRico Sonntag * Number of husbands. 8668add1155SRico Sonntag * 8678add1155SRico Sonntag * @return string 8688add1155SRico Sonntag */ 8698add1155SRico Sonntag public function totalMarriedMales(): string 8708add1155SRico Sonntag { 8718add1155SRico Sonntag $n = (int) Database::prepare( 8728add1155SRico Sonntag "SELECT COUNT(DISTINCT f_husb) FROM `##families` WHERE f_file = :tree_id AND f_gedcom LIKE '%\\n1 MARR%'" 8738add1155SRico Sonntag )->execute([ 8748add1155SRico Sonntag 'tree_id' => $this->tree->id(), 8758add1155SRico Sonntag ])->fetchOne(); 8768add1155SRico Sonntag 8778add1155SRico Sonntag return I18N::number($n); 8788add1155SRico Sonntag } 8798add1155SRico Sonntag 8808add1155SRico Sonntag /** 8818add1155SRico Sonntag * Number of wives. 8828add1155SRico Sonntag * 8838add1155SRico Sonntag * @return string 8848add1155SRico Sonntag */ 8858add1155SRico Sonntag public function totalMarriedFemales(): string 8868add1155SRico Sonntag { 8878add1155SRico Sonntag $n = (int) Database::prepare( 8888add1155SRico Sonntag "SELECT COUNT(DISTINCT f_wife) FROM `##families` WHERE f_file = :tree_id AND f_gedcom LIKE '%\\n1 MARR%'" 8898add1155SRico Sonntag )->execute([ 8908add1155SRico Sonntag 'tree_id' => $this->tree->id(), 8918add1155SRico Sonntag ])->fetchOne(); 8928add1155SRico Sonntag 8938add1155SRico Sonntag return I18N::number($n); 8948add1155SRico Sonntag } 8958add1155SRico Sonntag 8968add1155SRico Sonntag /** 8978add1155SRico Sonntag * General query on parents. 8988add1155SRico Sonntag * 8998add1155SRico Sonntag * @param string $type 9008add1155SRico Sonntag * @param string $age_dir 9018add1155SRico Sonntag * @param string $sex 9028add1155SRico Sonntag * @param bool $show_years 9038add1155SRico Sonntag * 9048add1155SRico Sonntag * @return string 9058add1155SRico Sonntag */ 9068add1155SRico Sonntag private function parentsQuery(string $type, string $age_dir, string $sex, bool $show_years): string 9078add1155SRico Sonntag { 9088add1155SRico Sonntag if ($sex === 'F') { 9098add1155SRico Sonntag $sex_field = 'WIFE'; 9108add1155SRico Sonntag } else { 9118add1155SRico Sonntag $sex_field = 'HUSB'; 9128add1155SRico Sonntag } 9138add1155SRico Sonntag 9148add1155SRico Sonntag if ($age_dir !== 'ASC') { 9158add1155SRico Sonntag $age_dir = 'DESC'; 9168add1155SRico Sonntag } 9178add1155SRico Sonntag 9188add1155SRico Sonntag $rows = $this->runSql( 9198add1155SRico Sonntag " SELECT" . 9208add1155SRico Sonntag " parentfamily.l_to AS id," . 9218add1155SRico Sonntag " childbirth.d_julianday2-birth.d_julianday1 AS age" . 9228add1155SRico Sonntag " FROM `##link` AS parentfamily" . 9238add1155SRico Sonntag " JOIN `##link` AS childfamily ON childfamily.l_file = {$this->tree->id()}" . 9248add1155SRico Sonntag " JOIN `##dates` AS birth ON birth.d_file = {$this->tree->id()}" . 9258add1155SRico Sonntag " JOIN `##dates` AS childbirth ON childbirth.d_file = {$this->tree->id()}" . 9268add1155SRico Sonntag " WHERE" . 9278add1155SRico Sonntag " birth.d_gid = parentfamily.l_to AND" . 9288add1155SRico Sonntag " childfamily.l_to = childbirth.d_gid AND" . 9298add1155SRico Sonntag " childfamily.l_type = 'CHIL' AND" . 9308add1155SRico Sonntag " parentfamily.l_type = '{$sex_field}' AND" . 9318add1155SRico Sonntag " childfamily.l_from = parentfamily.l_from AND" . 9328add1155SRico Sonntag " parentfamily.l_file = {$this->tree->id()} AND" . 9338add1155SRico Sonntag " birth.d_fact = 'BIRT' AND" . 9348add1155SRico Sonntag " childbirth.d_fact = 'BIRT' AND" . 9358add1155SRico Sonntag " birth.d_julianday1 <> 0 AND" . 9368add1155SRico Sonntag " childbirth.d_julianday2 > birth.d_julianday1" . 9378add1155SRico Sonntag " ORDER BY age {$age_dir} LIMIT 1" 9388add1155SRico Sonntag ); 9398add1155SRico Sonntag 9408add1155SRico Sonntag if (!isset($rows[0])) { 9418add1155SRico Sonntag return ''; 9428add1155SRico Sonntag } 9438add1155SRico Sonntag 9448add1155SRico Sonntag $row = $rows[0]; 9458add1155SRico Sonntag if (isset($row->id)) { 9468add1155SRico Sonntag $person = Individual::getInstance($row->id, $this->tree); 9478add1155SRico Sonntag } 9488add1155SRico Sonntag 9498add1155SRico Sonntag switch ($type) { 9508add1155SRico Sonntag default: 9518add1155SRico Sonntag case 'full': 9528add1155SRico Sonntag if ($person && $person->canShow()) { 9538add1155SRico Sonntag $result = $person->formatList(); 9548add1155SRico Sonntag } else { 9558add1155SRico Sonntag $result = I18N::translate('This information is private and cannot be shown.'); 9568add1155SRico Sonntag } 9578add1155SRico Sonntag break; 9588add1155SRico Sonntag 9598add1155SRico Sonntag case 'name': 9608add1155SRico Sonntag $result = '<a href="' . e($person->url()) . '">' . $person->getFullName() . '</a>'; 9618add1155SRico Sonntag break; 9628add1155SRico Sonntag 9638add1155SRico Sonntag case 'age': 9648add1155SRico Sonntag $age = $row->age; 9658add1155SRico Sonntag 9668add1155SRico Sonntag if ($show_years) { 9678add1155SRico Sonntag $result = $this->calculateAge((int) $row->age); 9688add1155SRico Sonntag } else { 9698add1155SRico Sonntag $result = (string) floor($age / 365.25); 9708add1155SRico Sonntag } 9718add1155SRico Sonntag 9728add1155SRico Sonntag break; 9738add1155SRico Sonntag } 9748add1155SRico Sonntag 9758add1155SRico Sonntag return $result; 9768add1155SRico Sonntag } 9778add1155SRico Sonntag 9788add1155SRico Sonntag /** 9798add1155SRico Sonntag * Find the youngest mother 9808add1155SRico Sonntag * 9818add1155SRico Sonntag * @return string 9828add1155SRico Sonntag */ 9838add1155SRico Sonntag public function youngestMother(): string 9848add1155SRico Sonntag { 9858add1155SRico Sonntag return $this->parentsQuery('full', 'ASC', 'F', false); 9868add1155SRico Sonntag } 9878add1155SRico Sonntag 9888add1155SRico Sonntag /** 9898add1155SRico Sonntag * Find the name of the youngest mother. 9908add1155SRico Sonntag * 9918add1155SRico Sonntag * @return string 9928add1155SRico Sonntag */ 9938add1155SRico Sonntag public function youngestMotherName(): string 9948add1155SRico Sonntag { 9958add1155SRico Sonntag return $this->parentsQuery('name', 'ASC', 'F', false); 9968add1155SRico Sonntag } 9978add1155SRico Sonntag 9988add1155SRico Sonntag /** 9998add1155SRico Sonntag * Find the age of the youngest mother. 10008add1155SRico Sonntag * 10018add1155SRico Sonntag * @param string $show_years 10028add1155SRico Sonntag * 10038add1155SRico Sonntag * @return string 10048add1155SRico Sonntag */ 10058add1155SRico Sonntag public function youngestMotherAge(string $show_years = ''): string 10068add1155SRico Sonntag { 10078add1155SRico Sonntag return $this->parentsQuery('age', 'ASC', 'F', (bool) $show_years); 10088add1155SRico Sonntag } 10098add1155SRico Sonntag 10108add1155SRico Sonntag /** 10118add1155SRico Sonntag * Find the oldest mother. 10128add1155SRico Sonntag * 10138add1155SRico Sonntag * @return string 10148add1155SRico Sonntag */ 10158add1155SRico Sonntag public function oldestMother(): string 10168add1155SRico Sonntag { 10178add1155SRico Sonntag return $this->parentsQuery('full', 'DESC', 'F', false); 10188add1155SRico Sonntag } 10198add1155SRico Sonntag 10208add1155SRico Sonntag /** 10218add1155SRico Sonntag * Find the name of the oldest mother. 10228add1155SRico Sonntag * 10238add1155SRico Sonntag * @return string 10248add1155SRico Sonntag */ 10258add1155SRico Sonntag public function oldestMotherName(): string 10268add1155SRico Sonntag { 10278add1155SRico Sonntag return $this->parentsQuery('name', 'DESC', 'F', false); 10288add1155SRico Sonntag } 10298add1155SRico Sonntag 10308add1155SRico Sonntag /** 10318add1155SRico Sonntag * Find the age of the oldest mother. 10328add1155SRico Sonntag * 10338add1155SRico Sonntag * @param string $show_years 10348add1155SRico Sonntag * 10358add1155SRico Sonntag * @return string 10368add1155SRico Sonntag */ 10378add1155SRico Sonntag public function oldestMotherAge(string $show_years = ''): string 10388add1155SRico Sonntag { 10398add1155SRico Sonntag return $this->parentsQuery('age', 'DESC', 'F', (bool) $show_years); 10408add1155SRico Sonntag } 10418add1155SRico Sonntag 10428add1155SRico Sonntag /** 10438add1155SRico Sonntag * Find the youngest father. 10448add1155SRico Sonntag * 10458add1155SRico Sonntag * @return string 10468add1155SRico Sonntag */ 10478add1155SRico Sonntag public function youngestFather(): string 10488add1155SRico Sonntag { 10498add1155SRico Sonntag return $this->parentsQuery('full', 'ASC', 'M', false); 10508add1155SRico Sonntag } 10518add1155SRico Sonntag 10528add1155SRico Sonntag /** 10538add1155SRico Sonntag * Find the name of the youngest father. 10548add1155SRico Sonntag * 10558add1155SRico Sonntag * @return string 10568add1155SRico Sonntag */ 10578add1155SRico Sonntag public function youngestFatherName(): string 10588add1155SRico Sonntag { 10598add1155SRico Sonntag return $this->parentsQuery('name', 'ASC', 'M', false); 10608add1155SRico Sonntag } 10618add1155SRico Sonntag 10628add1155SRico Sonntag /** 10638add1155SRico Sonntag * Find the age of the youngest father. 10648add1155SRico Sonntag * 10658add1155SRico Sonntag * @param string $show_years 10668add1155SRico Sonntag * 10678add1155SRico Sonntag * @return string 10688add1155SRico Sonntag */ 10698add1155SRico Sonntag public function youngestFatherAge(string $show_years = ''): string 10708add1155SRico Sonntag { 10718add1155SRico Sonntag return $this->parentsQuery('age', 'ASC', 'M', (bool) $show_years); 10728add1155SRico Sonntag } 10738add1155SRico Sonntag 10748add1155SRico Sonntag /** 10758add1155SRico Sonntag * Find the oldest father. 10768add1155SRico Sonntag * 10778add1155SRico Sonntag * @return string 10788add1155SRico Sonntag */ 10798add1155SRico Sonntag public function oldestFather(): string 10808add1155SRico Sonntag { 10818add1155SRico Sonntag return $this->parentsQuery('full', 'DESC', 'M', false); 10828add1155SRico Sonntag } 10838add1155SRico Sonntag 10848add1155SRico Sonntag /** 10858add1155SRico Sonntag * Find the name of the oldest father. 10868add1155SRico Sonntag * 10878add1155SRico Sonntag * @return string 10888add1155SRico Sonntag */ 10898add1155SRico Sonntag public function oldestFatherName(): string 10908add1155SRico Sonntag { 10918add1155SRico Sonntag return $this->parentsQuery('name', 'DESC', 'M', false); 10928add1155SRico Sonntag } 10938add1155SRico Sonntag 10948add1155SRico Sonntag /** 10958add1155SRico Sonntag * Find the age of the oldest father. 10968add1155SRico Sonntag * 10978add1155SRico Sonntag * @param string $show_years 10988add1155SRico Sonntag * 10998add1155SRico Sonntag * @return string 11008add1155SRico Sonntag */ 11018add1155SRico Sonntag public function oldestFatherAge(string $show_years = ''): string 11028add1155SRico Sonntag { 11038add1155SRico Sonntag return $this->parentsQuery('age', 'DESC', 'M', (bool) $show_years); 11048add1155SRico Sonntag } 11058add1155SRico Sonntag 11068add1155SRico Sonntag /** 11078add1155SRico Sonntag * General query on age at marriage. 11088add1155SRico Sonntag * 11098add1155SRico Sonntag * @param string $type 11108add1155SRico Sonntag * @param string $age_dir 11118add1155SRico Sonntag * @param int $total 11128add1155SRico Sonntag * 11138add1155SRico Sonntag * @return string 11148add1155SRico Sonntag */ 11158add1155SRico Sonntag private function ageOfMarriageQuery(string $type, string $age_dir, int $total): string 11168add1155SRico Sonntag { 11178add1155SRico Sonntag if ($age_dir !== 'ASC') { 11188add1155SRico Sonntag $age_dir = 'DESC'; 11198add1155SRico Sonntag } 11208add1155SRico Sonntag 11218add1155SRico Sonntag $hrows = $this->runSql( 11228add1155SRico Sonntag " SELECT DISTINCT fam.f_id AS family, MIN(husbdeath.d_julianday2-married.d_julianday1) AS age" . 11238add1155SRico Sonntag " FROM `##families` AS fam" . 11248add1155SRico Sonntag " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" . 11258add1155SRico Sonntag " LEFT JOIN `##dates` AS husbdeath ON husbdeath.d_file = {$this->tree->id()}" . 11268add1155SRico Sonntag " WHERE" . 11278add1155SRico Sonntag " fam.f_file = {$this->tree->id()} AND" . 11288add1155SRico Sonntag " husbdeath.d_gid = fam.f_husb AND" . 11298add1155SRico Sonntag " husbdeath.d_fact = 'DEAT' AND" . 11308add1155SRico Sonntag " married.d_gid = fam.f_id AND" . 11318add1155SRico Sonntag " married.d_fact = 'MARR' AND" . 11328add1155SRico Sonntag " married.d_julianday1 < husbdeath.d_julianday2 AND" . 11338add1155SRico Sonntag " married.d_julianday1 <> 0" . 11348add1155SRico Sonntag " GROUP BY family" . 11358add1155SRico Sonntag " ORDER BY age {$age_dir}" 11368add1155SRico Sonntag ); 11378add1155SRico Sonntag 11388add1155SRico Sonntag $wrows = $this->runSql( 11398add1155SRico Sonntag " SELECT DISTINCT fam.f_id AS family, MIN(wifedeath.d_julianday2-married.d_julianday1) AS age" . 11408add1155SRico Sonntag " FROM `##families` AS fam" . 11418add1155SRico Sonntag " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" . 11428add1155SRico Sonntag " LEFT JOIN `##dates` AS wifedeath ON wifedeath.d_file = {$this->tree->id()}" . 11438add1155SRico Sonntag " WHERE" . 11448add1155SRico Sonntag " fam.f_file = {$this->tree->id()} AND" . 11458add1155SRico Sonntag " wifedeath.d_gid = fam.f_wife AND" . 11468add1155SRico Sonntag " wifedeath.d_fact = 'DEAT' AND" . 11478add1155SRico Sonntag " married.d_gid = fam.f_id AND" . 11488add1155SRico Sonntag " married.d_fact = 'MARR' AND" . 11498add1155SRico Sonntag " married.d_julianday1 < wifedeath.d_julianday2 AND" . 11508add1155SRico Sonntag " married.d_julianday1 <> 0" . 11518add1155SRico Sonntag " GROUP BY family" . 11528add1155SRico Sonntag " ORDER BY age {$age_dir}" 11538add1155SRico Sonntag ); 11548add1155SRico Sonntag 11558add1155SRico Sonntag $drows = $this->runSql( 11568add1155SRico Sonntag " SELECT DISTINCT fam.f_id AS family, MIN(divorced.d_julianday2-married.d_julianday1) AS age" . 11578add1155SRico Sonntag " FROM `##families` AS fam" . 11588add1155SRico Sonntag " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" . 11598add1155SRico Sonntag " LEFT JOIN `##dates` AS divorced ON divorced.d_file = {$this->tree->id()}" . 11608add1155SRico Sonntag " WHERE" . 11618add1155SRico Sonntag " fam.f_file = {$this->tree->id()} AND" . 11628add1155SRico Sonntag " married.d_gid = fam.f_id AND" . 11638add1155SRico Sonntag " married.d_fact = 'MARR' AND" . 11648add1155SRico Sonntag " divorced.d_gid = fam.f_id AND" . 11658add1155SRico Sonntag " divorced.d_fact IN ('DIV', 'ANUL', '_SEPR', '_DETS') AND" . 11668add1155SRico Sonntag " married.d_julianday1 < divorced.d_julianday2 AND" . 11678add1155SRico Sonntag " married.d_julianday1 <> 0" . 11688add1155SRico Sonntag " GROUP BY family" . 11698add1155SRico Sonntag " ORDER BY age {$age_dir}" 11708add1155SRico Sonntag ); 11718add1155SRico Sonntag 11728add1155SRico Sonntag $rows = []; 11738add1155SRico Sonntag foreach ($drows as $family) { 11748add1155SRico Sonntag $rows[$family->family] = $family->age; 11758add1155SRico Sonntag } 11768add1155SRico Sonntag 11778add1155SRico Sonntag foreach ($hrows as $family) { 11788add1155SRico Sonntag if (!isset($rows[$family->family])) { 11798add1155SRico Sonntag $rows[$family->family] = $family->age; 11808add1155SRico Sonntag } 11818add1155SRico Sonntag } 11828add1155SRico Sonntag 11838add1155SRico Sonntag foreach ($wrows as $family) { 11848add1155SRico Sonntag if (!isset($rows[$family->family])) { 11858add1155SRico Sonntag $rows[$family->family] = $family->age; 11868add1155SRico Sonntag } elseif ($rows[$family->family] > $family->age) { 11878add1155SRico Sonntag $rows[$family->family] = $family->age; 11888add1155SRico Sonntag } 11898add1155SRico Sonntag } 11908add1155SRico Sonntag 11918add1155SRico Sonntag if ($age_dir === 'DESC') { 11928add1155SRico Sonntag arsort($rows); 11938add1155SRico Sonntag } else { 11948add1155SRico Sonntag asort($rows); 11958add1155SRico Sonntag } 11968add1155SRico Sonntag 11978add1155SRico Sonntag $top10 = []; 11988add1155SRico Sonntag $i = 0; 11998add1155SRico Sonntag foreach ($rows as $fam => $age) { 12008add1155SRico Sonntag $family = Family::getInstance($fam, $this->tree); 12018add1155SRico Sonntag if ($type === 'name') { 12028add1155SRico Sonntag return $family->formatList(); 12038add1155SRico Sonntag } 12048add1155SRico Sonntag 12058add1155SRico Sonntag $age = $this->calculateAge((int) $age); 12068add1155SRico Sonntag 12078add1155SRico Sonntag if ($type === 'age') { 12088add1155SRico Sonntag return $age; 12098add1155SRico Sonntag } 12108add1155SRico Sonntag 12118add1155SRico Sonntag $husb = $family->getHusband(); 12128add1155SRico Sonntag $wife = $family->getWife(); 12138add1155SRico Sonntag 12148add1155SRico Sonntag if (($husb && ($husb->getAllDeathDates() || !$husb->isDead())) 12158add1155SRico Sonntag && ($wife && ($wife->getAllDeathDates() || !$wife->isDead())) 12168add1155SRico Sonntag ) { 12178add1155SRico Sonntag if ($family && $family->canShow()) { 12188add1155SRico Sonntag if ($type === 'list') { 12198add1155SRico Sonntag $top10[] = '<li><a href="' . e($family->url()) . '">' . $family->getFullName() . '</a> (' . $age . ')' . '</li>'; 12208add1155SRico Sonntag } else { 12218add1155SRico Sonntag $top10[] = '<a href="' . e($family->url()) . '">' . $family->getFullName() . '</a> (' . $age . ')'; 12228add1155SRico Sonntag } 12238add1155SRico Sonntag } 12248add1155SRico Sonntag if (++$i === $total) { 12258add1155SRico Sonntag break; 12268add1155SRico Sonntag } 12278add1155SRico Sonntag } 12288add1155SRico Sonntag } 12298add1155SRico Sonntag 12308add1155SRico Sonntag if ($type === 'list') { 12318add1155SRico Sonntag $top10 = implode('', $top10); 12328add1155SRico Sonntag } else { 12338add1155SRico Sonntag $top10 = implode('; ', $top10); 12348add1155SRico Sonntag } 12358add1155SRico Sonntag 12368add1155SRico Sonntag if (I18N::direction() === 'rtl') { 12378add1155SRico Sonntag $top10 = str_replace([ 12388add1155SRico Sonntag '[', 12398add1155SRico Sonntag ']', 12408add1155SRico Sonntag '(', 12418add1155SRico Sonntag ')', 12428add1155SRico Sonntag '+', 12438add1155SRico Sonntag ], [ 12448add1155SRico Sonntag '‏[', 12458add1155SRico Sonntag '‏]', 12468add1155SRico Sonntag '‏(', 12478add1155SRico Sonntag '‏)', 12488add1155SRico Sonntag '‏+', 12498add1155SRico Sonntag ], $top10); 12508add1155SRico Sonntag } 12518add1155SRico Sonntag 12528add1155SRico Sonntag if ($type === 'list') { 12538add1155SRico Sonntag return '<ul>' . $top10 . '</ul>'; 12548add1155SRico Sonntag } 12558add1155SRico Sonntag 12568add1155SRico Sonntag return $top10; 12578add1155SRico Sonntag } 12588add1155SRico Sonntag 12598add1155SRico Sonntag /** 12608add1155SRico Sonntag * General query on marriage ages. 12618add1155SRico Sonntag * 12628add1155SRico Sonntag * @return string 12638add1155SRico Sonntag */ 12648add1155SRico Sonntag public function topAgeOfMarriageFamily(): string 12658add1155SRico Sonntag { 12668add1155SRico Sonntag return $this->ageOfMarriageQuery('name', 'DESC', 1); 12678add1155SRico Sonntag } 12688add1155SRico Sonntag 12698add1155SRico Sonntag /** 12708add1155SRico Sonntag * General query on marriage ages. 12718add1155SRico Sonntag * 12728add1155SRico Sonntag * @return string 12738add1155SRico Sonntag */ 12748add1155SRico Sonntag public function topAgeOfMarriage(): string 12758add1155SRico Sonntag { 12768add1155SRico Sonntag return $this->ageOfMarriageQuery('age', 'DESC', 1); 12778add1155SRico Sonntag } 12788add1155SRico Sonntag 12798add1155SRico Sonntag /** 12808add1155SRico Sonntag * General query on marriage ages. 12818add1155SRico Sonntag * 12828add1155SRico Sonntag * @param int $total 12838add1155SRico Sonntag * 12848add1155SRico Sonntag * @return string 12858add1155SRico Sonntag */ 12868add1155SRico Sonntag public function topAgeOfMarriageFamilies(int $total = 10): string 12878add1155SRico Sonntag { 12888add1155SRico Sonntag return $this->ageOfMarriageQuery('nolist', 'DESC', $total); 12898add1155SRico Sonntag } 12908add1155SRico Sonntag 12918add1155SRico Sonntag /** 12928add1155SRico Sonntag * General query on marriage ages. 12938add1155SRico Sonntag * 12948add1155SRico Sonntag * @param int $total 12958add1155SRico Sonntag * 12968add1155SRico Sonntag * @return string 12978add1155SRico Sonntag */ 12988add1155SRico Sonntag public function topAgeOfMarriageFamiliesList(int $total = 10): string 12998add1155SRico Sonntag { 13008add1155SRico Sonntag return $this->ageOfMarriageQuery('list', 'DESC', $total); 13018add1155SRico Sonntag } 13028add1155SRico Sonntag 13038add1155SRico Sonntag /** 13048add1155SRico Sonntag * General query on marriage ages. 13058add1155SRico Sonntag * 13068add1155SRico Sonntag * @return string 13078add1155SRico Sonntag */ 13088add1155SRico Sonntag public function minAgeOfMarriageFamily(): string 13098add1155SRico Sonntag { 13108add1155SRico Sonntag return $this->ageOfMarriageQuery('name', 'ASC', 1); 13118add1155SRico Sonntag } 13128add1155SRico Sonntag 13138add1155SRico Sonntag /** 13148add1155SRico Sonntag * General query on marriage ages. 13158add1155SRico Sonntag * 13168add1155SRico Sonntag * @return string 13178add1155SRico Sonntag */ 13188add1155SRico Sonntag public function minAgeOfMarriage(): string 13198add1155SRico Sonntag { 13208add1155SRico Sonntag return $this->ageOfMarriageQuery('age', 'ASC', 1); 13218add1155SRico Sonntag } 13228add1155SRico Sonntag 13238add1155SRico Sonntag /** 13248add1155SRico Sonntag * General query on marriage ages. 13258add1155SRico Sonntag * 13268add1155SRico Sonntag * @param int $total 13278add1155SRico Sonntag * 13288add1155SRico Sonntag * @return string 13298add1155SRico Sonntag */ 13308add1155SRico Sonntag public function minAgeOfMarriageFamilies(int $total = 10): string 13318add1155SRico Sonntag { 13328add1155SRico Sonntag return $this->ageOfMarriageQuery('nolist', 'ASC', $total); 13338add1155SRico Sonntag } 13348add1155SRico Sonntag 13358add1155SRico Sonntag /** 13368add1155SRico Sonntag * General query on marriage ages. 13378add1155SRico Sonntag * 13388add1155SRico Sonntag * @param int $total 13398add1155SRico Sonntag * 13408add1155SRico Sonntag * @return string 13418add1155SRico Sonntag */ 13428add1155SRico Sonntag public function minAgeOfMarriageFamiliesList(int $total = 10): string 13438add1155SRico Sonntag { 13448add1155SRico Sonntag return $this->ageOfMarriageQuery('list', 'ASC', $total); 13458add1155SRico Sonntag } 13468add1155SRico Sonntag 13478add1155SRico Sonntag /** 13488add1155SRico Sonntag * Find the ages between spouses. 13498add1155SRico Sonntag * 13508add1155SRico Sonntag * @param string $age_dir 13518add1155SRico Sonntag * @param int $total 13528add1155SRico Sonntag * 13538add1155SRico Sonntag * @return array 13548add1155SRico Sonntag */ 13558add1155SRico Sonntag private function ageBetweenSpousesQuery(string $age_dir, int $total): array 13568add1155SRico Sonntag { 13578add1155SRico Sonntag if ($age_dir === 'DESC') { 13588add1155SRico Sonntag $sql = 13598add1155SRico Sonntag "SELECT f_id AS xref, MIN(wife.d_julianday2-husb.d_julianday1) AS age" . 13608add1155SRico Sonntag " FROM `##families`" . 13618add1155SRico Sonntag " JOIN `##dates` AS wife ON wife.d_gid = f_wife AND wife.d_file = f_file" . 13628add1155SRico Sonntag " JOIN `##dates` AS husb ON husb.d_gid = f_husb AND husb.d_file = f_file" . 13638add1155SRico Sonntag " WHERE f_file = :tree_id" . 13648add1155SRico Sonntag " AND husb.d_fact = 'BIRT'" . 13658add1155SRico Sonntag " AND wife.d_fact = 'BIRT'" . 13668add1155SRico Sonntag " AND wife.d_julianday2 >= husb.d_julianday1 AND husb.d_julianday1 <> 0" . 13678add1155SRico Sonntag " GROUP BY xref" . 13688add1155SRico Sonntag " ORDER BY age DESC" . 13698add1155SRico Sonntag " LIMIT :limit"; 13708add1155SRico Sonntag } else { 13718add1155SRico Sonntag $sql = 13728add1155SRico Sonntag "SELECT f_id AS xref, MIN(husb.d_julianday2-wife.d_julianday1) AS age" . 13738add1155SRico Sonntag " FROM `##families`" . 13748add1155SRico Sonntag " JOIN `##dates` AS wife ON wife.d_gid = f_wife AND wife.d_file = f_file" . 13758add1155SRico Sonntag " JOIN `##dates` AS husb ON husb.d_gid = f_husb AND husb.d_file = f_file" . 13768add1155SRico Sonntag " WHERE f_file = :tree_id" . 13778add1155SRico Sonntag " AND husb.d_fact = 'BIRT'" . 13788add1155SRico Sonntag " AND wife.d_fact = 'BIRT'" . 13798add1155SRico Sonntag " AND husb.d_julianday2 >= wife.d_julianday1 AND wife.d_julianday1 <> 0" . 13808add1155SRico Sonntag " GROUP BY xref" . 13818add1155SRico Sonntag " ORDER BY age DESC" . 13828add1155SRico Sonntag " LIMIT :limit"; 13838add1155SRico Sonntag } 13848add1155SRico Sonntag 13858add1155SRico Sonntag $rows = Database::prepare( 13868add1155SRico Sonntag $sql 13878add1155SRico Sonntag )->execute([ 13888add1155SRico Sonntag 'tree_id' => $this->tree->id(), 13898add1155SRico Sonntag 'limit' => $total, 13908add1155SRico Sonntag ])->fetchAll(); 13918add1155SRico Sonntag 13928add1155SRico Sonntag $top10 = []; 13938add1155SRico Sonntag 13948add1155SRico Sonntag foreach ($rows as $fam) { 13958add1155SRico Sonntag $family = Family::getInstance($fam->xref, $this->tree); 13968add1155SRico Sonntag 13978add1155SRico Sonntag if ($fam->age < 0) { 13988add1155SRico Sonntag break; 13998add1155SRico Sonntag } 14008add1155SRico Sonntag 14018add1155SRico Sonntag if ($family->canShow()) { 14028add1155SRico Sonntag $top10[] = [ 14038add1155SRico Sonntag 'family' => $family, 14048add1155SRico Sonntag 'age' => $this->calculateAge((int) $fam->age), 14058add1155SRico Sonntag ]; 14068add1155SRico Sonntag } 14078add1155SRico Sonntag } 14088add1155SRico Sonntag 14098add1155SRico Sonntag return $top10; 14108add1155SRico Sonntag } 14118add1155SRico Sonntag 14128add1155SRico Sonntag /** 14138add1155SRico Sonntag * Find the age between husband and wife. 14148add1155SRico Sonntag * 14158add1155SRico Sonntag * @param int $total 14168add1155SRico Sonntag * 14178add1155SRico Sonntag * @return string 14188add1155SRico Sonntag */ 14198add1155SRico Sonntag public function ageBetweenSpousesMF(int $total = 10): string 14208add1155SRico Sonntag { 14218add1155SRico Sonntag $records = $this->ageBetweenSpousesQuery('DESC', $total); 14228add1155SRico Sonntag 14238add1155SRico Sonntag return view( 14248add1155SRico Sonntag 'statistics/families/top10-nolist-spouses', 14258add1155SRico Sonntag [ 14268add1155SRico Sonntag 'records' => $records, 14278add1155SRico Sonntag ] 14288add1155SRico Sonntag ); 14298add1155SRico Sonntag } 14308add1155SRico Sonntag 14318add1155SRico Sonntag /** 14328add1155SRico Sonntag * Find the age between husband and wife. 14338add1155SRico Sonntag * 14348add1155SRico Sonntag * @param int $total 14358add1155SRico Sonntag * 14368add1155SRico Sonntag * @return string 14378add1155SRico Sonntag */ 14388add1155SRico Sonntag public function ageBetweenSpousesMFList(int $total = 10): string 14398add1155SRico Sonntag { 14408add1155SRico Sonntag $records = $this->ageBetweenSpousesQuery('DESC', $total); 14418add1155SRico Sonntag 14428add1155SRico Sonntag return view( 14438add1155SRico Sonntag 'statistics/families/top10-list-spouses', 14448add1155SRico Sonntag [ 14458add1155SRico Sonntag 'records' => $records, 14468add1155SRico Sonntag ] 14478add1155SRico Sonntag ); 14488add1155SRico Sonntag } 14498add1155SRico Sonntag 14508add1155SRico Sonntag /** 14518add1155SRico Sonntag * Find the age between wife and husband.. 14528add1155SRico Sonntag * 14538add1155SRico Sonntag * @param int $total 14548add1155SRico Sonntag * 14558add1155SRico Sonntag * @return string 14568add1155SRico Sonntag */ 14578add1155SRico Sonntag public function ageBetweenSpousesFM(int $total = 10): string 14588add1155SRico Sonntag { 14598add1155SRico Sonntag $records = $this->ageBetweenSpousesQuery('ASC', $total); 14608add1155SRico Sonntag 14618add1155SRico Sonntag return view( 14628add1155SRico Sonntag 'statistics/families/top10-nolist-spouses', 14638add1155SRico Sonntag [ 14648add1155SRico Sonntag 'records' => $records, 14658add1155SRico Sonntag ] 14668add1155SRico Sonntag ); 14678add1155SRico Sonntag } 14688add1155SRico Sonntag 14698add1155SRico Sonntag /** 14708add1155SRico Sonntag * Find the age between wife and husband.. 14718add1155SRico Sonntag * 14728add1155SRico Sonntag * @param int $total 14738add1155SRico Sonntag * 14748add1155SRico Sonntag * @return string 14758add1155SRico Sonntag */ 14768add1155SRico Sonntag public function ageBetweenSpousesFMList(int $total = 10): string 14778add1155SRico Sonntag { 14788add1155SRico Sonntag $records = $this->ageBetweenSpousesQuery('ASC', $total); 14798add1155SRico Sonntag 14808add1155SRico Sonntag return view( 14818add1155SRico Sonntag 'statistics/families/top10-list-spouses', 14828add1155SRico Sonntag [ 14838add1155SRico Sonntag 'records' => $records, 14848add1155SRico Sonntag ] 14858add1155SRico Sonntag ); 14868add1155SRico Sonntag } 14878add1155SRico Sonntag 14888add1155SRico Sonntag /** 14898add1155SRico Sonntag * General query on ages at marriage. 14908add1155SRico Sonntag * 14918add1155SRico Sonntag * @param string $sex 14928add1155SRico Sonntag * @param int $year1 14938add1155SRico Sonntag * @param int $year2 14948add1155SRico Sonntag * 14958add1155SRico Sonntag * @return array 14968add1155SRico Sonntag */ 14978add1155SRico Sonntag public function statsMarrAgeQuery($sex = 'M', $year1 = -1, $year2 = -1): array 14988add1155SRico Sonntag { 14998add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 15008add1155SRico Sonntag $years = " married.d_year BETWEEN {$year1} AND {$year2} AND "; 15018add1155SRico Sonntag } else { 15028add1155SRico Sonntag $years = ''; 15038add1155SRico Sonntag } 15048add1155SRico Sonntag 15058add1155SRico Sonntag $rows = $this->runSql( 15068add1155SRico Sonntag "SELECT " . 15078add1155SRico Sonntag " fam.f_id, " . 15088add1155SRico Sonntag " birth.d_gid, " . 15098add1155SRico Sonntag " married.d_julianday2-birth.d_julianday1 AS age " . 15108add1155SRico Sonntag "FROM `##dates` AS married " . 15118add1155SRico Sonntag "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " . 15128add1155SRico Sonntag "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_husb AND birth.d_file=fam.f_file) " . 15138add1155SRico Sonntag "WHERE " . 15148add1155SRico Sonntag " '{$sex}' IN ('M', 'BOTH') AND {$years} " . 15158add1155SRico Sonntag " married.d_file={$this->tree->id()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " . 15168add1155SRico Sonntag " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " . 15178add1155SRico Sonntag " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " . 15188add1155SRico Sonntag "UNION ALL " . 15198add1155SRico Sonntag "SELECT " . 15208add1155SRico Sonntag " fam.f_id, " . 15218add1155SRico Sonntag " birth.d_gid, " . 15228add1155SRico Sonntag " married.d_julianday2-birth.d_julianday1 AS age " . 15238add1155SRico Sonntag "FROM `##dates` AS married " . 15248add1155SRico Sonntag "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " . 15258add1155SRico Sonntag "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_wife AND birth.d_file=fam.f_file) " . 15268add1155SRico Sonntag "WHERE " . 15278add1155SRico Sonntag " '{$sex}' IN ('F', 'BOTH') AND {$years} " . 15288add1155SRico Sonntag " married.d_file={$this->tree->id()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " . 15298add1155SRico Sonntag " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " . 15308add1155SRico Sonntag " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " 15318add1155SRico Sonntag ); 15328add1155SRico Sonntag 15338add1155SRico Sonntag foreach ($rows as $row) { 15348add1155SRico Sonntag $row->age = (int) $row->age; 15358add1155SRico Sonntag } 15368add1155SRico Sonntag 15378add1155SRico Sonntag return $rows; 15388add1155SRico Sonntag } 15398add1155SRico Sonntag 15408add1155SRico Sonntag /** 15418add1155SRico Sonntag * General query on marriage ages. 15428add1155SRico Sonntag * 15438add1155SRico Sonntag * @return string 15448add1155SRico Sonntag */ 154588de55fdSRico Sonntag public function statsMarrAge(): string 15468add1155SRico Sonntag { 15478add1155SRico Sonntag return (new ChartMarriageAge($this->tree)) 154888de55fdSRico Sonntag ->chartMarriageAge(); 15498add1155SRico Sonntag } 15508add1155SRico Sonntag 15518add1155SRico Sonntag /** 15528add1155SRico Sonntag * Query the database for marriage tags. 15538add1155SRico Sonntag * 15548add1155SRico Sonntag * @param string $type 15558add1155SRico Sonntag * @param string $age_dir 15568add1155SRico Sonntag * @param string $sex 15578add1155SRico Sonntag * @param bool $show_years 15588add1155SRico Sonntag * 15598add1155SRico Sonntag * @return string 15608add1155SRico Sonntag */ 15618add1155SRico Sonntag private function marriageQuery(string $type, string $age_dir, string $sex, bool $show_years): string 15628add1155SRico Sonntag { 15638add1155SRico Sonntag if ($sex === 'F') { 15648add1155SRico Sonntag $sex_field = 'f_wife'; 15658add1155SRico Sonntag } else { 15668add1155SRico Sonntag $sex_field = 'f_husb'; 15678add1155SRico Sonntag } 15688add1155SRico Sonntag 15698add1155SRico Sonntag if ($age_dir !== 'ASC') { 15708add1155SRico Sonntag $age_dir = 'DESC'; 15718add1155SRico Sonntag } 15728add1155SRico Sonntag 15738add1155SRico Sonntag $rows = $this->runSql( 15748add1155SRico Sonntag " SELECT fam.f_id AS famid, fam.{$sex_field}, married.d_julianday2-birth.d_julianday1 AS age, indi.i_id AS i_id" . 15758add1155SRico Sonntag " FROM `##families` AS fam" . 15768add1155SRico Sonntag " LEFT JOIN `##dates` AS birth ON birth.d_file = {$this->tree->id()}" . 15778add1155SRico Sonntag " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" . 15788add1155SRico Sonntag " LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->id()}" . 15798add1155SRico Sonntag " WHERE" . 15808add1155SRico Sonntag " birth.d_gid = indi.i_id AND" . 15818add1155SRico Sonntag " married.d_gid = fam.f_id AND" . 15828add1155SRico Sonntag " indi.i_id = fam.{$sex_field} AND" . 15838add1155SRico Sonntag " fam.f_file = {$this->tree->id()} AND" . 15848add1155SRico Sonntag " birth.d_fact = 'BIRT' AND" . 15858add1155SRico Sonntag " married.d_fact = 'MARR' AND" . 15868add1155SRico Sonntag " birth.d_julianday1 <> 0 AND" . 15878add1155SRico Sonntag " married.d_julianday2 > birth.d_julianday1 AND" . 15888add1155SRico Sonntag " i_sex='{$sex}'" . 15898add1155SRico Sonntag " ORDER BY" . 15908add1155SRico Sonntag " married.d_julianday2-birth.d_julianday1 {$age_dir} LIMIT 1" 15918add1155SRico Sonntag ); 15928add1155SRico Sonntag 15938add1155SRico Sonntag if (!isset($rows[0])) { 15948add1155SRico Sonntag return ''; 15958add1155SRico Sonntag } 15968add1155SRico Sonntag 15978add1155SRico Sonntag $row = $rows[0]; 15988add1155SRico Sonntag if (isset($row->famid)) { 15998add1155SRico Sonntag $family = Family::getInstance($row->famid, $this->tree); 16008add1155SRico Sonntag } 16018add1155SRico Sonntag 16028add1155SRico Sonntag if (isset($row->i_id)) { 16038add1155SRico Sonntag $person = Individual::getInstance($row->i_id, $this->tree); 16048add1155SRico Sonntag } 16058add1155SRico Sonntag 16068add1155SRico Sonntag switch ($type) { 16078add1155SRico Sonntag default: 16088add1155SRico Sonntag case 'full': 16098add1155SRico Sonntag if ($family && $family->canShow()) { 16108add1155SRico Sonntag $result = $family->formatList(); 16118add1155SRico Sonntag } else { 16128add1155SRico Sonntag $result = I18N::translate('This information is private and cannot be shown.'); 16138add1155SRico Sonntag } 16148add1155SRico Sonntag break; 16158add1155SRico Sonntag 16168add1155SRico Sonntag case 'name': 16178add1155SRico Sonntag $result = '<a href="' . e($family->url()) . '">' . $person->getFullName() . '</a>'; 16188add1155SRico Sonntag break; 16198add1155SRico Sonntag 16208add1155SRico Sonntag case 'age': 16218add1155SRico Sonntag $age = $row->age; 16228add1155SRico Sonntag 16238add1155SRico Sonntag if ($show_years) { 16248add1155SRico Sonntag $result = $this->calculateAge((int) $row->age); 16258add1155SRico Sonntag } else { 16268add1155SRico Sonntag $result = I18N::number((int) ($age / 365.25)); 16278add1155SRico Sonntag } 16288add1155SRico Sonntag 16298add1155SRico Sonntag break; 16308add1155SRico Sonntag } 16318add1155SRico Sonntag 16328add1155SRico Sonntag return $result; 16338add1155SRico Sonntag } 16348add1155SRico Sonntag 16358add1155SRico Sonntag /** 16368add1155SRico Sonntag * Find the youngest wife. 16378add1155SRico Sonntag * 16388add1155SRico Sonntag * @return string 16398add1155SRico Sonntag */ 16408add1155SRico Sonntag public function youngestMarriageFemale(): string 16418add1155SRico Sonntag { 16428add1155SRico Sonntag return $this->marriageQuery('full', 'ASC', 'F', false); 16438add1155SRico Sonntag } 16448add1155SRico Sonntag 16458add1155SRico Sonntag /** 16468add1155SRico Sonntag * Find the name of the youngest wife. 16478add1155SRico Sonntag * 16488add1155SRico Sonntag * @return string 16498add1155SRico Sonntag */ 16508add1155SRico Sonntag public function youngestMarriageFemaleName(): string 16518add1155SRico Sonntag { 16528add1155SRico Sonntag return $this->marriageQuery('name', 'ASC', 'F', false); 16538add1155SRico Sonntag } 16548add1155SRico Sonntag 16558add1155SRico Sonntag /** 16568add1155SRico Sonntag * Find the age of the youngest wife. 16578add1155SRico Sonntag * 16588add1155SRico Sonntag * @param string $show_years 16598add1155SRico Sonntag * 16608add1155SRico Sonntag * @return string 16618add1155SRico Sonntag */ 16628add1155SRico Sonntag public function youngestMarriageFemaleAge(string $show_years = ''): string 16638add1155SRico Sonntag { 16648add1155SRico Sonntag return $this->marriageQuery('age', 'ASC', 'F', (bool) $show_years); 16658add1155SRico Sonntag } 16668add1155SRico Sonntag 16678add1155SRico Sonntag /** 16688add1155SRico Sonntag * Find the oldest wife. 16698add1155SRico Sonntag * 16708add1155SRico Sonntag * @return string 16718add1155SRico Sonntag */ 16728add1155SRico Sonntag public function oldestMarriageFemale(): string 16738add1155SRico Sonntag { 16748add1155SRico Sonntag return $this->marriageQuery('full', 'DESC', 'F', false); 16758add1155SRico Sonntag } 16768add1155SRico Sonntag 16778add1155SRico Sonntag /** 16788add1155SRico Sonntag * Find the name of the oldest wife. 16798add1155SRico Sonntag * 16808add1155SRico Sonntag * @return string 16818add1155SRico Sonntag */ 16828add1155SRico Sonntag public function oldestMarriageFemaleName(): string 16838add1155SRico Sonntag { 16848add1155SRico Sonntag return $this->marriageQuery('name', 'DESC', 'F', false); 16858add1155SRico Sonntag } 16868add1155SRico Sonntag 16878add1155SRico Sonntag /** 16888add1155SRico Sonntag * Find the age of the oldest wife. 16898add1155SRico Sonntag * 16908add1155SRico Sonntag * @param string $show_years 16918add1155SRico Sonntag * 16928add1155SRico Sonntag * @return string 16938add1155SRico Sonntag */ 16948add1155SRico Sonntag public function oldestMarriageFemaleAge(string $show_years = ''): string 16958add1155SRico Sonntag { 16968add1155SRico Sonntag return $this->marriageQuery('age', 'DESC', 'F', (bool) $show_years); 16978add1155SRico Sonntag } 16988add1155SRico Sonntag 16998add1155SRico Sonntag /** 17008add1155SRico Sonntag * Find the youngest husband. 17018add1155SRico Sonntag * 17028add1155SRico Sonntag * @return string 17038add1155SRico Sonntag */ 17048add1155SRico Sonntag public function youngestMarriageMale(): string 17058add1155SRico Sonntag { 17068add1155SRico Sonntag return $this->marriageQuery('full', 'ASC', 'M', false); 17078add1155SRico Sonntag } 17088add1155SRico Sonntag 17098add1155SRico Sonntag /** 17108add1155SRico Sonntag * Find the name of the youngest husband. 17118add1155SRico Sonntag * 17128add1155SRico Sonntag * @return string 17138add1155SRico Sonntag */ 17148add1155SRico Sonntag public function youngestMarriageMaleName(): string 17158add1155SRico Sonntag { 17168add1155SRico Sonntag return $this->marriageQuery('name', 'ASC', 'M', false); 17178add1155SRico Sonntag } 17188add1155SRico Sonntag 17198add1155SRico Sonntag /** 17208add1155SRico Sonntag * Find the age of the youngest husband. 17218add1155SRico Sonntag * 17228add1155SRico Sonntag * @param string $show_years 17238add1155SRico Sonntag * 17248add1155SRico Sonntag * @return string 17258add1155SRico Sonntag */ 17268add1155SRico Sonntag public function youngestMarriageMaleAge(string $show_years = ''): string 17278add1155SRico Sonntag { 17288add1155SRico Sonntag return $this->marriageQuery('age', 'ASC', 'M', (bool) $show_years); 17298add1155SRico Sonntag } 17308add1155SRico Sonntag 17318add1155SRico Sonntag /** 17328add1155SRico Sonntag * Find the oldest husband. 17338add1155SRico Sonntag * 17348add1155SRico Sonntag * @return string 17358add1155SRico Sonntag */ 17368add1155SRico Sonntag public function oldestMarriageMale(): string 17378add1155SRico Sonntag { 17388add1155SRico Sonntag return $this->marriageQuery('full', 'DESC', 'M', false); 17398add1155SRico Sonntag } 17408add1155SRico Sonntag 17418add1155SRico Sonntag /** 17428add1155SRico Sonntag * Find the name of the oldest husband. 17438add1155SRico Sonntag * 17448add1155SRico Sonntag * @return string 17458add1155SRico Sonntag */ 17468add1155SRico Sonntag public function oldestMarriageMaleName(): string 17478add1155SRico Sonntag { 17488add1155SRico Sonntag return $this->marriageQuery('name', 'DESC', 'M', false); 17498add1155SRico Sonntag } 17508add1155SRico Sonntag 17518add1155SRico Sonntag /** 17528add1155SRico Sonntag * Find the age of the oldest husband. 17538add1155SRico Sonntag * 17548add1155SRico Sonntag * @param string $show_years 17558add1155SRico Sonntag * 17568add1155SRico Sonntag * @return string 17578add1155SRico Sonntag */ 17588add1155SRico Sonntag public function oldestMarriageMaleAge(string $show_years = ''): string 17598add1155SRico Sonntag { 17608add1155SRico Sonntag return $this->marriageQuery('age', 'DESC', 'M', (bool) $show_years); 17618add1155SRico Sonntag } 17628add1155SRico Sonntag 17638add1155SRico Sonntag /** 17648add1155SRico Sonntag * General query on marriages. 17658add1155SRico Sonntag * 17668add1155SRico Sonntag * @param bool $first 17678add1155SRico Sonntag * @param int $year1 17688add1155SRico Sonntag * @param int $year2 17698add1155SRico Sonntag * 17708add1155SRico Sonntag * @return array 17718add1155SRico Sonntag */ 17728add1155SRico Sonntag public function statsMarrQuery(bool $first = false, int $year1 = -1, int $year2 = -1): array 17738add1155SRico Sonntag { 17748add1155SRico Sonntag if ($first) { 17758add1155SRico Sonntag $years = ''; 17768add1155SRico Sonntag 17778add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 17788add1155SRico Sonntag $years = " married.d_year BETWEEN '{$year1}' AND '{$year2}' AND"; 17798add1155SRico Sonntag } 17808add1155SRico Sonntag 17818add1155SRico Sonntag $sql = 17828add1155SRico Sonntag " SELECT fam.f_id AS fams, fam.f_husb, fam.f_wife, married.d_julianday2 AS age, married.d_month AS month, indi.i_id AS indi" . 17838add1155SRico Sonntag " FROM `##families` AS fam" . 17848add1155SRico Sonntag " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" . 17858add1155SRico Sonntag " LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->id()}" . 17868add1155SRico Sonntag " WHERE" . 17878add1155SRico Sonntag " married.d_gid = fam.f_id AND" . 17888add1155SRico Sonntag " fam.f_file = {$this->tree->id()} AND" . 17898add1155SRico Sonntag " married.d_fact = 'MARR' AND" . 17908add1155SRico Sonntag " married.d_julianday2 <> 0 AND" . 17918add1155SRico Sonntag $years . 17928add1155SRico Sonntag " (indi.i_id = fam.f_husb OR indi.i_id = fam.f_wife)" . 17938add1155SRico Sonntag " ORDER BY fams, indi, age ASC"; 17948add1155SRico Sonntag } else { 17958add1155SRico Sonntag $sql = 17968add1155SRico Sonntag "SELECT d_month, COUNT(*) AS total" . 17978add1155SRico Sonntag " FROM `##dates`" . 17988add1155SRico Sonntag " WHERE d_file={$this->tree->id()} AND d_fact='MARR'"; 17998add1155SRico Sonntag 18008add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 18018add1155SRico Sonntag $sql .= " AND d_year BETWEEN '{$year1}' AND '{$year2}'"; 18028add1155SRico Sonntag } 18038add1155SRico Sonntag 18048add1155SRico Sonntag $sql .= " GROUP BY d_month"; 18058add1155SRico Sonntag } 18068add1155SRico Sonntag 18078add1155SRico Sonntag return $this->runSql($sql); 18088add1155SRico Sonntag } 18098add1155SRico Sonntag 18108add1155SRico Sonntag /** 18118add1155SRico Sonntag * General query on marriages. 18128add1155SRico Sonntag * 18138add1155SRico Sonntag * @param string|null $color_from 18148add1155SRico Sonntag * @param string|null $color_to 18158add1155SRico Sonntag * 18168add1155SRico Sonntag * @return string 18178add1155SRico Sonntag */ 181888de55fdSRico Sonntag public function statsMarr(string $color_from = null, string $color_to = null): string 18198add1155SRico Sonntag { 18208add1155SRico Sonntag return (new ChartMarriage($this->tree)) 182188de55fdSRico Sonntag ->chartMarriage($color_from, $color_to); 18228add1155SRico Sonntag } 18238add1155SRico Sonntag 18248add1155SRico Sonntag /** 18258add1155SRico Sonntag * General divorce query. 18268add1155SRico Sonntag * 18278add1155SRico Sonntag * @param string|null $color_from 18288add1155SRico Sonntag * @param string|null $color_to 18298add1155SRico Sonntag * 18308add1155SRico Sonntag * @return string 18318add1155SRico Sonntag */ 183288de55fdSRico Sonntag public function statsDiv(string $color_from = null, string $color_to = null): string 18338add1155SRico Sonntag { 18348add1155SRico Sonntag return (new ChartDivorce($this->tree)) 183588de55fdSRico Sonntag ->chartDivorce($color_from, $color_to); 18368add1155SRico Sonntag } 18378add1155SRico Sonntag} 1838