18add1155SRico Sonntag<?php 28add1155SRico Sonntag/** 38add1155SRico Sonntag * webtrees: online genealogy 4242a7862SGreg 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\Family; 218add1155SRico Sonntaguse Fisharebest\Webtrees\Functions\FunctionsDate; 22d1a467e4SGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 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; 34*2d7289dcSGreg Roachuse Illuminate\Database\Query\Builder; 35d1a467e4SGreg Roachuse Illuminate\Database\Query\JoinClause; 368add1155SRico Sonntaguse stdClass; 378add1155SRico Sonntag 388add1155SRico Sonntag/** 398add1155SRico Sonntag * 408add1155SRico Sonntag */ 418add1155SRico Sonntagclass FamilyRepository 428add1155SRico Sonntag{ 438add1155SRico Sonntag /** 448add1155SRico Sonntag * @var Tree 458add1155SRico Sonntag */ 468add1155SRico Sonntag private $tree; 478add1155SRico Sonntag 488add1155SRico Sonntag /** 498add1155SRico Sonntag * Constructor. 508add1155SRico Sonntag * 518add1155SRico Sonntag * @param Tree $tree 528add1155SRico Sonntag */ 538add1155SRico Sonntag public function __construct(Tree $tree) 548add1155SRico Sonntag { 558add1155SRico Sonntag $this->tree = $tree; 568add1155SRico Sonntag } 578add1155SRico Sonntag 588add1155SRico Sonntag /** 598add1155SRico Sonntag * General query on family. 608add1155SRico Sonntag * 618add1155SRico Sonntag * @param string $type 628add1155SRico Sonntag * 638add1155SRico Sonntag * @return string 648add1155SRico Sonntag */ 658add1155SRico Sonntag private function familyQuery(string $type): string 668add1155SRico Sonntag { 6744cdc21eSGreg Roach $row = DB::table('families') 6844cdc21eSGreg Roach ->where('f_file', '=', $this->tree->id()) 6944cdc21eSGreg Roach ->orderBy('f_numchil', 'desc') 7044cdc21eSGreg Roach ->first(); 718add1155SRico Sonntag 7244cdc21eSGreg Roach if ($row === null) { 738add1155SRico Sonntag return ''; 748add1155SRico Sonntag } 758add1155SRico Sonntag 7644cdc21eSGreg Roach /** @var Family $family */ 7744cdc21eSGreg Roach $family = Family::rowMapper()($row); 788add1155SRico Sonntag 7944cdc21eSGreg Roach if (!$family->canShow()) { 8044cdc21eSGreg Roach return I18N::translate('This information is private and cannot be shown.'); 818add1155SRico Sonntag } 828add1155SRico Sonntag 838add1155SRico Sonntag switch ($type) { 848add1155SRico Sonntag default: 858add1155SRico Sonntag case 'full': 8644cdc21eSGreg Roach return $family->formatList(); 878add1155SRico Sonntag 8844cdc21eSGreg Roach case 'size': 8944cdc21eSGreg Roach return I18N::number((int) $row->f_numchil); 9044cdc21eSGreg Roach 9144cdc21eSGreg Roach case 'name': 9239ca88baSGreg Roach return '<a href="' . e($family->url()) . '">' . $family->fullName() . '</a>'; 9344cdc21eSGreg Roach } 948add1155SRico Sonntag } 958add1155SRico Sonntag 968add1155SRico Sonntag /** 978add1155SRico Sonntag * Run an SQL query and cache the result. 988add1155SRico Sonntag * 998add1155SRico Sonntag * @param string $sql 1008add1155SRico Sonntag * 1018add1155SRico Sonntag * @return stdClass[] 1028add1155SRico Sonntag */ 1038add1155SRico Sonntag private function runSql($sql): array 1048add1155SRico Sonntag { 1058add1155SRico Sonntag return Sql::runSql($sql); 1068add1155SRico Sonntag } 1078add1155SRico Sonntag 1088add1155SRico Sonntag /** 1098add1155SRico Sonntag * Find the family with the most children. 1108add1155SRico Sonntag * 1118add1155SRico Sonntag * @return string 1128add1155SRico Sonntag */ 1138add1155SRico Sonntag public function largestFamily(): string 1148add1155SRico Sonntag { 1158add1155SRico Sonntag return $this->familyQuery('full'); 1168add1155SRico Sonntag } 1178add1155SRico Sonntag 1188add1155SRico Sonntag /** 1198add1155SRico Sonntag * Find the number of children in the largest family. 1208add1155SRico Sonntag * 1218add1155SRico Sonntag * @return string 1228add1155SRico Sonntag */ 1238add1155SRico Sonntag public function largestFamilySize(): string 1248add1155SRico Sonntag { 1258add1155SRico Sonntag return $this->familyQuery('size'); 1268add1155SRico Sonntag } 1278add1155SRico Sonntag 1288add1155SRico Sonntag /** 1298add1155SRico Sonntag * Find the family with the most children. 1308add1155SRico Sonntag * 1318add1155SRico Sonntag * @return string 1328add1155SRico Sonntag */ 1338add1155SRico Sonntag public function largestFamilyName(): string 1348add1155SRico Sonntag { 1358add1155SRico Sonntag return $this->familyQuery('name'); 1368add1155SRico Sonntag } 1378add1155SRico Sonntag 1388add1155SRico Sonntag /** 1398add1155SRico Sonntag * Find the couple with the most grandchildren. 1408add1155SRico Sonntag * 1418add1155SRico Sonntag * @param int $total 1428add1155SRico Sonntag * 1438add1155SRico Sonntag * @return array 1448add1155SRico Sonntag */ 1458add1155SRico Sonntag private function topTenGrandFamilyQuery(int $total): array 1468add1155SRico Sonntag { 147*2d7289dcSGreg Roach return DB::table('families') 148*2d7289dcSGreg Roach ->join('link AS children', function (JoinClause $join) { 149*2d7289dcSGreg Roach $join 150*2d7289dcSGreg Roach ->on('children.l_from', '=', 'f_id') 151*2d7289dcSGreg Roach ->on('children.l_file', '=', 'f_file') 152*2d7289dcSGreg Roach ->where('children.l_type', '=', 'CHIL'); 153*2d7289dcSGreg Roach })->join('link AS mchildren', function (JoinClause $join) { 154*2d7289dcSGreg Roach $join 155*2d7289dcSGreg Roach ->on('mchildren.l_file', '=', 'children.l_file') 156*2d7289dcSGreg Roach ->on('mchildren.l_from', '=', 'children.l_to') 157*2d7289dcSGreg Roach ->where('mchildren.l_type', '=', 'FAMS'); 158*2d7289dcSGreg Roach })->join('link AS gchildren', function (JoinClause $join) { 159*2d7289dcSGreg Roach $join 160*2d7289dcSGreg Roach ->on('gchildren.l_file', '=', 'mchildren.l_file') 161*2d7289dcSGreg Roach ->on('gchildren.l_from', '=', 'mchildren.l_to') 162*2d7289dcSGreg Roach ->where('gchildren.l_type', '=', 'CHIL'); 163*2d7289dcSGreg Roach }) 164*2d7289dcSGreg Roach ->where('f_file', '=', $this->tree->id()) 165*2d7289dcSGreg Roach ->groupBy(['f_id', 'f_file']) 166*2d7289dcSGreg Roach ->orderBy(DB::raw('COUNT(*)'), 'DESC') 167*2d7289dcSGreg Roach ->select('families.*') 168*2d7289dcSGreg Roach ->limit($total) 169*2d7289dcSGreg Roach ->get() 170*2d7289dcSGreg Roach ->map(Family::rowMapper()) 171*2d7289dcSGreg Roach ->filter(GedcomRecord::accessFilter()) 172*2d7289dcSGreg Roach ->map(function (Family $family): array { 173*2d7289dcSGreg Roach $count = 0; 174*2d7289dcSGreg Roach foreach ($family->children() as $child) { 175*2d7289dcSGreg Roach foreach ($child->spouseFamilies() as $spouse_family) { 176*2d7289dcSGreg Roach $count += $spouse_family->children()->count(); 177*2d7289dcSGreg Roach } 1788add1155SRico Sonntag } 1798add1155SRico Sonntag 180*2d7289dcSGreg Roach return [ 1818add1155SRico Sonntag 'family' => $family, 182*2d7289dcSGreg Roach 'count' => $count, 1838add1155SRico Sonntag ]; 184*2d7289dcSGreg Roach }) 185*2d7289dcSGreg Roach ->all(); 1868add1155SRico Sonntag } 1878add1155SRico Sonntag 1888add1155SRico Sonntag /** 1898add1155SRico Sonntag * Find the couple with the most grandchildren. 1908add1155SRico Sonntag * 1918add1155SRico Sonntag * @param int $total 1928add1155SRico Sonntag * 1938add1155SRico Sonntag * @return string 1948add1155SRico Sonntag */ 1958add1155SRico Sonntag public function topTenLargestGrandFamily(int $total = 10): string 1968add1155SRico Sonntag { 197*2d7289dcSGreg Roach return view('statistics/families/top10-nolist-grand', [ 198*2d7289dcSGreg Roach 'records' => $this->topTenGrandFamilyQuery($total), 199*2d7289dcSGreg Roach ]); 2008add1155SRico Sonntag } 2018add1155SRico Sonntag 2028add1155SRico Sonntag /** 2038add1155SRico Sonntag * Find the couple with the most grandchildren. 2048add1155SRico Sonntag * 2058add1155SRico Sonntag * @param int $total 2068add1155SRico Sonntag * 2078add1155SRico Sonntag * @return string 2088add1155SRico Sonntag */ 2098add1155SRico Sonntag public function topTenLargestGrandFamilyList(int $total = 10): string 2108add1155SRico Sonntag { 211*2d7289dcSGreg Roach return view('statistics/families/top10-list-grand', [ 212*2d7289dcSGreg Roach 'records' => $this->topTenGrandFamilyQuery($total), 213*2d7289dcSGreg Roach ]); 2148add1155SRico Sonntag } 2158add1155SRico Sonntag 2168add1155SRico Sonntag /** 2178add1155SRico Sonntag * Find the families with no children. 2188add1155SRico Sonntag * 2198add1155SRico Sonntag * @return int 2208add1155SRico Sonntag */ 2218add1155SRico Sonntag private function noChildrenFamiliesQuery(): int 2228add1155SRico Sonntag { 223*2d7289dcSGreg Roach return DB::table('families') 224*2d7289dcSGreg Roach ->where('f_file', '=', $this->tree->id()) 225*2d7289dcSGreg Roach ->where('f_numchil', '=', 0) 226*2d7289dcSGreg Roach ->count(); 2278add1155SRico Sonntag } 2288add1155SRico Sonntag 2298add1155SRico Sonntag /** 2308add1155SRico Sonntag * Find the families with no children. 2318add1155SRico Sonntag * 2328add1155SRico Sonntag * @return string 2338add1155SRico Sonntag */ 2348add1155SRico Sonntag public function noChildrenFamilies(): string 2358add1155SRico Sonntag { 2368add1155SRico Sonntag return I18N::number($this->noChildrenFamiliesQuery()); 2378add1155SRico Sonntag } 2388add1155SRico Sonntag 2398add1155SRico Sonntag /** 2408add1155SRico Sonntag * Find the families with no children. 2418add1155SRico Sonntag * 2428add1155SRico Sonntag * @param string $type 2438add1155SRico Sonntag * 2448add1155SRico Sonntag * @return string 2458add1155SRico Sonntag */ 2468add1155SRico Sonntag public function noChildrenFamiliesList($type = 'list'): string 2478add1155SRico Sonntag { 2488add1155SRico Sonntag $rows = $this->runSql( 2498add1155SRico Sonntag " SELECT f_id AS family" . 2508add1155SRico Sonntag " FROM `##families` AS fam" . 2518add1155SRico Sonntag " WHERE f_numchil = 0 AND fam.f_file = {$this->tree->id()}" 2528add1155SRico Sonntag ); 2538add1155SRico Sonntag 2548add1155SRico Sonntag if (!isset($rows[0])) { 2558add1155SRico Sonntag return ''; 2568add1155SRico Sonntag } 2578add1155SRico Sonntag 2588add1155SRico Sonntag $top10 = []; 2598add1155SRico Sonntag foreach ($rows as $row) { 2608add1155SRico Sonntag $family = Family::getInstance($row->family, $this->tree); 2618add1155SRico Sonntag if ($family->canShow()) { 2628add1155SRico Sonntag if ($type === 'list') { 26339ca88baSGreg Roach $top10[] = '<li><a href="' . e($family->url()) . '">' . $family->fullName() . '</a></li>'; 2648add1155SRico Sonntag } else { 26539ca88baSGreg Roach $top10[] = '<a href="' . e($family->url()) . '">' . $family->fullName() . '</a>'; 2668add1155SRico Sonntag } 2678add1155SRico Sonntag } 2688add1155SRico Sonntag } 2698add1155SRico Sonntag 2708add1155SRico Sonntag if ($type === 'list') { 2718add1155SRico Sonntag $top10 = implode('', $top10); 2728add1155SRico Sonntag } else { 2738add1155SRico Sonntag $top10 = implode('; ', $top10); 2748add1155SRico Sonntag } 2758add1155SRico Sonntag 2768add1155SRico Sonntag if (I18N::direction() === 'rtl') { 2778add1155SRico Sonntag $top10 = str_replace([ 2788add1155SRico Sonntag '[', 2798add1155SRico Sonntag ']', 2808add1155SRico Sonntag '(', 2818add1155SRico Sonntag ')', 2828add1155SRico Sonntag '+', 2838add1155SRico Sonntag ], [ 2848add1155SRico Sonntag '‏[', 2858add1155SRico Sonntag '‏]', 2868add1155SRico Sonntag '‏(', 2878add1155SRico Sonntag '‏)', 2888add1155SRico Sonntag '‏+', 2898add1155SRico Sonntag ], $top10); 2908add1155SRico Sonntag } 2918add1155SRico Sonntag if ($type === 'list') { 2928add1155SRico Sonntag return '<ul>' . $top10 . '</ul>'; 2938add1155SRico Sonntag } 2948add1155SRico Sonntag 2958add1155SRico Sonntag return $top10; 2968add1155SRico Sonntag } 2978add1155SRico Sonntag 2988add1155SRico Sonntag /** 2998add1155SRico Sonntag * Create a chart of children with no families. 3008add1155SRico Sonntag * 3018add1155SRico Sonntag * @param int $year1 3028add1155SRico Sonntag * @param int $year2 3038add1155SRico Sonntag * 3048add1155SRico Sonntag * @return string 3058add1155SRico Sonntag */ 30688de55fdSRico Sonntag public function chartNoChildrenFamilies(int $year1 = -1, int $year2 = -1): string 3078add1155SRico Sonntag { 3088add1155SRico Sonntag $no_child_fam = $this->noChildrenFamiliesQuery(); 3098add1155SRico Sonntag 3108add1155SRico Sonntag return (new ChartNoChildrenFamilies($this->tree)) 31188de55fdSRico Sonntag ->chartNoChildrenFamilies($no_child_fam, $year1, $year2); 3128add1155SRico Sonntag } 3138add1155SRico Sonntag 3148add1155SRico Sonntag /** 3158add1155SRico Sonntag * Returns the ages between siblings. 3168add1155SRico Sonntag * 3178add1155SRico Sonntag * @param int $total The total number of records to query 3188add1155SRico Sonntag * 3198add1155SRico Sonntag * @return array 3208add1155SRico Sonntag */ 3218add1155SRico Sonntag private function ageBetweenSiblingsQuery(int $total): array 3228add1155SRico Sonntag { 3238add1155SRico Sonntag $rows = $this->runSql( 3248add1155SRico Sonntag " SELECT DISTINCT" . 3258add1155SRico Sonntag " link1.l_from AS family," . 3268add1155SRico Sonntag " link1.l_to AS ch1," . 3278add1155SRico Sonntag " link2.l_to AS ch2," . 3288add1155SRico Sonntag " child1.d_julianday2-child2.d_julianday2 AS age" . 3298add1155SRico Sonntag " FROM `##link` AS link1" . 3308add1155SRico Sonntag " LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->id()}" . 3318add1155SRico Sonntag " LEFT JOIN `##dates` AS child2 ON child2.d_file = {$this->tree->id()}" . 3328add1155SRico Sonntag " LEFT JOIN `##link` AS link2 ON link2.l_file = {$this->tree->id()}" . 3338add1155SRico Sonntag " WHERE" . 3348add1155SRico Sonntag " link1.l_file = {$this->tree->id()} AND" . 3358add1155SRico Sonntag " link1.l_from = link2.l_from AND" . 3368add1155SRico Sonntag " link1.l_type = 'CHIL' AND" . 3378add1155SRico Sonntag " child1.d_gid = link1.l_to AND" . 3388add1155SRico Sonntag " child1.d_fact = 'BIRT' AND" . 3398add1155SRico Sonntag " link2.l_type = 'CHIL' AND" . 3408add1155SRico Sonntag " child2.d_gid = link2.l_to AND" . 3418add1155SRico Sonntag " child2.d_fact = 'BIRT' AND" . 3428add1155SRico Sonntag " child1.d_julianday2 > child2.d_julianday2 AND" . 3438add1155SRico Sonntag " child2.d_julianday2 <> 0 AND" . 3448add1155SRico Sonntag " child1.d_gid <> child2.d_gid" . 3458add1155SRico Sonntag " ORDER BY age DESC" . 3468add1155SRico Sonntag " LIMIT " . $total 3478add1155SRico Sonntag ); 3488add1155SRico Sonntag 3498add1155SRico Sonntag if (!isset($rows[0])) { 3508add1155SRico Sonntag return []; 3518add1155SRico Sonntag } 3528add1155SRico Sonntag 3538add1155SRico Sonntag return $rows; 3548add1155SRico Sonntag } 3558add1155SRico Sonntag 3568add1155SRico Sonntag /** 3578add1155SRico Sonntag * Returns the calculated age the time of event. 3588add1155SRico Sonntag * 3598add1155SRico Sonntag * @param int $age The age from the database record 3608add1155SRico Sonntag * 3618add1155SRico Sonntag * @return string 3628add1155SRico Sonntag */ 3638add1155SRico Sonntag private function calculateAge(int $age): string 3648add1155SRico Sonntag { 3658add1155SRico Sonntag if ((int) ($age / 365.25) > 0) { 3668add1155SRico Sonntag $result = (int) ($age / 365.25) . 'y'; 3678add1155SRico Sonntag } elseif ((int) ($age / 30.4375) > 0) { 3688add1155SRico Sonntag $result = (int) ($age / 30.4375) . 'm'; 3698add1155SRico Sonntag } else { 3708add1155SRico Sonntag $result = $age . 'd'; 3718add1155SRico Sonntag } 3728add1155SRico Sonntag 3738add1155SRico Sonntag return FunctionsDate::getAgeAtEvent($result); 3748add1155SRico Sonntag } 3758add1155SRico Sonntag 3768add1155SRico Sonntag /** 3778add1155SRico Sonntag * Find the ages between siblings. 3788add1155SRico Sonntag * 3798add1155SRico Sonntag * @param int $total The total number of records to query 3808add1155SRico Sonntag * 3818add1155SRico Sonntag * @return array 3828add1155SRico Sonntag * @throws \Exception 3838add1155SRico Sonntag */ 3848add1155SRico Sonntag private function ageBetweenSiblingsNoList(int $total): array 3858add1155SRico Sonntag { 3868add1155SRico Sonntag $rows = $this->ageBetweenSiblingsQuery($total); 3878add1155SRico Sonntag 3888add1155SRico Sonntag foreach ($rows as $fam) { 3898add1155SRico Sonntag $family = Family::getInstance($fam->family, $this->tree); 3908add1155SRico Sonntag $child1 = Individual::getInstance($fam->ch1, $this->tree); 3918add1155SRico Sonntag $child2 = Individual::getInstance($fam->ch2, $this->tree); 3928add1155SRico Sonntag 3938add1155SRico Sonntag if ($child1->canShow() && $child2->canShow()) { 3948add1155SRico Sonntag // ! Single array (no list) 3958add1155SRico Sonntag return [ 3968add1155SRico Sonntag 'child1' => $child1, 3978add1155SRico Sonntag 'child2' => $child2, 3988add1155SRico Sonntag 'family' => $family, 3998add1155SRico Sonntag 'age' => $this->calculateAge((int) $fam->age), 4008add1155SRico Sonntag ]; 4018add1155SRico Sonntag } 4028add1155SRico Sonntag } 4038add1155SRico Sonntag 4048add1155SRico Sonntag return []; 4058add1155SRico Sonntag } 4068add1155SRico Sonntag 4078add1155SRico Sonntag /** 4088add1155SRico Sonntag * Find the ages between siblings. 4098add1155SRico Sonntag * 4108add1155SRico Sonntag * @param int $total The total number of records to query 4118add1155SRico Sonntag * @param bool $one Include each family only once if true 4128add1155SRico Sonntag * 4138add1155SRico Sonntag * @return array 4148add1155SRico Sonntag * @throws \Exception 4158add1155SRico Sonntag */ 4168add1155SRico Sonntag private function ageBetweenSiblingsList(int $total, bool $one): array 4178add1155SRico Sonntag { 4188add1155SRico Sonntag $rows = $this->ageBetweenSiblingsQuery($total); 4198add1155SRico Sonntag $top10 = []; 4208add1155SRico Sonntag $dist = []; 4218add1155SRico Sonntag 4228add1155SRico Sonntag foreach ($rows as $fam) { 4238add1155SRico Sonntag $family = Family::getInstance($fam->family, $this->tree); 4248add1155SRico Sonntag $child1 = Individual::getInstance($fam->ch1, $this->tree); 4258add1155SRico Sonntag $child2 = Individual::getInstance($fam->ch2, $this->tree); 4268add1155SRico Sonntag 4278add1155SRico Sonntag $age = $this->calculateAge((int) $fam->age); 4288add1155SRico Sonntag 4298add1155SRico Sonntag if ($one && !\in_array($fam->family, $dist, true)) { 4308add1155SRico Sonntag if ($child1->canShow() && $child2->canShow()) { 4318add1155SRico Sonntag $top10[] = [ 4328add1155SRico Sonntag 'child1' => $child1, 4338add1155SRico Sonntag 'child2' => $child2, 4348add1155SRico Sonntag 'family' => $family, 4358add1155SRico Sonntag 'age' => $age, 4368add1155SRico Sonntag ]; 4378add1155SRico Sonntag 4388add1155SRico Sonntag $dist[] = $fam->family; 4398add1155SRico Sonntag } 4408add1155SRico Sonntag } elseif (!$one && $child1->canShow() && $child2->canShow()) { 4418add1155SRico Sonntag $top10[] = [ 4428add1155SRico Sonntag 'child1' => $child1, 4438add1155SRico Sonntag 'child2' => $child2, 4448add1155SRico Sonntag 'family' => $family, 4458add1155SRico Sonntag 'age' => $age, 4468add1155SRico Sonntag ]; 4478add1155SRico Sonntag } 4488add1155SRico Sonntag } 4498add1155SRico Sonntag 4508add1155SRico Sonntag // TODO 4518add1155SRico Sonntag // if (I18N::direction() === 'rtl') { 4528add1155SRico Sonntag // $top10 = str_replace([ 4538add1155SRico Sonntag // '[', 4548add1155SRico Sonntag // ']', 4558add1155SRico Sonntag // '(', 4568add1155SRico Sonntag // ')', 4578add1155SRico Sonntag // '+', 4588add1155SRico Sonntag // ], [ 4598add1155SRico Sonntag // '‏[', 4608add1155SRico Sonntag // '‏]', 4618add1155SRico Sonntag // '‏(', 4628add1155SRico Sonntag // '‏)', 4638add1155SRico Sonntag // '‏+', 4648add1155SRico Sonntag // ], $top10); 4658add1155SRico Sonntag // } 4668add1155SRico Sonntag 4678add1155SRico Sonntag return $top10; 4688add1155SRico Sonntag } 4698add1155SRico Sonntag 4708add1155SRico Sonntag /** 4718add1155SRico Sonntag * Find the ages between siblings. 4728add1155SRico Sonntag * 4738add1155SRico Sonntag * @param int $total The total number of records to query 4748add1155SRico Sonntag * 4758add1155SRico Sonntag * @return string 4768add1155SRico Sonntag */ 4778add1155SRico Sonntag private function ageBetweenSiblingsAge(int $total): string 4788add1155SRico Sonntag { 4798add1155SRico Sonntag $rows = $this->ageBetweenSiblingsQuery($total); 4808add1155SRico Sonntag 4818add1155SRico Sonntag foreach ($rows as $fam) { 4828add1155SRico Sonntag return $this->calculateAge((int) $fam->age); 4838add1155SRico Sonntag } 4848add1155SRico Sonntag 4858add1155SRico Sonntag return ''; 4868add1155SRico Sonntag } 4878add1155SRico Sonntag 4888add1155SRico Sonntag /** 4898add1155SRico Sonntag * Find the ages between siblings. 4908add1155SRico Sonntag * 4918add1155SRico Sonntag * @param int $total The total number of records to query 4928add1155SRico Sonntag * 4938add1155SRico Sonntag * @return string 4948add1155SRico Sonntag * @throws \Exception 4958add1155SRico Sonntag */ 4968add1155SRico Sonntag private function ageBetweenSiblingsName(int $total): string 4978add1155SRico Sonntag { 4988add1155SRico Sonntag $rows = $this->ageBetweenSiblingsQuery($total); 4998add1155SRico Sonntag 5008add1155SRico Sonntag foreach ($rows as $fam) { 5018add1155SRico Sonntag $family = Family::getInstance($fam->family, $this->tree); 5028add1155SRico Sonntag $child1 = Individual::getInstance($fam->ch1, $this->tree); 5038add1155SRico Sonntag $child2 = Individual::getInstance($fam->ch2, $this->tree); 5048add1155SRico Sonntag 5058add1155SRico Sonntag if ($child1->canShow() && $child2->canShow()) { 50639ca88baSGreg Roach $return = '<a href="' . e($child2->url()) . '">' . $child2->fullName() . '</a> '; 5078add1155SRico Sonntag $return .= I18N::translate('and') . ' '; 50839ca88baSGreg Roach $return .= '<a href="' . e($child1->url()) . '">' . $child1->fullName() . '</a>'; 5098add1155SRico Sonntag $return .= ' <a href="' . e($family->url()) . '">[' . I18N::translate('View this family') . ']</a>'; 5108add1155SRico Sonntag } else { 5118add1155SRico Sonntag $return = I18N::translate('This information is private and cannot be shown.'); 5128add1155SRico Sonntag } 5138add1155SRico Sonntag 5148add1155SRico Sonntag return $return; 5158add1155SRico Sonntag } 5168add1155SRico Sonntag 5178add1155SRico Sonntag return ''; 5188add1155SRico Sonntag } 5198add1155SRico Sonntag 5208add1155SRico Sonntag /** 5218add1155SRico Sonntag * Find the names of siblings with the widest age gap. 5228add1155SRico Sonntag * 5238add1155SRico Sonntag * @param int $total 5248add1155SRico Sonntag * 5258add1155SRico Sonntag * @return string 5268add1155SRico Sonntag */ 5278add1155SRico Sonntag public function topAgeBetweenSiblingsName(int $total = 10): string 5288add1155SRico Sonntag { 5298add1155SRico Sonntag return $this->ageBetweenSiblingsName($total); 5308add1155SRico Sonntag } 5318add1155SRico Sonntag 5328add1155SRico Sonntag /** 5338add1155SRico Sonntag * Find the widest age gap between siblings. 5348add1155SRico Sonntag * 5358add1155SRico Sonntag * @param int $total 5368add1155SRico Sonntag * 5378add1155SRico Sonntag * @return string 5388add1155SRico Sonntag */ 5398add1155SRico Sonntag public function topAgeBetweenSiblings(int $total = 10): string 5408add1155SRico Sonntag { 5418add1155SRico Sonntag return $this->ageBetweenSiblingsAge($total); 5428add1155SRico Sonntag } 5438add1155SRico Sonntag 5448add1155SRico Sonntag /** 5458add1155SRico Sonntag * Find the name of siblings with the widest age gap. 5468add1155SRico Sonntag * 5478add1155SRico Sonntag * @param int $total 5488add1155SRico Sonntag * 5498add1155SRico Sonntag * @return string 5508add1155SRico Sonntag */ 5518add1155SRico Sonntag public function topAgeBetweenSiblingsFullName(int $total = 10): string 5528add1155SRico Sonntag { 5538add1155SRico Sonntag $record = $this->ageBetweenSiblingsNoList($total); 5548add1155SRico Sonntag 555cb2263dcSGreg Roach if (empty($record)) { 556dd7dd2a1SRico Sonntag return I18N::translate('This information is not available.'); 557cb2263dcSGreg Roach } 558cb2263dcSGreg Roach 559cb2263dcSGreg Roach return view('statistics/families/top10-nolist-age', [ 5608add1155SRico Sonntag 'record' => $record, 561cb2263dcSGreg Roach ]); 5628add1155SRico Sonntag } 5638add1155SRico Sonntag 5648add1155SRico Sonntag /** 5658add1155SRico Sonntag * Find the siblings with the widest age gaps. 5668add1155SRico Sonntag * 5678add1155SRico Sonntag * @param int $total 5688add1155SRico Sonntag * @param string $one 5698add1155SRico Sonntag * 5708add1155SRico Sonntag * @return string 5718add1155SRico Sonntag */ 5728add1155SRico Sonntag public function topAgeBetweenSiblingsList(int $total = 10, string $one = ''): string 5738add1155SRico Sonntag { 5748add1155SRico Sonntag $records = $this->ageBetweenSiblingsList($total, (bool) $one); 5758add1155SRico Sonntag 576cb2263dcSGreg Roach return view('statistics/families/top10-list-age', [ 5778add1155SRico Sonntag 'records' => $records, 578cb2263dcSGreg Roach ]); 5798add1155SRico Sonntag } 5808add1155SRico Sonntag 5818add1155SRico Sonntag /** 5828add1155SRico Sonntag * General query on familes/children. 5838add1155SRico Sonntag * 5848add1155SRico Sonntag * @param string $sex 5858add1155SRico Sonntag * @param int $year1 5868add1155SRico Sonntag * @param int $year2 5878add1155SRico Sonntag * 5888add1155SRico Sonntag * @return stdClass[] 5898add1155SRico Sonntag */ 5908add1155SRico Sonntag public function statsChildrenQuery(string $sex = 'BOTH', int $year1 = -1, int $year2 = -1): array 5918add1155SRico Sonntag { 5928add1155SRico Sonntag if ($sex === 'M') { 5938add1155SRico Sonntag $sql = 5948add1155SRico Sonntag "SELECT num, COUNT(*) AS total FROM " . 5958add1155SRico Sonntag "(SELECT count(i_sex) AS num FROM `##link` " . 5968add1155SRico Sonntag "LEFT OUTER JOIN `##individuals` " . 5978add1155SRico Sonntag "ON l_from=i_id AND l_file=i_file AND i_sex='M' AND l_type='FAMC' " . 5988add1155SRico Sonntag "JOIN `##families` ON f_file=l_file AND f_id=l_to WHERE f_file={$this->tree->id()} GROUP BY l_to" . 5998add1155SRico Sonntag ") boys" . 6008add1155SRico Sonntag " GROUP BY num" . 6018add1155SRico Sonntag " ORDER BY num"; 6028add1155SRico Sonntag } elseif ($sex === 'F') { 6038add1155SRico Sonntag $sql = 6048add1155SRico Sonntag "SELECT num, COUNT(*) AS total FROM " . 6058add1155SRico Sonntag "(SELECT count(i_sex) AS num FROM `##link` " . 6068add1155SRico Sonntag "LEFT OUTER JOIN `##individuals` " . 6078add1155SRico Sonntag "ON l_from=i_id AND l_file=i_file AND i_sex='F' AND l_type='FAMC' " . 6088add1155SRico Sonntag "JOIN `##families` ON f_file=l_file AND f_id=l_to WHERE f_file={$this->tree->id()} GROUP BY l_to" . 6098add1155SRico Sonntag ") girls" . 6108add1155SRico Sonntag " GROUP BY num" . 6118add1155SRico Sonntag " ORDER BY num"; 6128add1155SRico Sonntag } else { 6138add1155SRico Sonntag $sql = "SELECT f_numchil, COUNT(*) AS total FROM `##families` "; 6148add1155SRico Sonntag 6158add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 6168add1155SRico Sonntag $sql .= 6178add1155SRico Sonntag "AS fam LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" 6188add1155SRico Sonntag . " WHERE" 6198add1155SRico Sonntag . " married.d_gid = fam.f_id AND" 6208add1155SRico Sonntag . " fam.f_file = {$this->tree->id()} AND" 6218add1155SRico Sonntag . " married.d_fact = 'MARR' AND" 6228add1155SRico Sonntag . " married.d_year BETWEEN '{$year1}' AND '{$year2}'"; 6238add1155SRico Sonntag } else { 6248add1155SRico Sonntag $sql .= "WHERE f_file={$this->tree->id()}"; 6258add1155SRico Sonntag } 6268add1155SRico Sonntag 6278add1155SRico Sonntag $sql .= ' GROUP BY f_numchil'; 6288add1155SRico Sonntag } 6298add1155SRico Sonntag 6308add1155SRico Sonntag return $this->runSql($sql); 6318add1155SRico Sonntag } 6328add1155SRico Sonntag 6338add1155SRico Sonntag /** 6348add1155SRico Sonntag * Genearl query on families/children. 6358add1155SRico Sonntag * 6368add1155SRico Sonntag * @return string 6378add1155SRico Sonntag */ 63888de55fdSRico Sonntag public function statsChildren(): string 6398add1155SRico Sonntag { 6408add1155SRico Sonntag return (new ChartChildren($this->tree)) 64188de55fdSRico Sonntag ->chartChildren(); 6428add1155SRico Sonntag } 6438add1155SRico Sonntag 6448add1155SRico Sonntag /** 6458add1155SRico Sonntag * Count the total children. 6468add1155SRico Sonntag * 6478add1155SRico Sonntag * @return string 6488add1155SRico Sonntag */ 6498add1155SRico Sonntag public function totalChildren(): string 6508add1155SRico Sonntag { 651d1a467e4SGreg Roach $total = (int) DB::table('families') 652d1a467e4SGreg Roach ->where('f_file', '=', $this->tree->id()) 653d1a467e4SGreg Roach ->sum('f_numchil'); 6548add1155SRico Sonntag 6558add1155SRico Sonntag return I18N::number($total); 6568add1155SRico Sonntag } 6578add1155SRico Sonntag 6588add1155SRico Sonntag /** 6598add1155SRico Sonntag * Find the average number of children in families. 6608add1155SRico Sonntag * 6618add1155SRico Sonntag * @return string 6628add1155SRico Sonntag */ 6638add1155SRico Sonntag public function averageChildren(): string 6648add1155SRico Sonntag { 665d1a467e4SGreg Roach $average = (float) DB::table('families') 666d1a467e4SGreg Roach ->where('f_file', '=', $this->tree->id()) 667d1a467e4SGreg Roach ->avg('f_numchil'); 6688add1155SRico Sonntag 6698add1155SRico Sonntag return I18N::number($average, 2); 6708add1155SRico Sonntag } 6718add1155SRico Sonntag 6728add1155SRico Sonntag /** 6738add1155SRico Sonntag * General query on families. 6748add1155SRico Sonntag * 6758add1155SRico Sonntag * @param int $total 6768add1155SRico Sonntag * 6778add1155SRico Sonntag * @return array 6788add1155SRico Sonntag */ 6798add1155SRico Sonntag private function topTenFamilyQuery(int $total): array 6808add1155SRico Sonntag { 681*2d7289dcSGreg Roach return DB::table('families') 682*2d7289dcSGreg Roach ->where('f_file', '=', $this->tree->id()) 683*2d7289dcSGreg Roach ->orderBy('f_numchil', 'DESC') 684*2d7289dcSGreg Roach ->limit($total) 685*2d7289dcSGreg Roach ->get() 686*2d7289dcSGreg Roach ->map(Family::rowMapper()) 687*2d7289dcSGreg Roach ->filter(GedcomRecord::accessFilter()) 688*2d7289dcSGreg Roach ->map(function (Family $family): array { 689*2d7289dcSGreg Roach return [ 6908add1155SRico Sonntag 'family' => $family, 691*2d7289dcSGreg Roach 'count' => $family->numberOfChildren(), 6928add1155SRico Sonntag ]; 693*2d7289dcSGreg Roach }) 694*2d7289dcSGreg Roach ->all(); 6958add1155SRico Sonntag } 6968add1155SRico Sonntag 6978add1155SRico Sonntag /** 6988add1155SRico Sonntag * The the families with the most children. 6998add1155SRico Sonntag * 7008add1155SRico Sonntag * @param int $total 7018add1155SRico Sonntag * 7028add1155SRico Sonntag * @return string 7038add1155SRico Sonntag */ 7048add1155SRico Sonntag public function topTenLargestFamily(int $total = 10): string 7058add1155SRico Sonntag { 7068add1155SRico Sonntag $records = $this->topTenFamilyQuery($total); 7078add1155SRico Sonntag 7088add1155SRico Sonntag return view( 7098add1155SRico Sonntag 'statistics/families/top10-nolist', 7108add1155SRico Sonntag [ 7118add1155SRico Sonntag 'records' => $records, 7128add1155SRico Sonntag ] 7138add1155SRico Sonntag ); 7148add1155SRico Sonntag } 7158add1155SRico Sonntag 7168add1155SRico Sonntag /** 7178add1155SRico Sonntag * Find the families with the most children. 7188add1155SRico Sonntag * 7198add1155SRico Sonntag * @param int $total 7208add1155SRico Sonntag * 7218add1155SRico Sonntag * @return string 7228add1155SRico Sonntag */ 7238add1155SRico Sonntag public function topTenLargestFamilyList(int $total = 10): string 7248add1155SRico Sonntag { 7258add1155SRico Sonntag $records = $this->topTenFamilyQuery($total); 7268add1155SRico Sonntag 7278add1155SRico Sonntag return view( 7288add1155SRico Sonntag 'statistics/families/top10-list', 7298add1155SRico Sonntag [ 7308add1155SRico Sonntag 'records' => $records, 7318add1155SRico Sonntag ] 7328add1155SRico Sonntag ); 7338add1155SRico Sonntag } 7348add1155SRico Sonntag 7358add1155SRico Sonntag /** 7368add1155SRico Sonntag * Create a chart of the largest families. 7378add1155SRico Sonntag * 7388add1155SRico Sonntag * @param string|null $color_from 7398add1155SRico Sonntag * @param string|null $color_to 7408add1155SRico Sonntag * @param int $total 7418add1155SRico Sonntag * 7428add1155SRico Sonntag * @return string 7438add1155SRico Sonntag */ 7448add1155SRico Sonntag public function chartLargestFamilies( 7458add1155SRico Sonntag string $color_from = null, 7468add1155SRico Sonntag string $color_to = null, 7478add1155SRico Sonntag int $total = 10 748e2cbf57aSGreg Roach ): string { 7498add1155SRico Sonntag return (new ChartFamilyLargest($this->tree)) 75088de55fdSRico Sonntag ->chartLargestFamilies($color_from, $color_to, $total); 7518add1155SRico Sonntag } 7528add1155SRico Sonntag 7538add1155SRico Sonntag /** 7548add1155SRico Sonntag * Find the month in the year of the birth of the first child. 7558add1155SRico Sonntag * 7568add1155SRico Sonntag * @param bool $sex 7578add1155SRico Sonntag * 7588add1155SRico Sonntag * @return stdClass[] 7598add1155SRico Sonntag */ 7608add1155SRico Sonntag public function monthFirstChildQuery(bool $sex = false): array 7618add1155SRico Sonntag { 7628add1155SRico Sonntag if ($sex) { 7638add1155SRico Sonntag $sql_sex1 = ', i_sex'; 7648add1155SRico Sonntag $sql_sex2 = " JOIN `##individuals` AS child ON child1.d_file = i_file AND child1.d_gid = child.i_id "; 7658add1155SRico Sonntag } else { 7668add1155SRico Sonntag $sql_sex1 = ''; 7678add1155SRico Sonntag $sql_sex2 = ''; 7688add1155SRico Sonntag } 7698add1155SRico Sonntag 7708add1155SRico Sonntag $sql = 7718add1155SRico Sonntag "SELECT d_month{$sql_sex1}, COUNT(*) AS total " . 7728add1155SRico Sonntag "FROM (" . 7738add1155SRico Sonntag " SELECT family{$sql_sex1}, MIN(date) AS d_date, d_month" . 7748add1155SRico Sonntag " FROM (" . 7758add1155SRico Sonntag " SELECT" . 7768add1155SRico Sonntag " link1.l_from AS family," . 7778add1155SRico Sonntag " link1.l_to AS child," . 7788add1155SRico Sonntag " child1.d_julianday2 AS date," . 7798add1155SRico Sonntag " child1.d_month as d_month" . 7808add1155SRico Sonntag $sql_sex1 . 7818add1155SRico Sonntag " FROM `##link` AS link1" . 7828add1155SRico Sonntag " LEFT JOIN `##dates` AS child1 ON child1.d_file = {$this->tree->id()}" . 7838add1155SRico Sonntag $sql_sex2 . 7848add1155SRico Sonntag " WHERE" . 7858add1155SRico Sonntag " link1.l_file = {$this->tree->id()} AND" . 7868add1155SRico Sonntag " link1.l_type = 'CHIL' AND" . 7878add1155SRico Sonntag " child1.d_gid = link1.l_to AND" . 7888add1155SRico Sonntag " child1.d_fact = 'BIRT' AND" . 7898add1155SRico Sonntag " child1.d_month IN ('JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC')" . 7908add1155SRico Sonntag " ORDER BY date" . 7918add1155SRico Sonntag " ) AS children" . 7928add1155SRico Sonntag " GROUP BY family, d_month{$sql_sex1}" . 7938add1155SRico Sonntag ") AS first_child " . 7948add1155SRico Sonntag "GROUP BY d_month"; 7958add1155SRico Sonntag 7968add1155SRico Sonntag if ($sex) { 7978add1155SRico Sonntag $sql .= ', i_sex'; 7988add1155SRico Sonntag } 7998add1155SRico Sonntag 8008add1155SRico Sonntag return $this->runSql($sql); 8018add1155SRico Sonntag } 8028add1155SRico Sonntag 8038add1155SRico Sonntag /** 8048add1155SRico Sonntag * Number of husbands. 8058add1155SRico Sonntag * 8068add1155SRico Sonntag * @return string 8078add1155SRico Sonntag */ 8088add1155SRico Sonntag public function totalMarriedMales(): string 8098add1155SRico Sonntag { 810d1a467e4SGreg Roach $n = (int) DB::table('families') 811d1a467e4SGreg Roach ->where('f_file', '=', $this->tree->id()) 812d1a467e4SGreg Roach ->where('f_gedcom', 'LIKE', "%\n1 MARR%") 813d1a467e4SGreg Roach ->distinct() 814d1a467e4SGreg Roach ->count('f_husb'); 8158add1155SRico Sonntag 8168add1155SRico Sonntag return I18N::number($n); 8178add1155SRico Sonntag } 8188add1155SRico Sonntag 8198add1155SRico Sonntag /** 8208add1155SRico Sonntag * Number of wives. 8218add1155SRico Sonntag * 8228add1155SRico Sonntag * @return string 8238add1155SRico Sonntag */ 8248add1155SRico Sonntag public function totalMarriedFemales(): string 8258add1155SRico Sonntag { 826d1a467e4SGreg Roach $n = (int) DB::table('families') 827d1a467e4SGreg Roach ->where('f_file', '=', $this->tree->id()) 828d1a467e4SGreg Roach ->where('f_gedcom', 'LIKE', "%\n1 MARR%") 829d1a467e4SGreg Roach ->distinct() 830d1a467e4SGreg Roach ->count('f_wife'); 8318add1155SRico Sonntag 8328add1155SRico Sonntag return I18N::number($n); 8338add1155SRico Sonntag } 8348add1155SRico Sonntag 8358add1155SRico Sonntag /** 8368add1155SRico Sonntag * General query on parents. 8378add1155SRico Sonntag * 8388add1155SRico Sonntag * @param string $type 8398add1155SRico Sonntag * @param string $age_dir 8408add1155SRico Sonntag * @param string $sex 8418add1155SRico Sonntag * @param bool $show_years 8428add1155SRico Sonntag * 8438add1155SRico Sonntag * @return string 8448add1155SRico Sonntag */ 8458add1155SRico Sonntag private function parentsQuery(string $type, string $age_dir, string $sex, bool $show_years): string 8468add1155SRico Sonntag { 8478add1155SRico Sonntag if ($sex === 'F') { 8488add1155SRico Sonntag $sex_field = 'WIFE'; 8498add1155SRico Sonntag } else { 8508add1155SRico Sonntag $sex_field = 'HUSB'; 8518add1155SRico Sonntag } 8528add1155SRico Sonntag 8538add1155SRico Sonntag if ($age_dir !== 'ASC') { 8548add1155SRico Sonntag $age_dir = 'DESC'; 8558add1155SRico Sonntag } 8568add1155SRico Sonntag 8578add1155SRico Sonntag $rows = $this->runSql( 8588add1155SRico Sonntag " SELECT" . 8598add1155SRico Sonntag " parentfamily.l_to AS id," . 8608add1155SRico Sonntag " childbirth.d_julianday2-birth.d_julianday1 AS age" . 8618add1155SRico Sonntag " FROM `##link` AS parentfamily" . 8628add1155SRico Sonntag " JOIN `##link` AS childfamily ON childfamily.l_file = {$this->tree->id()}" . 8638add1155SRico Sonntag " JOIN `##dates` AS birth ON birth.d_file = {$this->tree->id()}" . 8648add1155SRico Sonntag " JOIN `##dates` AS childbirth ON childbirth.d_file = {$this->tree->id()}" . 8658add1155SRico Sonntag " WHERE" . 8668add1155SRico Sonntag " birth.d_gid = parentfamily.l_to AND" . 8678add1155SRico Sonntag " childfamily.l_to = childbirth.d_gid AND" . 8688add1155SRico Sonntag " childfamily.l_type = 'CHIL' AND" . 8698add1155SRico Sonntag " parentfamily.l_type = '{$sex_field}' AND" . 8708add1155SRico Sonntag " childfamily.l_from = parentfamily.l_from AND" . 8718add1155SRico Sonntag " parentfamily.l_file = {$this->tree->id()} AND" . 8728add1155SRico Sonntag " birth.d_fact = 'BIRT' AND" . 8738add1155SRico Sonntag " childbirth.d_fact = 'BIRT' AND" . 8748add1155SRico Sonntag " birth.d_julianday1 <> 0 AND" . 8758add1155SRico Sonntag " childbirth.d_julianday2 > birth.d_julianday1" . 8768add1155SRico Sonntag " ORDER BY age {$age_dir} LIMIT 1" 8778add1155SRico Sonntag ); 8788add1155SRico Sonntag 8798add1155SRico Sonntag if (!isset($rows[0])) { 8808add1155SRico Sonntag return ''; 8818add1155SRico Sonntag } 8828add1155SRico Sonntag 8838add1155SRico Sonntag $row = $rows[0]; 8848add1155SRico Sonntag if (isset($row->id)) { 8858add1155SRico Sonntag $person = Individual::getInstance($row->id, $this->tree); 8868add1155SRico Sonntag } 8878add1155SRico Sonntag 8888add1155SRico Sonntag switch ($type) { 8898add1155SRico Sonntag default: 8908add1155SRico Sonntag case 'full': 8918add1155SRico Sonntag if ($person && $person->canShow()) { 8928add1155SRico Sonntag $result = $person->formatList(); 8938add1155SRico Sonntag } else { 8948add1155SRico Sonntag $result = I18N::translate('This information is private and cannot be shown.'); 8958add1155SRico Sonntag } 8968add1155SRico Sonntag break; 8978add1155SRico Sonntag 8988add1155SRico Sonntag case 'name': 89939ca88baSGreg Roach $result = '<a href="' . e($person->url()) . '">' . $person->fullName() . '</a>'; 9008add1155SRico Sonntag break; 9018add1155SRico Sonntag 9028add1155SRico Sonntag case 'age': 9038add1155SRico Sonntag $age = $row->age; 9048add1155SRico Sonntag 9058add1155SRico Sonntag if ($show_years) { 9068add1155SRico Sonntag $result = $this->calculateAge((int) $row->age); 9078add1155SRico Sonntag } else { 9088add1155SRico Sonntag $result = (string) floor($age / 365.25); 9098add1155SRico Sonntag } 9108add1155SRico Sonntag 9118add1155SRico Sonntag break; 9128add1155SRico Sonntag } 9138add1155SRico Sonntag 9148add1155SRico Sonntag return $result; 9158add1155SRico Sonntag } 9168add1155SRico Sonntag 9178add1155SRico Sonntag /** 9188add1155SRico Sonntag * Find the youngest mother 9198add1155SRico Sonntag * 9208add1155SRico Sonntag * @return string 9218add1155SRico Sonntag */ 9228add1155SRico Sonntag public function youngestMother(): string 9238add1155SRico Sonntag { 9248add1155SRico Sonntag return $this->parentsQuery('full', 'ASC', 'F', false); 9258add1155SRico Sonntag } 9268add1155SRico Sonntag 9278add1155SRico Sonntag /** 9288add1155SRico Sonntag * Find the name of the youngest mother. 9298add1155SRico Sonntag * 9308add1155SRico Sonntag * @return string 9318add1155SRico Sonntag */ 9328add1155SRico Sonntag public function youngestMotherName(): string 9338add1155SRico Sonntag { 9348add1155SRico Sonntag return $this->parentsQuery('name', 'ASC', 'F', false); 9358add1155SRico Sonntag } 9368add1155SRico Sonntag 9378add1155SRico Sonntag /** 9388add1155SRico Sonntag * Find the age of the youngest mother. 9398add1155SRico Sonntag * 9408add1155SRico Sonntag * @param string $show_years 9418add1155SRico Sonntag * 9428add1155SRico Sonntag * @return string 9438add1155SRico Sonntag */ 9448add1155SRico Sonntag public function youngestMotherAge(string $show_years = ''): string 9458add1155SRico Sonntag { 9468add1155SRico Sonntag return $this->parentsQuery('age', 'ASC', 'F', (bool) $show_years); 9478add1155SRico Sonntag } 9488add1155SRico Sonntag 9498add1155SRico Sonntag /** 9508add1155SRico Sonntag * Find the oldest mother. 9518add1155SRico Sonntag * 9528add1155SRico Sonntag * @return string 9538add1155SRico Sonntag */ 9548add1155SRico Sonntag public function oldestMother(): string 9558add1155SRico Sonntag { 9568add1155SRico Sonntag return $this->parentsQuery('full', 'DESC', 'F', false); 9578add1155SRico Sonntag } 9588add1155SRico Sonntag 9598add1155SRico Sonntag /** 9608add1155SRico Sonntag * Find the name of the oldest mother. 9618add1155SRico Sonntag * 9628add1155SRico Sonntag * @return string 9638add1155SRico Sonntag */ 9648add1155SRico Sonntag public function oldestMotherName(): string 9658add1155SRico Sonntag { 9668add1155SRico Sonntag return $this->parentsQuery('name', 'DESC', 'F', false); 9678add1155SRico Sonntag } 9688add1155SRico Sonntag 9698add1155SRico Sonntag /** 9708add1155SRico Sonntag * Find the age of the oldest mother. 9718add1155SRico Sonntag * 9728add1155SRico Sonntag * @param string $show_years 9738add1155SRico Sonntag * 9748add1155SRico Sonntag * @return string 9758add1155SRico Sonntag */ 9768add1155SRico Sonntag public function oldestMotherAge(string $show_years = ''): string 9778add1155SRico Sonntag { 9788add1155SRico Sonntag return $this->parentsQuery('age', 'DESC', 'F', (bool) $show_years); 9798add1155SRico Sonntag } 9808add1155SRico Sonntag 9818add1155SRico Sonntag /** 9828add1155SRico Sonntag * Find the youngest father. 9838add1155SRico Sonntag * 9848add1155SRico Sonntag * @return string 9858add1155SRico Sonntag */ 9868add1155SRico Sonntag public function youngestFather(): string 9878add1155SRico Sonntag { 9888add1155SRico Sonntag return $this->parentsQuery('full', 'ASC', 'M', false); 9898add1155SRico Sonntag } 9908add1155SRico Sonntag 9918add1155SRico Sonntag /** 9928add1155SRico Sonntag * Find the name of the youngest father. 9938add1155SRico Sonntag * 9948add1155SRico Sonntag * @return string 9958add1155SRico Sonntag */ 9968add1155SRico Sonntag public function youngestFatherName(): string 9978add1155SRico Sonntag { 9988add1155SRico Sonntag return $this->parentsQuery('name', 'ASC', 'M', false); 9998add1155SRico Sonntag } 10008add1155SRico Sonntag 10018add1155SRico Sonntag /** 10028add1155SRico Sonntag * Find the age of the youngest father. 10038add1155SRico Sonntag * 10048add1155SRico Sonntag * @param string $show_years 10058add1155SRico Sonntag * 10068add1155SRico Sonntag * @return string 10078add1155SRico Sonntag */ 10088add1155SRico Sonntag public function youngestFatherAge(string $show_years = ''): string 10098add1155SRico Sonntag { 10108add1155SRico Sonntag return $this->parentsQuery('age', 'ASC', 'M', (bool) $show_years); 10118add1155SRico Sonntag } 10128add1155SRico Sonntag 10138add1155SRico Sonntag /** 10148add1155SRico Sonntag * Find the oldest father. 10158add1155SRico Sonntag * 10168add1155SRico Sonntag * @return string 10178add1155SRico Sonntag */ 10188add1155SRico Sonntag public function oldestFather(): string 10198add1155SRico Sonntag { 10208add1155SRico Sonntag return $this->parentsQuery('full', 'DESC', 'M', false); 10218add1155SRico Sonntag } 10228add1155SRico Sonntag 10238add1155SRico Sonntag /** 10248add1155SRico Sonntag * Find the name of the oldest father. 10258add1155SRico Sonntag * 10268add1155SRico Sonntag * @return string 10278add1155SRico Sonntag */ 10288add1155SRico Sonntag public function oldestFatherName(): string 10298add1155SRico Sonntag { 10308add1155SRico Sonntag return $this->parentsQuery('name', 'DESC', 'M', false); 10318add1155SRico Sonntag } 10328add1155SRico Sonntag 10338add1155SRico Sonntag /** 10348add1155SRico Sonntag * Find the age of the oldest father. 10358add1155SRico Sonntag * 10368add1155SRico Sonntag * @param string $show_years 10378add1155SRico Sonntag * 10388add1155SRico Sonntag * @return string 10398add1155SRico Sonntag */ 10408add1155SRico Sonntag public function oldestFatherAge(string $show_years = ''): string 10418add1155SRico Sonntag { 10428add1155SRico Sonntag return $this->parentsQuery('age', 'DESC', 'M', (bool) $show_years); 10438add1155SRico Sonntag } 10448add1155SRico Sonntag 10458add1155SRico Sonntag /** 10468add1155SRico Sonntag * General query on age at marriage. 10478add1155SRico Sonntag * 10488add1155SRico Sonntag * @param string $type 10498add1155SRico Sonntag * @param string $age_dir 10508add1155SRico Sonntag * @param int $total 10518add1155SRico Sonntag * 10528add1155SRico Sonntag * @return string 10538add1155SRico Sonntag */ 10548add1155SRico Sonntag private function ageOfMarriageQuery(string $type, string $age_dir, int $total): string 10558add1155SRico Sonntag { 10568add1155SRico Sonntag if ($age_dir !== 'ASC') { 10578add1155SRico Sonntag $age_dir = 'DESC'; 10588add1155SRico Sonntag } 10598add1155SRico Sonntag 10608add1155SRico Sonntag $hrows = $this->runSql( 10618add1155SRico Sonntag " SELECT DISTINCT fam.f_id AS family, MIN(husbdeath.d_julianday2-married.d_julianday1) AS age" . 10628add1155SRico Sonntag " FROM `##families` AS fam" . 10638add1155SRico Sonntag " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" . 10648add1155SRico Sonntag " LEFT JOIN `##dates` AS husbdeath ON husbdeath.d_file = {$this->tree->id()}" . 10658add1155SRico Sonntag " WHERE" . 10668add1155SRico Sonntag " fam.f_file = {$this->tree->id()} AND" . 10678add1155SRico Sonntag " husbdeath.d_gid = fam.f_husb AND" . 10688add1155SRico Sonntag " husbdeath.d_fact = 'DEAT' AND" . 10698add1155SRico Sonntag " married.d_gid = fam.f_id AND" . 10708add1155SRico Sonntag " married.d_fact = 'MARR' AND" . 10718add1155SRico Sonntag " married.d_julianday1 < husbdeath.d_julianday2 AND" . 10728add1155SRico Sonntag " married.d_julianday1 <> 0" . 10738add1155SRico Sonntag " GROUP BY family" . 10748add1155SRico Sonntag " ORDER BY age {$age_dir}" 10758add1155SRico Sonntag ); 10768add1155SRico Sonntag 10778add1155SRico Sonntag $wrows = $this->runSql( 10788add1155SRico Sonntag " SELECT DISTINCT fam.f_id AS family, MIN(wifedeath.d_julianday2-married.d_julianday1) AS age" . 10798add1155SRico Sonntag " FROM `##families` AS fam" . 10808add1155SRico Sonntag " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" . 10818add1155SRico Sonntag " LEFT JOIN `##dates` AS wifedeath ON wifedeath.d_file = {$this->tree->id()}" . 10828add1155SRico Sonntag " WHERE" . 10838add1155SRico Sonntag " fam.f_file = {$this->tree->id()} AND" . 10848add1155SRico Sonntag " wifedeath.d_gid = fam.f_wife AND" . 10858add1155SRico Sonntag " wifedeath.d_fact = 'DEAT' AND" . 10868add1155SRico Sonntag " married.d_gid = fam.f_id AND" . 10878add1155SRico Sonntag " married.d_fact = 'MARR' AND" . 10888add1155SRico Sonntag " married.d_julianday1 < wifedeath.d_julianday2 AND" . 10898add1155SRico Sonntag " married.d_julianday1 <> 0" . 10908add1155SRico Sonntag " GROUP BY family" . 10918add1155SRico Sonntag " ORDER BY age {$age_dir}" 10928add1155SRico Sonntag ); 10938add1155SRico Sonntag 10948add1155SRico Sonntag $drows = $this->runSql( 10958add1155SRico Sonntag " SELECT DISTINCT fam.f_id AS family, MIN(divorced.d_julianday2-married.d_julianday1) AS age" . 10968add1155SRico Sonntag " FROM `##families` AS fam" . 10978add1155SRico Sonntag " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" . 10988add1155SRico Sonntag " LEFT JOIN `##dates` AS divorced ON divorced.d_file = {$this->tree->id()}" . 10998add1155SRico Sonntag " WHERE" . 11008add1155SRico Sonntag " fam.f_file = {$this->tree->id()} AND" . 11018add1155SRico Sonntag " married.d_gid = fam.f_id AND" . 11028add1155SRico Sonntag " married.d_fact = 'MARR' AND" . 11038add1155SRico Sonntag " divorced.d_gid = fam.f_id AND" . 11048add1155SRico Sonntag " divorced.d_fact IN ('DIV', 'ANUL', '_SEPR', '_DETS') AND" . 11058add1155SRico Sonntag " married.d_julianday1 < divorced.d_julianday2 AND" . 11068add1155SRico Sonntag " married.d_julianday1 <> 0" . 11078add1155SRico Sonntag " GROUP BY family" . 11088add1155SRico Sonntag " ORDER BY age {$age_dir}" 11098add1155SRico Sonntag ); 11108add1155SRico Sonntag 11118add1155SRico Sonntag $rows = []; 11128add1155SRico Sonntag foreach ($drows as $family) { 11138add1155SRico Sonntag $rows[$family->family] = $family->age; 11148add1155SRico Sonntag } 11158add1155SRico Sonntag 11168add1155SRico Sonntag foreach ($hrows as $family) { 11178add1155SRico Sonntag if (!isset($rows[$family->family])) { 11188add1155SRico Sonntag $rows[$family->family] = $family->age; 11198add1155SRico Sonntag } 11208add1155SRico Sonntag } 11218add1155SRico Sonntag 11228add1155SRico Sonntag foreach ($wrows as $family) { 11238add1155SRico Sonntag if (!isset($rows[$family->family])) { 11248add1155SRico Sonntag $rows[$family->family] = $family->age; 11258add1155SRico Sonntag } elseif ($rows[$family->family] > $family->age) { 11268add1155SRico Sonntag $rows[$family->family] = $family->age; 11278add1155SRico Sonntag } 11288add1155SRico Sonntag } 11298add1155SRico Sonntag 11308add1155SRico Sonntag if ($age_dir === 'DESC') { 11318add1155SRico Sonntag arsort($rows); 11328add1155SRico Sonntag } else { 11338add1155SRico Sonntag asort($rows); 11348add1155SRico Sonntag } 11358add1155SRico Sonntag 11368add1155SRico Sonntag $top10 = []; 11378add1155SRico Sonntag $i = 0; 11388add1155SRico Sonntag foreach ($rows as $fam => $age) { 11398add1155SRico Sonntag $family = Family::getInstance($fam, $this->tree); 11408add1155SRico Sonntag if ($type === 'name') { 11418add1155SRico Sonntag return $family->formatList(); 11428add1155SRico Sonntag } 11438add1155SRico Sonntag 11448add1155SRico Sonntag $age = $this->calculateAge((int) $age); 11458add1155SRico Sonntag 11468add1155SRico Sonntag if ($type === 'age') { 11478add1155SRico Sonntag return $age; 11488add1155SRico Sonntag } 11498add1155SRico Sonntag 115039ca88baSGreg Roach $husb = $family->husband(); 115139ca88baSGreg Roach $wife = $family->wife(); 11528add1155SRico Sonntag 11538add1155SRico Sonntag if (($husb && ($husb->getAllDeathDates() || !$husb->isDead())) 11548add1155SRico Sonntag && ($wife && ($wife->getAllDeathDates() || !$wife->isDead())) 11558add1155SRico Sonntag ) { 11568add1155SRico Sonntag if ($family && $family->canShow()) { 11578add1155SRico Sonntag if ($type === 'list') { 115839ca88baSGreg Roach $top10[] = '<li><a href="' . e($family->url()) . '">' . $family->fullName() . '</a> (' . $age . ')' . '</li>'; 11598add1155SRico Sonntag } else { 116039ca88baSGreg Roach $top10[] = '<a href="' . e($family->url()) . '">' . $family->fullName() . '</a> (' . $age . ')'; 11618add1155SRico Sonntag } 11628add1155SRico Sonntag } 11638add1155SRico Sonntag if (++$i === $total) { 11648add1155SRico Sonntag break; 11658add1155SRico Sonntag } 11668add1155SRico Sonntag } 11678add1155SRico Sonntag } 11688add1155SRico Sonntag 11698add1155SRico Sonntag if ($type === 'list') { 11708add1155SRico Sonntag $top10 = implode('', $top10); 11718add1155SRico Sonntag } else { 11728add1155SRico Sonntag $top10 = implode('; ', $top10); 11738add1155SRico Sonntag } 11748add1155SRico Sonntag 11758add1155SRico Sonntag if (I18N::direction() === 'rtl') { 11768add1155SRico Sonntag $top10 = str_replace([ 11778add1155SRico Sonntag '[', 11788add1155SRico Sonntag ']', 11798add1155SRico Sonntag '(', 11808add1155SRico Sonntag ')', 11818add1155SRico Sonntag '+', 11828add1155SRico Sonntag ], [ 11838add1155SRico Sonntag '‏[', 11848add1155SRico Sonntag '‏]', 11858add1155SRico Sonntag '‏(', 11868add1155SRico Sonntag '‏)', 11878add1155SRico Sonntag '‏+', 11888add1155SRico Sonntag ], $top10); 11898add1155SRico Sonntag } 11908add1155SRico Sonntag 11918add1155SRico Sonntag if ($type === 'list') { 11928add1155SRico Sonntag return '<ul>' . $top10 . '</ul>'; 11938add1155SRico Sonntag } 11948add1155SRico Sonntag 11958add1155SRico Sonntag return $top10; 11968add1155SRico Sonntag } 11978add1155SRico Sonntag 11988add1155SRico Sonntag /** 11998add1155SRico Sonntag * General query on marriage ages. 12008add1155SRico Sonntag * 12018add1155SRico Sonntag * @return string 12028add1155SRico Sonntag */ 12038add1155SRico Sonntag public function topAgeOfMarriageFamily(): string 12048add1155SRico Sonntag { 12058add1155SRico Sonntag return $this->ageOfMarriageQuery('name', 'DESC', 1); 12068add1155SRico Sonntag } 12078add1155SRico Sonntag 12088add1155SRico Sonntag /** 12098add1155SRico Sonntag * General query on marriage ages. 12108add1155SRico Sonntag * 12118add1155SRico Sonntag * @return string 12128add1155SRico Sonntag */ 12138add1155SRico Sonntag public function topAgeOfMarriage(): string 12148add1155SRico Sonntag { 12158add1155SRico Sonntag return $this->ageOfMarriageQuery('age', 'DESC', 1); 12168add1155SRico Sonntag } 12178add1155SRico Sonntag 12188add1155SRico Sonntag /** 12198add1155SRico Sonntag * General query on marriage ages. 12208add1155SRico Sonntag * 12218add1155SRico Sonntag * @param int $total 12228add1155SRico Sonntag * 12238add1155SRico Sonntag * @return string 12248add1155SRico Sonntag */ 12258add1155SRico Sonntag public function topAgeOfMarriageFamilies(int $total = 10): string 12268add1155SRico Sonntag { 12278add1155SRico Sonntag return $this->ageOfMarriageQuery('nolist', 'DESC', $total); 12288add1155SRico Sonntag } 12298add1155SRico Sonntag 12308add1155SRico Sonntag /** 12318add1155SRico Sonntag * General query on marriage ages. 12328add1155SRico Sonntag * 12338add1155SRico Sonntag * @param int $total 12348add1155SRico Sonntag * 12358add1155SRico Sonntag * @return string 12368add1155SRico Sonntag */ 12378add1155SRico Sonntag public function topAgeOfMarriageFamiliesList(int $total = 10): string 12388add1155SRico Sonntag { 12398add1155SRico Sonntag return $this->ageOfMarriageQuery('list', 'DESC', $total); 12408add1155SRico Sonntag } 12418add1155SRico Sonntag 12428add1155SRico Sonntag /** 12438add1155SRico Sonntag * General query on marriage ages. 12448add1155SRico Sonntag * 12458add1155SRico Sonntag * @return string 12468add1155SRico Sonntag */ 12478add1155SRico Sonntag public function minAgeOfMarriageFamily(): string 12488add1155SRico Sonntag { 12498add1155SRico Sonntag return $this->ageOfMarriageQuery('name', 'ASC', 1); 12508add1155SRico Sonntag } 12518add1155SRico Sonntag 12528add1155SRico Sonntag /** 12538add1155SRico Sonntag * General query on marriage ages. 12548add1155SRico Sonntag * 12558add1155SRico Sonntag * @return string 12568add1155SRico Sonntag */ 12578add1155SRico Sonntag public function minAgeOfMarriage(): string 12588add1155SRico Sonntag { 12598add1155SRico Sonntag return $this->ageOfMarriageQuery('age', 'ASC', 1); 12608add1155SRico Sonntag } 12618add1155SRico Sonntag 12628add1155SRico Sonntag /** 12638add1155SRico Sonntag * General query on marriage ages. 12648add1155SRico Sonntag * 12658add1155SRico Sonntag * @param int $total 12668add1155SRico Sonntag * 12678add1155SRico Sonntag * @return string 12688add1155SRico Sonntag */ 12698add1155SRico Sonntag public function minAgeOfMarriageFamilies(int $total = 10): string 12708add1155SRico Sonntag { 12718add1155SRico Sonntag return $this->ageOfMarriageQuery('nolist', 'ASC', $total); 12728add1155SRico Sonntag } 12738add1155SRico Sonntag 12748add1155SRico Sonntag /** 12758add1155SRico Sonntag * General query on marriage ages. 12768add1155SRico Sonntag * 12778add1155SRico Sonntag * @param int $total 12788add1155SRico Sonntag * 12798add1155SRico Sonntag * @return string 12808add1155SRico Sonntag */ 12818add1155SRico Sonntag public function minAgeOfMarriageFamiliesList(int $total = 10): string 12828add1155SRico Sonntag { 12838add1155SRico Sonntag return $this->ageOfMarriageQuery('list', 'ASC', $total); 12848add1155SRico Sonntag } 12858add1155SRico Sonntag 12868add1155SRico Sonntag /** 12878add1155SRico Sonntag * Find the ages between spouses. 12888add1155SRico Sonntag * 12898add1155SRico Sonntag * @param string $age_dir 12908add1155SRico Sonntag * @param int $total 12918add1155SRico Sonntag * 12928add1155SRico Sonntag * @return array 12938add1155SRico Sonntag */ 12948add1155SRico Sonntag private function ageBetweenSpousesQuery(string $age_dir, int $total): array 12958add1155SRico Sonntag { 1296d1a467e4SGreg Roach $prefix = DB::connection()->getTablePrefix(); 1297d1a467e4SGreg Roach 1298d1a467e4SGreg Roach $query = DB::table('families') 1299d1a467e4SGreg Roach ->where('f_file', '=', $this->tree->id()) 1300d1a467e4SGreg Roach ->join('dates AS wife', function (JoinClause $join): void { 1301d1a467e4SGreg Roach $join 1302d1a467e4SGreg Roach ->on('wife.d_gid', '=', 'f_wife') 1303d1a467e4SGreg Roach ->on('wife.d_file', '=', 'f_file') 1304d1a467e4SGreg Roach ->where('wife.d_fact', '=', 'BIRT') 1305d1a467e4SGreg Roach ->where('wife.d_julianday1', '<>', 0); 1306d1a467e4SGreg Roach }) 1307d1a467e4SGreg Roach ->join('dates AS husb', function (JoinClause $join): void { 1308d1a467e4SGreg Roach $join 1309d1a467e4SGreg Roach ->on('husb.d_gid', '=', 'f_husb') 1310d1a467e4SGreg Roach ->on('husb.d_file', '=', 'f_file') 1311d1a467e4SGreg Roach ->where('husb.d_fact', '=', 'BIRT') 1312d1a467e4SGreg Roach ->where('husb.d_julianday1', '<>', 0); 1313d1a467e4SGreg Roach }); 1314d1a467e4SGreg Roach 13158add1155SRico Sonntag if ($age_dir === 'DESC') { 1316d1a467e4SGreg Roach $query 1317d1a467e4SGreg Roach ->whereColumn('wife.d_julianday1', '>=', 'husb.d_julianday1') 1318d1a467e4SGreg Roach ->orderBy(DB::raw('MIN(' . $prefix . 'wife.d_julianday1) - MIN(' . $prefix . 'husb.d_julianday1)'), 'DESC'); 13198add1155SRico Sonntag } else { 1320d1a467e4SGreg Roach $query 1321d1a467e4SGreg Roach ->whereColumn('husb.d_julianday1', '>=', 'wife.d_julianday1') 1322d1a467e4SGreg Roach ->orderBy(DB::raw('MIN(' . $prefix . 'husb.d_julianday1) - MIN(' . $prefix . 'wife.d_julianday1)'), 'DESC'); 13238add1155SRico Sonntag } 13248add1155SRico Sonntag 1325d1a467e4SGreg Roach $families = $query 1326d1a467e4SGreg Roach ->groupBy(['f_id', 'f_file']) 1327d1a467e4SGreg Roach ->select('families.*') 1328d1a467e4SGreg Roach ->take($total) 1329d1a467e4SGreg Roach ->get() 1330d1a467e4SGreg Roach ->map(Family::rowMapper()) 1331d1a467e4SGreg Roach ->filter(GedcomRecord::accessFilter()); 13328add1155SRico Sonntag 13338add1155SRico Sonntag $top10 = []; 13348add1155SRico Sonntag 1335d1a467e4SGreg Roach /** @var Family $family */ 1336d1a467e4SGreg Roach foreach ($families as $family) { 1337d1a467e4SGreg Roach $husb_birt_jd = $family->husband()->getBirthDate()->minimumJulianDay(); 1338d1a467e4SGreg Roach $wife_birt_jd = $family->wife()->getBirthDate()->minimumJulianDay(); 13398add1155SRico Sonntag 1340d1a467e4SGreg Roach if ($age_dir === 'DESC') { 1341d1a467e4SGreg Roach $diff = $wife_birt_jd - $husb_birt_jd; 1342d1a467e4SGreg Roach } else { 1343d1a467e4SGreg Roach $diff = $husb_birt_jd - $wife_birt_jd; 13448add1155SRico Sonntag } 13458add1155SRico Sonntag 13468add1155SRico Sonntag $top10[] = [ 13478add1155SRico Sonntag 'family' => $family, 1348d1a467e4SGreg Roach 'age' => $this->calculateAge((int) $diff), 13498add1155SRico Sonntag ]; 13508add1155SRico Sonntag } 13518add1155SRico Sonntag 13528add1155SRico Sonntag return $top10; 13538add1155SRico Sonntag } 13548add1155SRico Sonntag 13558add1155SRico Sonntag /** 13568add1155SRico Sonntag * Find the age between husband and wife. 13578add1155SRico Sonntag * 13588add1155SRico Sonntag * @param int $total 13598add1155SRico Sonntag * 13608add1155SRico Sonntag * @return string 13618add1155SRico Sonntag */ 13628add1155SRico Sonntag public function ageBetweenSpousesMF(int $total = 10): string 13638add1155SRico Sonntag { 13648add1155SRico Sonntag $records = $this->ageBetweenSpousesQuery('DESC', $total); 13658add1155SRico Sonntag 13668add1155SRico Sonntag return view( 13678add1155SRico Sonntag 'statistics/families/top10-nolist-spouses', 13688add1155SRico Sonntag [ 13698add1155SRico Sonntag 'records' => $records, 13708add1155SRico Sonntag ] 13718add1155SRico Sonntag ); 13728add1155SRico Sonntag } 13738add1155SRico Sonntag 13748add1155SRico Sonntag /** 13758add1155SRico Sonntag * Find the age between husband and wife. 13768add1155SRico Sonntag * 13778add1155SRico Sonntag * @param int $total 13788add1155SRico Sonntag * 13798add1155SRico Sonntag * @return string 13808add1155SRico Sonntag */ 13818add1155SRico Sonntag public function ageBetweenSpousesMFList(int $total = 10): string 13828add1155SRico Sonntag { 13838add1155SRico Sonntag $records = $this->ageBetweenSpousesQuery('DESC', $total); 13848add1155SRico Sonntag 13858add1155SRico Sonntag return view( 13868add1155SRico Sonntag 'statistics/families/top10-list-spouses', 13878add1155SRico Sonntag [ 13888add1155SRico Sonntag 'records' => $records, 13898add1155SRico Sonntag ] 13908add1155SRico Sonntag ); 13918add1155SRico Sonntag } 13928add1155SRico Sonntag 13938add1155SRico Sonntag /** 13948add1155SRico Sonntag * Find the age between wife and husband.. 13958add1155SRico Sonntag * 13968add1155SRico Sonntag * @param int $total 13978add1155SRico Sonntag * 13988add1155SRico Sonntag * @return string 13998add1155SRico Sonntag */ 14008add1155SRico Sonntag public function ageBetweenSpousesFM(int $total = 10): string 14018add1155SRico Sonntag { 14028add1155SRico Sonntag $records = $this->ageBetweenSpousesQuery('ASC', $total); 14038add1155SRico Sonntag 14048add1155SRico Sonntag return view( 14058add1155SRico Sonntag 'statistics/families/top10-nolist-spouses', 14068add1155SRico Sonntag [ 14078add1155SRico Sonntag 'records' => $records, 14088add1155SRico Sonntag ] 14098add1155SRico Sonntag ); 14108add1155SRico Sonntag } 14118add1155SRico Sonntag 14128add1155SRico Sonntag /** 14138add1155SRico Sonntag * Find the age between wife and husband.. 14148add1155SRico Sonntag * 14158add1155SRico Sonntag * @param int $total 14168add1155SRico Sonntag * 14178add1155SRico Sonntag * @return string 14188add1155SRico Sonntag */ 14198add1155SRico Sonntag public function ageBetweenSpousesFMList(int $total = 10): string 14208add1155SRico Sonntag { 14218add1155SRico Sonntag $records = $this->ageBetweenSpousesQuery('ASC', $total); 14228add1155SRico Sonntag 14238add1155SRico Sonntag return view( 14248add1155SRico Sonntag 'statistics/families/top10-list-spouses', 14258add1155SRico Sonntag [ 14268add1155SRico Sonntag 'records' => $records, 14278add1155SRico Sonntag ] 14288add1155SRico Sonntag ); 14298add1155SRico Sonntag } 14308add1155SRico Sonntag 14318add1155SRico Sonntag /** 14328add1155SRico Sonntag * General query on ages at marriage. 14338add1155SRico Sonntag * 14348add1155SRico Sonntag * @param string $sex 14358add1155SRico Sonntag * @param int $year1 14368add1155SRico Sonntag * @param int $year2 14378add1155SRico Sonntag * 14388add1155SRico Sonntag * @return array 14398add1155SRico Sonntag */ 14408add1155SRico Sonntag public function statsMarrAgeQuery($sex = 'M', $year1 = -1, $year2 = -1): array 14418add1155SRico Sonntag { 14428add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 14438add1155SRico Sonntag $years = " married.d_year BETWEEN {$year1} AND {$year2} AND "; 14448add1155SRico Sonntag } else { 14458add1155SRico Sonntag $years = ''; 14468add1155SRico Sonntag } 14478add1155SRico Sonntag 14488add1155SRico Sonntag $rows = $this->runSql( 14498add1155SRico Sonntag "SELECT " . 14508add1155SRico Sonntag " fam.f_id, " . 14518add1155SRico Sonntag " birth.d_gid, " . 14528add1155SRico Sonntag " married.d_julianday2-birth.d_julianday1 AS age " . 14538add1155SRico Sonntag "FROM `##dates` AS married " . 14548add1155SRico Sonntag "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " . 14558add1155SRico Sonntag "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_husb AND birth.d_file=fam.f_file) " . 14568add1155SRico Sonntag "WHERE " . 14578add1155SRico Sonntag " '{$sex}' IN ('M', 'BOTH') AND {$years} " . 14588add1155SRico Sonntag " married.d_file={$this->tree->id()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " . 14598add1155SRico Sonntag " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " . 14608add1155SRico Sonntag " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " . 14618add1155SRico Sonntag "UNION ALL " . 14628add1155SRico Sonntag "SELECT " . 14638add1155SRico Sonntag " fam.f_id, " . 14648add1155SRico Sonntag " birth.d_gid, " . 14658add1155SRico Sonntag " married.d_julianday2-birth.d_julianday1 AS age " . 14668add1155SRico Sonntag "FROM `##dates` AS married " . 14678add1155SRico Sonntag "JOIN `##families` AS fam ON (married.d_gid=fam.f_id AND married.d_file=fam.f_file) " . 14688add1155SRico Sonntag "JOIN `##dates` AS birth ON (birth.d_gid=fam.f_wife AND birth.d_file=fam.f_file) " . 14698add1155SRico Sonntag "WHERE " . 14708add1155SRico Sonntag " '{$sex}' IN ('F', 'BOTH') AND {$years} " . 14718add1155SRico Sonntag " married.d_file={$this->tree->id()} AND married.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND married.d_fact='MARR' AND " . 14728add1155SRico Sonntag " birth.d_type IN ('@#DGREGORIAN@', '@#DJULIAN@') AND birth.d_fact='BIRT' AND " . 14738add1155SRico Sonntag " married.d_julianday1>birth.d_julianday1 AND birth.d_julianday1<>0 " 14748add1155SRico Sonntag ); 14758add1155SRico Sonntag 14768add1155SRico Sonntag foreach ($rows as $row) { 14778add1155SRico Sonntag $row->age = (int) $row->age; 14788add1155SRico Sonntag } 14798add1155SRico Sonntag 14808add1155SRico Sonntag return $rows; 14818add1155SRico Sonntag } 14828add1155SRico Sonntag 14838add1155SRico Sonntag /** 14848add1155SRico Sonntag * General query on marriage ages. 14858add1155SRico Sonntag * 14868add1155SRico Sonntag * @return string 14878add1155SRico Sonntag */ 148888de55fdSRico Sonntag public function statsMarrAge(): string 14898add1155SRico Sonntag { 14908add1155SRico Sonntag return (new ChartMarriageAge($this->tree)) 149188de55fdSRico Sonntag ->chartMarriageAge(); 14928add1155SRico Sonntag } 14938add1155SRico Sonntag 14948add1155SRico Sonntag /** 14958add1155SRico Sonntag * Query the database for marriage tags. 14968add1155SRico Sonntag * 14978add1155SRico Sonntag * @param string $type 14988add1155SRico Sonntag * @param string $age_dir 14998add1155SRico Sonntag * @param string $sex 15008add1155SRico Sonntag * @param bool $show_years 15018add1155SRico Sonntag * 15028add1155SRico Sonntag * @return string 15038add1155SRico Sonntag */ 15048add1155SRico Sonntag private function marriageQuery(string $type, string $age_dir, string $sex, bool $show_years): string 15058add1155SRico Sonntag { 15068add1155SRico Sonntag if ($sex === 'F') { 15078add1155SRico Sonntag $sex_field = 'f_wife'; 15088add1155SRico Sonntag } else { 15098add1155SRico Sonntag $sex_field = 'f_husb'; 15108add1155SRico Sonntag } 15118add1155SRico Sonntag 15128add1155SRico Sonntag if ($age_dir !== 'ASC') { 15138add1155SRico Sonntag $age_dir = 'DESC'; 15148add1155SRico Sonntag } 15158add1155SRico Sonntag 15168add1155SRico Sonntag $rows = $this->runSql( 15178add1155SRico Sonntag " SELECT fam.f_id AS famid, fam.{$sex_field}, married.d_julianday2-birth.d_julianday1 AS age, indi.i_id AS i_id" . 15188add1155SRico Sonntag " FROM `##families` AS fam" . 15198add1155SRico Sonntag " LEFT JOIN `##dates` AS birth ON birth.d_file = {$this->tree->id()}" . 15208add1155SRico Sonntag " LEFT JOIN `##dates` AS married ON married.d_file = {$this->tree->id()}" . 15218add1155SRico Sonntag " LEFT JOIN `##individuals` AS indi ON indi.i_file = {$this->tree->id()}" . 15228add1155SRico Sonntag " WHERE" . 15238add1155SRico Sonntag " birth.d_gid = indi.i_id AND" . 15248add1155SRico Sonntag " married.d_gid = fam.f_id AND" . 15258add1155SRico Sonntag " indi.i_id = fam.{$sex_field} AND" . 15268add1155SRico Sonntag " fam.f_file = {$this->tree->id()} AND" . 15278add1155SRico Sonntag " birth.d_fact = 'BIRT' AND" . 15288add1155SRico Sonntag " married.d_fact = 'MARR' AND" . 15298add1155SRico Sonntag " birth.d_julianday1 <> 0 AND" . 15308add1155SRico Sonntag " married.d_julianday2 > birth.d_julianday1 AND" . 15318add1155SRico Sonntag " i_sex='{$sex}'" . 15328add1155SRico Sonntag " ORDER BY" . 15338add1155SRico Sonntag " married.d_julianday2-birth.d_julianday1 {$age_dir} LIMIT 1" 15348add1155SRico Sonntag ); 15358add1155SRico Sonntag 15368add1155SRico Sonntag if (!isset($rows[0])) { 15378add1155SRico Sonntag return ''; 15388add1155SRico Sonntag } 15398add1155SRico Sonntag 15408add1155SRico Sonntag $row = $rows[0]; 15418add1155SRico Sonntag if (isset($row->famid)) { 15428add1155SRico Sonntag $family = Family::getInstance($row->famid, $this->tree); 15438add1155SRico Sonntag } 15448add1155SRico Sonntag 15458add1155SRico Sonntag if (isset($row->i_id)) { 15468add1155SRico Sonntag $person = Individual::getInstance($row->i_id, $this->tree); 15478add1155SRico Sonntag } 15488add1155SRico Sonntag 15498add1155SRico Sonntag switch ($type) { 15508add1155SRico Sonntag default: 15518add1155SRico Sonntag case 'full': 15528add1155SRico Sonntag if ($family && $family->canShow()) { 15538add1155SRico Sonntag $result = $family->formatList(); 15548add1155SRico Sonntag } else { 15558add1155SRico Sonntag $result = I18N::translate('This information is private and cannot be shown.'); 15568add1155SRico Sonntag } 15578add1155SRico Sonntag break; 15588add1155SRico Sonntag 15598add1155SRico Sonntag case 'name': 156039ca88baSGreg Roach $result = '<a href="' . e($family->url()) . '">' . $person->fullName() . '</a>'; 15618add1155SRico Sonntag break; 15628add1155SRico Sonntag 15638add1155SRico Sonntag case 'age': 15648add1155SRico Sonntag $age = $row->age; 15658add1155SRico Sonntag 15668add1155SRico Sonntag if ($show_years) { 15678add1155SRico Sonntag $result = $this->calculateAge((int) $row->age); 15688add1155SRico Sonntag } else { 15698add1155SRico Sonntag $result = I18N::number((int) ($age / 365.25)); 15708add1155SRico Sonntag } 15718add1155SRico Sonntag 15728add1155SRico Sonntag break; 15738add1155SRico Sonntag } 15748add1155SRico Sonntag 15758add1155SRico Sonntag return $result; 15768add1155SRico Sonntag } 15778add1155SRico Sonntag 15788add1155SRico Sonntag /** 15798add1155SRico Sonntag * Find the youngest wife. 15808add1155SRico Sonntag * 15818add1155SRico Sonntag * @return string 15828add1155SRico Sonntag */ 15838add1155SRico Sonntag public function youngestMarriageFemale(): string 15848add1155SRico Sonntag { 15858add1155SRico Sonntag return $this->marriageQuery('full', 'ASC', 'F', false); 15868add1155SRico Sonntag } 15878add1155SRico Sonntag 15888add1155SRico Sonntag /** 15898add1155SRico Sonntag * Find the name of the youngest wife. 15908add1155SRico Sonntag * 15918add1155SRico Sonntag * @return string 15928add1155SRico Sonntag */ 15938add1155SRico Sonntag public function youngestMarriageFemaleName(): string 15948add1155SRico Sonntag { 15958add1155SRico Sonntag return $this->marriageQuery('name', 'ASC', 'F', false); 15968add1155SRico Sonntag } 15978add1155SRico Sonntag 15988add1155SRico Sonntag /** 15998add1155SRico Sonntag * Find the age of the youngest wife. 16008add1155SRico Sonntag * 16018add1155SRico Sonntag * @param string $show_years 16028add1155SRico Sonntag * 16038add1155SRico Sonntag * @return string 16048add1155SRico Sonntag */ 16058add1155SRico Sonntag public function youngestMarriageFemaleAge(string $show_years = ''): string 16068add1155SRico Sonntag { 16078add1155SRico Sonntag return $this->marriageQuery('age', 'ASC', 'F', (bool) $show_years); 16088add1155SRico Sonntag } 16098add1155SRico Sonntag 16108add1155SRico Sonntag /** 16118add1155SRico Sonntag * Find the oldest wife. 16128add1155SRico Sonntag * 16138add1155SRico Sonntag * @return string 16148add1155SRico Sonntag */ 16158add1155SRico Sonntag public function oldestMarriageFemale(): string 16168add1155SRico Sonntag { 16178add1155SRico Sonntag return $this->marriageQuery('full', 'DESC', 'F', false); 16188add1155SRico Sonntag } 16198add1155SRico Sonntag 16208add1155SRico Sonntag /** 16218add1155SRico Sonntag * Find the name of the oldest wife. 16228add1155SRico Sonntag * 16238add1155SRico Sonntag * @return string 16248add1155SRico Sonntag */ 16258add1155SRico Sonntag public function oldestMarriageFemaleName(): string 16268add1155SRico Sonntag { 16278add1155SRico Sonntag return $this->marriageQuery('name', 'DESC', 'F', false); 16288add1155SRico Sonntag } 16298add1155SRico Sonntag 16308add1155SRico Sonntag /** 16318add1155SRico Sonntag * Find the age of the oldest wife. 16328add1155SRico Sonntag * 16338add1155SRico Sonntag * @param string $show_years 16348add1155SRico Sonntag * 16358add1155SRico Sonntag * @return string 16368add1155SRico Sonntag */ 16378add1155SRico Sonntag public function oldestMarriageFemaleAge(string $show_years = ''): string 16388add1155SRico Sonntag { 16398add1155SRico Sonntag return $this->marriageQuery('age', 'DESC', 'F', (bool) $show_years); 16408add1155SRico Sonntag } 16418add1155SRico Sonntag 16428add1155SRico Sonntag /** 16438add1155SRico Sonntag * Find the youngest husband. 16448add1155SRico Sonntag * 16458add1155SRico Sonntag * @return string 16468add1155SRico Sonntag */ 16478add1155SRico Sonntag public function youngestMarriageMale(): string 16488add1155SRico Sonntag { 16498add1155SRico Sonntag return $this->marriageQuery('full', 'ASC', 'M', false); 16508add1155SRico Sonntag } 16518add1155SRico Sonntag 16528add1155SRico Sonntag /** 16538add1155SRico Sonntag * Find the name of the youngest husband. 16548add1155SRico Sonntag * 16558add1155SRico Sonntag * @return string 16568add1155SRico Sonntag */ 16578add1155SRico Sonntag public function youngestMarriageMaleName(): string 16588add1155SRico Sonntag { 16598add1155SRico Sonntag return $this->marriageQuery('name', 'ASC', 'M', false); 16608add1155SRico Sonntag } 16618add1155SRico Sonntag 16628add1155SRico Sonntag /** 16638add1155SRico Sonntag * Find the age of the youngest husband. 16648add1155SRico Sonntag * 16658add1155SRico Sonntag * @param string $show_years 16668add1155SRico Sonntag * 16678add1155SRico Sonntag * @return string 16688add1155SRico Sonntag */ 16698add1155SRico Sonntag public function youngestMarriageMaleAge(string $show_years = ''): string 16708add1155SRico Sonntag { 16718add1155SRico Sonntag return $this->marriageQuery('age', 'ASC', 'M', (bool) $show_years); 16728add1155SRico Sonntag } 16738add1155SRico Sonntag 16748add1155SRico Sonntag /** 16758add1155SRico Sonntag * Find the oldest husband. 16768add1155SRico Sonntag * 16778add1155SRico Sonntag * @return string 16788add1155SRico Sonntag */ 16798add1155SRico Sonntag public function oldestMarriageMale(): string 16808add1155SRico Sonntag { 16818add1155SRico Sonntag return $this->marriageQuery('full', 'DESC', 'M', false); 16828add1155SRico Sonntag } 16838add1155SRico Sonntag 16848add1155SRico Sonntag /** 16858add1155SRico Sonntag * Find the name of the oldest husband. 16868add1155SRico Sonntag * 16878add1155SRico Sonntag * @return string 16888add1155SRico Sonntag */ 16898add1155SRico Sonntag public function oldestMarriageMaleName(): string 16908add1155SRico Sonntag { 16918add1155SRico Sonntag return $this->marriageQuery('name', 'DESC', 'M', false); 16928add1155SRico Sonntag } 16938add1155SRico Sonntag 16948add1155SRico Sonntag /** 16958add1155SRico Sonntag * Find the age of the oldest husband. 16968add1155SRico Sonntag * 16978add1155SRico Sonntag * @param string $show_years 16988add1155SRico Sonntag * 16998add1155SRico Sonntag * @return string 17008add1155SRico Sonntag */ 17018add1155SRico Sonntag public function oldestMarriageMaleAge(string $show_years = ''): string 17028add1155SRico Sonntag { 17038add1155SRico Sonntag return $this->marriageQuery('age', 'DESC', 'M', (bool) $show_years); 17048add1155SRico Sonntag } 17058add1155SRico Sonntag 17068add1155SRico Sonntag /** 17078add1155SRico Sonntag * General query on marriages. 17088add1155SRico Sonntag * 1709*2d7289dcSGreg Roach * @param bool $first_marriage 17108add1155SRico Sonntag * @param int $year1 17118add1155SRico Sonntag * @param int $year2 17128add1155SRico Sonntag * 17138add1155SRico Sonntag * @return array 17148add1155SRico Sonntag */ 1715*2d7289dcSGreg Roach public function statsMarrQuery(bool $first_marriage = false, int $year1 = -1, int $year2 = -1): array 17168add1155SRico Sonntag { 1717*2d7289dcSGreg Roach if ($first_marriage) { 1718*2d7289dcSGreg Roach $query = DB::table('families') 1719*2d7289dcSGreg Roach ->join('dates', function (JoinClause $join): void { 1720*2d7289dcSGreg Roach $join 1721*2d7289dcSGreg Roach ->on('d_gid', '=', 'f_id') 1722*2d7289dcSGreg Roach ->on('d_file', '=', 'f_file') 1723*2d7289dcSGreg Roach ->where('d_fact', '=', 'MARR') 1724*2d7289dcSGreg Roach ->where('d_julianday2', '<>', 0); 1725*2d7289dcSGreg Roach })->join('individuals', function (JoinClause $join): void { 1726*2d7289dcSGreg Roach $join 1727*2d7289dcSGreg Roach ->on('i_file', '=', 'f_file'); 1728*2d7289dcSGreg Roach }) 1729*2d7289dcSGreg Roach ->where('f_file', '=', $this->tree->id()) 1730*2d7289dcSGreg Roach ->where(function (Builder $query): void { 1731*2d7289dcSGreg Roach $query 1732*2d7289dcSGreg Roach ->whereColumn('i_id', '=', 'f_husb') 1733*2d7289dcSGreg Roach ->orWhereColumn('i_id', '=', 'f_wife'); 1734*2d7289dcSGreg Roach }); 17358add1155SRico Sonntag 17368add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 1737*2d7289dcSGreg Roach $query->whereBetween('d_year', [$year1, $year2]); 17388add1155SRico Sonntag } 17398add1155SRico Sonntag 1740*2d7289dcSGreg Roach return $query 1741*2d7289dcSGreg Roach ->select(['f_id AS fams', 'f_husb', 'f_wife', 'd_julianday2 AS age', 'd_month AS month', 'i_id AS indi']) 1742*2d7289dcSGreg Roach ->orderBy('f_id') 1743*2d7289dcSGreg Roach ->orderBy('i_id') 1744*2d7289dcSGreg Roach ->orderBy('d_julianday2') 1745*2d7289dcSGreg Roach ->get() 1746*2d7289dcSGreg Roach ->all(); 17478add1155SRico Sonntag } else { 1748*2d7289dcSGreg Roach $query = DB::table('dates') 1749*2d7289dcSGreg Roach ->where('d_file', '=', $this->tree->id()) 1750*2d7289dcSGreg Roach ->where('d_fact', '=', 'MARR') 1751*2d7289dcSGreg Roach ->select(['d_month', DB::raw('COUNT(*) AS total')]) 1752*2d7289dcSGreg Roach ->groupBy('d_month'); 17538add1155SRico Sonntag 17548add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 1755*2d7289dcSGreg Roach $query->whereBetween('d_year', [$year1, $year2]); 17568add1155SRico Sonntag } 17578add1155SRico Sonntag 1758*2d7289dcSGreg Roach return $query 1759*2d7289dcSGreg Roach ->get() 1760*2d7289dcSGreg Roach ->all(); 17618add1155SRico Sonntag } 17628add1155SRico Sonntag } 17638add1155SRico Sonntag 17648add1155SRico Sonntag /** 17658add1155SRico Sonntag * General query on marriages. 17668add1155SRico Sonntag * 17678add1155SRico Sonntag * @param string|null $color_from 17688add1155SRico Sonntag * @param string|null $color_to 17698add1155SRico Sonntag * 17708add1155SRico Sonntag * @return string 17718add1155SRico Sonntag */ 177288de55fdSRico Sonntag public function statsMarr(string $color_from = null, string $color_to = null): string 17738add1155SRico Sonntag { 17748add1155SRico Sonntag return (new ChartMarriage($this->tree)) 177588de55fdSRico Sonntag ->chartMarriage($color_from, $color_to); 17768add1155SRico Sonntag } 17778add1155SRico Sonntag 17788add1155SRico Sonntag /** 17798add1155SRico Sonntag * General divorce query. 17808add1155SRico Sonntag * 17818add1155SRico Sonntag * @param string|null $color_from 17828add1155SRico Sonntag * @param string|null $color_to 17838add1155SRico Sonntag * 17848add1155SRico Sonntag * @return string 17858add1155SRico Sonntag */ 178688de55fdSRico Sonntag public function statsDiv(string $color_from = null, string $color_to = null): string 17878add1155SRico Sonntag { 17888add1155SRico Sonntag return (new ChartDivorce($this->tree)) 178988de55fdSRico Sonntag ->chartDivorce($color_from, $color_to); 17908add1155SRico Sonntag } 17918add1155SRico Sonntag} 1792