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 20*269fd10dSGreg Roachuse Carbon\Carbon; 218add1155SRico Sonntaguse Fisharebest\Webtrees\Auth; 228add1155SRico Sonntaguse Fisharebest\Webtrees\Functions\FunctionsDate; 238add1155SRico Sonntaguse Fisharebest\Webtrees\Functions\FunctionsPrintLists; 248add1155SRico Sonntaguse Fisharebest\Webtrees\Gedcom; 25d1a467e4SGreg Roachuse Fisharebest\Webtrees\GedcomRecord; 268add1155SRico Sonntaguse Fisharebest\Webtrees\I18N; 278add1155SRico Sonntaguse Fisharebest\Webtrees\Individual; 2867992b6aSRichard Cisseeuse Fisharebest\Webtrees\Module\IndividualListModule; 2967992b6aSRichard Cisseeuse Fisharebest\Webtrees\Module\ModuleInterface; 3087cca37cSGreg Roachuse Fisharebest\Webtrees\Module\ModuleListInterface; 3167992b6aSRichard Cisseeuse Fisharebest\Webtrees\Services\ModuleService; 328add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartAge; 338add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartBirth; 348add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartCommonGiven; 358add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartCommonSurname; 368add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartDeath; 378add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartFamilyWithSources; 3888de55fdSRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartIndividualWithSources; 398add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartMortality; 408add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Google\ChartSex; 418add1155SRico Sonntaguse Fisharebest\Webtrees\Statistics\Repository\Interfaces\IndividualRepositoryInterface; 428add1155SRico Sonntaguse Fisharebest\Webtrees\Tree; 438add1155SRico Sonntaguse Illuminate\Database\Capsule\Manager as DB; 443dc8167dSGreg Roachuse Illuminate\Database\Query\Builder; 458add1155SRico Sonntaguse Illuminate\Database\Query\JoinClause; 468add1155SRico Sonntag 478add1155SRico Sonntag/** 488add1155SRico Sonntag * 498add1155SRico Sonntag */ 508add1155SRico Sonntagclass IndividualRepository implements IndividualRepositoryInterface 518add1155SRico Sonntag{ 528add1155SRico Sonntag /** 538add1155SRico Sonntag * @var Tree 548add1155SRico Sonntag */ 558add1155SRico Sonntag private $tree; 568add1155SRico Sonntag 578add1155SRico Sonntag /** 588add1155SRico Sonntag * Constructor. 598add1155SRico Sonntag * 608add1155SRico Sonntag * @param Tree $tree 618add1155SRico Sonntag */ 628add1155SRico Sonntag public function __construct(Tree $tree) 638add1155SRico Sonntag { 648add1155SRico Sonntag $this->tree = $tree; 658add1155SRico Sonntag } 668add1155SRico Sonntag 678add1155SRico Sonntag /** 688add1155SRico Sonntag * Find common given names. 698add1155SRico Sonntag * 708add1155SRico Sonntag * @param string $sex 718add1155SRico Sonntag * @param string $type 728add1155SRico Sonntag * @param bool $show_tot 738add1155SRico Sonntag * @param int $threshold 748add1155SRico Sonntag * @param int $maxtoshow 758add1155SRico Sonntag * 768add1155SRico Sonntag * @return string|int[] 778add1155SRico Sonntag */ 788add1155SRico Sonntag private function commonGivenQuery(string $sex, string $type, bool $show_tot, int $threshold, int $maxtoshow) 798add1155SRico Sonntag { 80d1a467e4SGreg Roach $query = DB::table('name') 81d1a467e4SGreg Roach ->join('individuals', function (JoinClause $join): void { 82d1a467e4SGreg Roach $join 83d1a467e4SGreg Roach ->on('i_file', '=', 'n_file') 84d1a467e4SGreg Roach ->on('i_id', '=', 'n_id'); 85d1a467e4SGreg Roach }) 86d1a467e4SGreg Roach ->where('n_file', '=', $this->tree->id()) 87d1a467e4SGreg Roach ->where('n_type', '<>', '_MARNM') 88d1a467e4SGreg Roach ->where('n_givn', '<>', '@P.N.') 89d1a467e4SGreg Roach ->where(DB::raw('LENGTH(n_givn)'), '>', 1); 90d1a467e4SGreg Roach 918add1155SRico Sonntag switch ($sex) { 928add1155SRico Sonntag case 'M': 938add1155SRico Sonntag case 'F': 948add1155SRico Sonntag case 'U': 95d1a467e4SGreg Roach $query->where('i_sex', '=', $sex); 968add1155SRico Sonntag break; 97d1a467e4SGreg Roach 988add1155SRico Sonntag case 'B': 998add1155SRico Sonntag default: 100d1a467e4SGreg Roach $query->where('i_sex', '<>', 'U'); 1018add1155SRico Sonntag break; 1028add1155SRico Sonntag } 1038add1155SRico Sonntag 104d1a467e4SGreg Roach $rows = $query 1053413ec75SRico Sonntag ->groupBy(['n_givn']) 1063413ec75SRico Sonntag ->select(['n_givn', DB::raw('COUNT(distinct n_id) AS count')]) 107d1a467e4SGreg Roach ->pluck('count', 'n_givn'); 1088add1155SRico Sonntag 1098add1155SRico Sonntag $nameList = []; 1108add1155SRico Sonntag 111d1a467e4SGreg Roach foreach ($rows as $n_givn => $count) { 1128add1155SRico Sonntag // Split “John Thomas” into “John” and “Thomas” and count against both totals 113d1a467e4SGreg Roach foreach (explode(' ', $n_givn) as $given) { 1148add1155SRico Sonntag // Exclude initials and particles. 1158add1155SRico Sonntag if (!preg_match('/^([A-Z]|[a-z]{1,3})$/', $given)) { 1168add1155SRico Sonntag if (\array_key_exists($given, $nameList)) { 117d1a467e4SGreg Roach $nameList[$given] += (int) $count; 1188add1155SRico Sonntag } else { 119d1a467e4SGreg Roach $nameList[$given] = (int) $count; 1208add1155SRico Sonntag } 1218add1155SRico Sonntag } 1228add1155SRico Sonntag } 1238add1155SRico Sonntag } 1248add1155SRico Sonntag arsort($nameList); 1258add1155SRico Sonntag $nameList = \array_slice($nameList, 0, $maxtoshow); 1268add1155SRico Sonntag 1278add1155SRico Sonntag foreach ($nameList as $given => $total) { 1288add1155SRico Sonntag if ($total < $threshold) { 1298add1155SRico Sonntag unset($nameList[$given]); 1308add1155SRico Sonntag } 1318add1155SRico Sonntag } 1328add1155SRico Sonntag 1338add1155SRico Sonntag switch ($type) { 1348add1155SRico Sonntag case 'chart': 1358add1155SRico Sonntag return $nameList; 1368add1155SRico Sonntag 1378add1155SRico Sonntag case 'table': 1388add1155SRico Sonntag return view('lists/given-names-table', [ 1398add1155SRico Sonntag 'given_names' => $nameList, 1408add1155SRico Sonntag ]); 1418add1155SRico Sonntag 1428add1155SRico Sonntag case 'list': 1438add1155SRico Sonntag return view('lists/given-names-list', [ 1448add1155SRico Sonntag 'given_names' => $nameList, 1458add1155SRico Sonntag 'show_totals' => $show_tot, 1468add1155SRico Sonntag ]); 1478add1155SRico Sonntag 1488add1155SRico Sonntag case 'nolist': 1498add1155SRico Sonntag default: 1508add1155SRico Sonntag array_walk($nameList, function (int &$value, string $key) use ($show_tot): void { 1518add1155SRico Sonntag if ($show_tot) { 1528add1155SRico Sonntag $value = '<span dir="auto">' . e($key); 1538add1155SRico Sonntag } else { 1548add1155SRico Sonntag $value = '<span dir="auto">' . e($key) . ' (' . I18N::number($value) . ')'; 1558add1155SRico Sonntag } 1568add1155SRico Sonntag }); 1578add1155SRico Sonntag 1588add1155SRico Sonntag return implode(I18N::$list_separator, $nameList); 1598add1155SRico Sonntag } 1608add1155SRico Sonntag } 1618add1155SRico Sonntag 1628add1155SRico Sonntag /** 1638add1155SRico Sonntag * Find common give names. 1648add1155SRico Sonntag * 1658add1155SRico Sonntag * @param int $threshold 1668add1155SRico Sonntag * @param int $maxtoshow 1678add1155SRico Sonntag * 1688add1155SRico Sonntag * @return string 1698add1155SRico Sonntag */ 1708add1155SRico Sonntag public function commonGiven(int $threshold = 1, int $maxtoshow = 10): string 1718add1155SRico Sonntag { 1728add1155SRico Sonntag return $this->commonGivenQuery('B', 'nolist', false, $threshold, $maxtoshow); 1738add1155SRico Sonntag } 1748add1155SRico Sonntag 1758add1155SRico Sonntag /** 1768add1155SRico Sonntag * Find common give names. 1778add1155SRico Sonntag * 1788add1155SRico Sonntag * @param int $threshold 1798add1155SRico Sonntag * @param int $maxtoshow 1808add1155SRico Sonntag * 1818add1155SRico Sonntag * @return string 1828add1155SRico Sonntag */ 1838add1155SRico Sonntag public function commonGivenTotals(int $threshold = 1, int $maxtoshow = 10): string 1848add1155SRico Sonntag { 1858add1155SRico Sonntag return $this->commonGivenQuery('B', 'nolist', true, $threshold, $maxtoshow); 1868add1155SRico Sonntag } 1878add1155SRico Sonntag 1888add1155SRico Sonntag /** 1898add1155SRico Sonntag * Find common give names. 1908add1155SRico Sonntag * 1918add1155SRico Sonntag * @param int $threshold 1928add1155SRico Sonntag * @param int $maxtoshow 1938add1155SRico Sonntag * 1948add1155SRico Sonntag * @return string 1958add1155SRico Sonntag */ 1968add1155SRico Sonntag public function commonGivenList(int $threshold = 1, int $maxtoshow = 10): string 1978add1155SRico Sonntag { 1988add1155SRico Sonntag return $this->commonGivenQuery('B', 'list', false, $threshold, $maxtoshow); 1998add1155SRico Sonntag } 2008add1155SRico Sonntag 2018add1155SRico Sonntag /** 2028add1155SRico Sonntag * Find common give names. 2038add1155SRico Sonntag * 2048add1155SRico Sonntag * @param int $threshold 2058add1155SRico Sonntag * @param int $maxtoshow 2068add1155SRico Sonntag * 2078add1155SRico Sonntag * @return string 2088add1155SRico Sonntag */ 2098add1155SRico Sonntag public function commonGivenListTotals(int $threshold = 1, int $maxtoshow = 10): string 2108add1155SRico Sonntag { 2118add1155SRico Sonntag return $this->commonGivenQuery('B', 'list', true, $threshold, $maxtoshow); 2128add1155SRico Sonntag } 2138add1155SRico Sonntag 2148add1155SRico Sonntag /** 2158add1155SRico Sonntag * Find common give names. 2168add1155SRico Sonntag * 2178add1155SRico Sonntag * @param int $threshold 2188add1155SRico Sonntag * @param int $maxtoshow 2198add1155SRico Sonntag * 2208add1155SRico Sonntag * @return string 2218add1155SRico Sonntag */ 2228add1155SRico Sonntag public function commonGivenTable(int $threshold = 1, int $maxtoshow = 10): string 2238add1155SRico Sonntag { 2248add1155SRico Sonntag return $this->commonGivenQuery('B', 'table', false, $threshold, $maxtoshow); 2258add1155SRico Sonntag } 2268add1155SRico Sonntag 2278add1155SRico Sonntag /** 2288add1155SRico Sonntag * Find common give names of females. 2298add1155SRico Sonntag * 2308add1155SRico Sonntag * @param int $threshold 2318add1155SRico Sonntag * @param int $maxtoshow 2328add1155SRico Sonntag * 2338add1155SRico Sonntag * @return string 2348add1155SRico Sonntag */ 2358add1155SRico Sonntag public function commonGivenFemale(int $threshold = 1, int $maxtoshow = 10): string 2368add1155SRico Sonntag { 2378add1155SRico Sonntag return $this->commonGivenQuery('F', 'nolist', false, $threshold, $maxtoshow); 2388add1155SRico Sonntag } 2398add1155SRico Sonntag 2408add1155SRico Sonntag /** 2418add1155SRico Sonntag * Find common give names of females. 2428add1155SRico Sonntag * 2438add1155SRico Sonntag * @param int $threshold 2448add1155SRico Sonntag * @param int $maxtoshow 2458add1155SRico Sonntag * 2468add1155SRico Sonntag * @return string 2478add1155SRico Sonntag */ 2488add1155SRico Sonntag public function commonGivenFemaleTotals(int $threshold = 1, int $maxtoshow = 10): string 2498add1155SRico Sonntag { 2508add1155SRico Sonntag return $this->commonGivenQuery('F', 'nolist', true, $threshold, $maxtoshow); 2518add1155SRico Sonntag } 2528add1155SRico Sonntag 2538add1155SRico Sonntag /** 2548add1155SRico Sonntag * Find common give names of females. 2558add1155SRico Sonntag * 2568add1155SRico Sonntag * @param int $threshold 2578add1155SRico Sonntag * @param int $maxtoshow 2588add1155SRico Sonntag * 2598add1155SRico Sonntag * @return string 2608add1155SRico Sonntag */ 2618add1155SRico Sonntag public function commonGivenFemaleList(int $threshold = 1, int $maxtoshow = 10): string 2628add1155SRico Sonntag { 2638add1155SRico Sonntag return $this->commonGivenQuery('F', 'list', false, $threshold, $maxtoshow); 2648add1155SRico Sonntag } 2658add1155SRico Sonntag 2668add1155SRico Sonntag /** 2678add1155SRico Sonntag * Find common give names of females. 2688add1155SRico Sonntag * 2698add1155SRico Sonntag * @param int $threshold 2708add1155SRico Sonntag * @param int $maxtoshow 2718add1155SRico Sonntag * 2728add1155SRico Sonntag * @return string 2738add1155SRico Sonntag */ 2748add1155SRico Sonntag public function commonGivenFemaleListTotals(int $threshold = 1, int $maxtoshow = 10): string 2758add1155SRico Sonntag { 2768add1155SRico Sonntag return $this->commonGivenQuery('F', 'list', true, $threshold, $maxtoshow); 2778add1155SRico Sonntag } 2788add1155SRico Sonntag 2798add1155SRico Sonntag /** 2808add1155SRico Sonntag * Find common give names of females. 2818add1155SRico Sonntag * 2828add1155SRico Sonntag * @param int $threshold 2838add1155SRico Sonntag * @param int $maxtoshow 2848add1155SRico Sonntag * 2858add1155SRico Sonntag * @return string 2868add1155SRico Sonntag */ 2878add1155SRico Sonntag public function commonGivenFemaleTable(int $threshold = 1, int $maxtoshow = 10): string 2888add1155SRico Sonntag { 2898add1155SRico Sonntag return $this->commonGivenQuery('F', 'table', false, $threshold, $maxtoshow); 2908add1155SRico Sonntag } 2918add1155SRico Sonntag 2928add1155SRico Sonntag /** 2938add1155SRico Sonntag * Find common give names of males. 2948add1155SRico Sonntag * 2958add1155SRico Sonntag * @param int $threshold 2968add1155SRico Sonntag * @param int $maxtoshow 2978add1155SRico Sonntag * 2988add1155SRico Sonntag * @return string 2998add1155SRico Sonntag */ 3008add1155SRico Sonntag public function commonGivenMale(int $threshold = 1, int $maxtoshow = 10): string 3018add1155SRico Sonntag { 3028add1155SRico Sonntag return $this->commonGivenQuery('M', 'nolist', false, $threshold, $maxtoshow); 3038add1155SRico Sonntag } 3048add1155SRico Sonntag 3058add1155SRico Sonntag /** 3068add1155SRico Sonntag * Find common give names of males. 3078add1155SRico Sonntag * 3088add1155SRico Sonntag * @param int $threshold 3098add1155SRico Sonntag * @param int $maxtoshow 3108add1155SRico Sonntag * 3118add1155SRico Sonntag * @return string 3128add1155SRico Sonntag */ 3138add1155SRico Sonntag public function commonGivenMaleTotals(int $threshold = 1, int $maxtoshow = 10): string 3148add1155SRico Sonntag { 3158add1155SRico Sonntag return $this->commonGivenQuery('M', 'nolist', true, $threshold, $maxtoshow); 3168add1155SRico Sonntag } 3178add1155SRico Sonntag 3188add1155SRico Sonntag /** 3198add1155SRico Sonntag * Find common give names of males. 3208add1155SRico Sonntag * 3218add1155SRico Sonntag * @param int $threshold 3228add1155SRico Sonntag * @param int $maxtoshow 3238add1155SRico Sonntag * 3248add1155SRico Sonntag * @return string 3258add1155SRico Sonntag */ 3268add1155SRico Sonntag public function commonGivenMaleList(int $threshold = 1, int $maxtoshow = 10): string 3278add1155SRico Sonntag { 3288add1155SRico Sonntag return $this->commonGivenQuery('M', 'list', false, $threshold, $maxtoshow); 3298add1155SRico Sonntag } 3308add1155SRico Sonntag 3318add1155SRico Sonntag /** 3328add1155SRico Sonntag * Find common give names of males. 3338add1155SRico Sonntag * 3348add1155SRico Sonntag * @param int $threshold 3358add1155SRico Sonntag * @param int $maxtoshow 3368add1155SRico Sonntag * 3378add1155SRico Sonntag * @return string 3388add1155SRico Sonntag */ 3398add1155SRico Sonntag public function commonGivenMaleListTotals(int $threshold = 1, int $maxtoshow = 10): string 3408add1155SRico Sonntag { 3418add1155SRico Sonntag return $this->commonGivenQuery('M', 'list', true, $threshold, $maxtoshow); 3428add1155SRico Sonntag } 3438add1155SRico Sonntag 3448add1155SRico Sonntag /** 3458add1155SRico Sonntag * Find common give names of males. 3468add1155SRico Sonntag * 3478add1155SRico Sonntag * @param int $threshold 3488add1155SRico Sonntag * @param int $maxtoshow 3498add1155SRico Sonntag * 3508add1155SRico Sonntag * @return string 3518add1155SRico Sonntag */ 3528add1155SRico Sonntag public function commonGivenMaleTable(int $threshold = 1, int $maxtoshow = 10): string 3538add1155SRico Sonntag { 3548add1155SRico Sonntag return $this->commonGivenQuery('M', 'table', false, $threshold, $maxtoshow); 3558add1155SRico Sonntag } 3568add1155SRico Sonntag 3578add1155SRico Sonntag /** 3588add1155SRico Sonntag * Find common give names of unknown sexes. 3598add1155SRico Sonntag * 3608add1155SRico Sonntag * @param int $threshold 3618add1155SRico Sonntag * @param int $maxtoshow 3628add1155SRico Sonntag * 3638add1155SRico Sonntag * @return string 3648add1155SRico Sonntag */ 3658add1155SRico Sonntag public function commonGivenUnknown(int $threshold = 1, int $maxtoshow = 10): string 3668add1155SRico Sonntag { 3678add1155SRico Sonntag return $this->commonGivenQuery('U', 'nolist', false, $threshold, $maxtoshow); 3688add1155SRico Sonntag } 3698add1155SRico Sonntag 3708add1155SRico Sonntag /** 3718add1155SRico Sonntag * Find common give names of unknown sexes. 3728add1155SRico Sonntag * 3738add1155SRico Sonntag * @param int $threshold 3748add1155SRico Sonntag * @param int $maxtoshow 3758add1155SRico Sonntag * 3768add1155SRico Sonntag * @return string 3778add1155SRico Sonntag */ 3788add1155SRico Sonntag public function commonGivenUnknownTotals(int $threshold = 1, int $maxtoshow = 10): string 3798add1155SRico Sonntag { 3808add1155SRico Sonntag return $this->commonGivenQuery('U', 'nolist', true, $threshold, $maxtoshow); 3818add1155SRico Sonntag } 3828add1155SRico Sonntag 3838add1155SRico Sonntag /** 3848add1155SRico Sonntag * Find common give names of unknown sexes. 3858add1155SRico Sonntag * 3868add1155SRico Sonntag * @param int $threshold 3878add1155SRico Sonntag * @param int $maxtoshow 3888add1155SRico Sonntag * 3898add1155SRico Sonntag * @return string 3908add1155SRico Sonntag */ 3918add1155SRico Sonntag public function commonGivenUnknownList(int $threshold = 1, int $maxtoshow = 10): string 3928add1155SRico Sonntag { 3938add1155SRico Sonntag return $this->commonGivenQuery('U', 'list', false, $threshold, $maxtoshow); 3948add1155SRico Sonntag } 3958add1155SRico Sonntag 3968add1155SRico Sonntag /** 3978add1155SRico Sonntag * Find common give names of unknown sexes. 3988add1155SRico Sonntag * 3998add1155SRico Sonntag * @param int $threshold 4008add1155SRico Sonntag * @param int $maxtoshow 4018add1155SRico Sonntag * 4028add1155SRico Sonntag * @return string 4038add1155SRico Sonntag */ 4048add1155SRico Sonntag public function commonGivenUnknownListTotals(int $threshold = 1, int $maxtoshow = 10): string 4058add1155SRico Sonntag { 4068add1155SRico Sonntag return $this->commonGivenQuery('U', 'list', true, $threshold, $maxtoshow); 4078add1155SRico Sonntag } 4088add1155SRico Sonntag 4098add1155SRico Sonntag /** 4108add1155SRico Sonntag * Find common give names of unknown sexes. 4118add1155SRico Sonntag * 4128add1155SRico Sonntag * @param int $threshold 4138add1155SRico Sonntag * @param int $maxtoshow 4148add1155SRico Sonntag * 4158add1155SRico Sonntag * @return string 4168add1155SRico Sonntag */ 4178add1155SRico Sonntag public function commonGivenUnknownTable(int $threshold = 1, int $maxtoshow = 10): string 4188add1155SRico Sonntag { 4198add1155SRico Sonntag return $this->commonGivenQuery('U', 'table', false, $threshold, $maxtoshow); 4208add1155SRico Sonntag } 4218add1155SRico Sonntag 4228add1155SRico Sonntag /** 4233dc8167dSGreg Roach * Count the number of distinct given names (or the number of occurences of specific given names). 4248add1155SRico Sonntag * 4253dc8167dSGreg Roach * @param string[] ...$params 4268add1155SRico Sonntag * 4278add1155SRico Sonntag * @return string 4288add1155SRico Sonntag */ 4298add1155SRico Sonntag public function totalGivennames(...$params): string 4308add1155SRico Sonntag { 4313dc8167dSGreg Roach $query = DB::table('name') 4323dc8167dSGreg Roach ->where('n_file', '=', $this->tree->id()); 4333dc8167dSGreg Roach 4343dc8167dSGreg Roach if (empty($params)) { 435c09e99bfSRico Sonntag // Count number of distinct given names. 4363dc8167dSGreg Roach $query 437c09e99bfSRico Sonntag ->distinct() 4382ccd1f0bSGreg Roach ->where('n_givn', '<>', '@P.N.') 439c09e99bfSRico Sonntag ->whereNotNull('n_givn'); 4408add1155SRico Sonntag } else { 441c09e99bfSRico Sonntag // Count number of occurences of specific given names. 44244cdc21eSGreg Roach $query->whereIn('n_givn', $params); 4438add1155SRico Sonntag } 4448add1155SRico Sonntag 445c09e99bfSRico Sonntag $count = $query->count('n_givn'); 4463dc8167dSGreg Roach 4473dc8167dSGreg Roach return I18N::number($count); 4488add1155SRico Sonntag } 4498add1155SRico Sonntag 4508add1155SRico Sonntag /** 451c09e99bfSRico Sonntag * Count the number of distinct surnames (or the number of occurences of specific surnames). 4528add1155SRico Sonntag * 4533dc8167dSGreg Roach * @param string[] ...$params 4548add1155SRico Sonntag * 4558add1155SRico Sonntag * @return string 4568add1155SRico Sonntag */ 4578add1155SRico Sonntag public function totalSurnames(...$params): string 4588add1155SRico Sonntag { 4593dc8167dSGreg Roach $query = DB::table('name') 4603dc8167dSGreg Roach ->where('n_file', '=', $this->tree->id()); 4613dc8167dSGreg Roach 4623dc8167dSGreg Roach if (empty($params)) { 4633dc8167dSGreg Roach // Count number of distinct surnames 464c09e99bfSRico Sonntag $query->distinct() 465c09e99bfSRico Sonntag ->whereNotNull('n_surn'); 4668add1155SRico Sonntag } else { 4673dc8167dSGreg Roach // Count number of occurences of specific surnames. 4683dc8167dSGreg Roach $query->whereIn('n_surn', $params); 4698add1155SRico Sonntag } 4708add1155SRico Sonntag 471c09e99bfSRico Sonntag $count = $query->count('n_surn'); 4728add1155SRico Sonntag 4733dc8167dSGreg Roach return I18N::number($count); 4748add1155SRico Sonntag } 4758add1155SRico Sonntag 4768add1155SRico Sonntag /** 4778add1155SRico Sonntag * @param int $number_of_surnames 4788add1155SRico Sonntag * @param int $threshold 4798add1155SRico Sonntag * 4808add1155SRico Sonntag * @return \stdClass[] 4818add1155SRico Sonntag */ 4828add1155SRico Sonntag private function topSurnames(int $number_of_surnames, int $threshold): array 4838add1155SRico Sonntag { 4848add1155SRico Sonntag // Use the count of base surnames. 485d1a467e4SGreg Roach $top_surnames = DB::table('name') 486d1a467e4SGreg Roach ->where('n_file', '=', $this->tree->id()) 487d1a467e4SGreg Roach ->where('n_type', '<>', '_MARNM') 488d1a467e4SGreg Roach ->whereNotIn('n_surn', ['', '@N.N.']) 489d1a467e4SGreg Roach ->select('n_surn') 490d1a467e4SGreg Roach ->groupBy('n_surn') 4913413ec75SRico Sonntag ->orderByRaw('count(n_surn) desc') 492d1a467e4SGreg Roach ->take($number_of_surnames) 493d1a467e4SGreg Roach ->get() 494d1a467e4SGreg Roach ->pluck('n_surn') 495d1a467e4SGreg Roach ->all(); 4968add1155SRico Sonntag 4978add1155SRico Sonntag $surnames = []; 4988add1155SRico Sonntag foreach ($top_surnames as $top_surname) { 499d1a467e4SGreg Roach $variants = DB::table('name') 500d1a467e4SGreg Roach ->where('n_file', '=', $this->tree->id()) 501d1a467e4SGreg Roach ->where(DB::raw('n_surn /* COLLATE ' . I18N::collation() . ' */'), '=', $top_surname) 502d1a467e4SGreg Roach ->select('n_surn', DB::raw('COUNT(*) AS count')) 503d1a467e4SGreg Roach ->groupBy('n_surn') 504d1a467e4SGreg Roach ->get() 505d1a467e4SGreg Roach ->pluck('count', 'n_surn') 506d1a467e4SGreg Roach ->all(); 5078add1155SRico Sonntag 5088add1155SRico Sonntag if (array_sum($variants) > $threshold) { 5098add1155SRico Sonntag $surnames[$top_surname] = $variants; 5108add1155SRico Sonntag } 5118add1155SRico Sonntag } 5128add1155SRico Sonntag 5138add1155SRico Sonntag return $surnames; 5148add1155SRico Sonntag } 5158add1155SRico Sonntag 5168add1155SRico Sonntag /** 5178add1155SRico Sonntag * Find common surnames. 5188add1155SRico Sonntag * 5198add1155SRico Sonntag * @return string 5208add1155SRico Sonntag */ 5218add1155SRico Sonntag public function getCommonSurname(): string 5228add1155SRico Sonntag { 5238add1155SRico Sonntag $top_surname = $this->topSurnames(1, 0); 524f24db0ceSRico Sonntag 525f24db0ceSRico Sonntag return $top_surname 526f24db0ceSRico Sonntag ? implode(', ', array_keys(array_shift($top_surname)) ?? []) 527f24db0ceSRico Sonntag : ''; 5288add1155SRico Sonntag } 5298add1155SRico Sonntag 5308add1155SRico Sonntag /** 5318add1155SRico Sonntag * Find common surnames. 5328add1155SRico Sonntag * 5338add1155SRico Sonntag * @param string $type 5348add1155SRico Sonntag * @param bool $show_tot 5358add1155SRico Sonntag * @param int $threshold 5368add1155SRico Sonntag * @param int $number_of_surnames 5378add1155SRico Sonntag * @param string $sorting 5388add1155SRico Sonntag * 5398add1155SRico Sonntag * @return string 5408add1155SRico Sonntag */ 5418add1155SRico Sonntag private function commonSurnamesQuery( 5428add1155SRico Sonntag string $type, 5438add1155SRico Sonntag bool $show_tot, 5448add1155SRico Sonntag int $threshold, 5458add1155SRico Sonntag int $number_of_surnames, 5468add1155SRico Sonntag string $sorting 5478add1155SRico Sonntag ): string { 5488add1155SRico Sonntag $surnames = $this->topSurnames($number_of_surnames, $threshold); 5498add1155SRico Sonntag 5508add1155SRico Sonntag switch ($sorting) { 5518add1155SRico Sonntag default: 5528add1155SRico Sonntag case 'alpha': 5538add1155SRico Sonntag uksort($surnames, [I18N::class, 'strcasecmp']); 5548add1155SRico Sonntag break; 5558add1155SRico Sonntag case 'count': 5568add1155SRico Sonntag break; 5578add1155SRico Sonntag case 'rcount': 5588add1155SRico Sonntag $surnames = array_reverse($surnames, true); 5598add1155SRico Sonntag break; 5608add1155SRico Sonntag } 5618add1155SRico Sonntag 56267992b6aSRichard Cissee //find a module providing individual lists 56387cca37cSGreg Roach $module = app(ModuleService::class)->findByComponent(ModuleListInterface::class, $this->tree, Auth::user())->first(function (ModuleInterface $module) { 56467992b6aSRichard Cissee return $module instanceof IndividualListModule; 56567992b6aSRichard Cissee }); 56667992b6aSRichard Cissee 5678add1155SRico Sonntag return FunctionsPrintLists::surnameList( 5688add1155SRico Sonntag $surnames, 5698add1155SRico Sonntag ($type === 'list' ? 1 : 2), 5708add1155SRico Sonntag $show_tot, 57167992b6aSRichard Cissee $module, 5728add1155SRico Sonntag $this->tree 5738add1155SRico Sonntag ); 5748add1155SRico Sonntag } 5758add1155SRico Sonntag 5768add1155SRico Sonntag /** 5778add1155SRico Sonntag * Find common surnames. 5788add1155SRico Sonntag * 5798add1155SRico Sonntag * @param int $threshold 5808add1155SRico Sonntag * @param int $number_of_surnames 5818add1155SRico Sonntag * @param string $sorting 5828add1155SRico Sonntag * 5838add1155SRico Sonntag * @return string 5848add1155SRico Sonntag */ 5858add1155SRico Sonntag public function commonSurnames( 5868add1155SRico Sonntag int $threshold = 1, 5878add1155SRico Sonntag int $number_of_surnames = 10, 5888add1155SRico Sonntag string $sorting = 'alpha' 5898add1155SRico Sonntag ): string { 5908add1155SRico Sonntag return $this->commonSurnamesQuery('nolist', false, $threshold, $number_of_surnames, $sorting); 5918add1155SRico Sonntag } 5928add1155SRico Sonntag 5938add1155SRico Sonntag /** 5948add1155SRico Sonntag * Find common surnames. 5958add1155SRico Sonntag * 5968add1155SRico Sonntag * @param int $threshold 5978add1155SRico Sonntag * @param int $number_of_surnames 5988add1155SRico Sonntag * @param string $sorting 5998add1155SRico Sonntag * 6008add1155SRico Sonntag * @return string 6018add1155SRico Sonntag */ 6028add1155SRico Sonntag public function commonSurnamesTotals( 6038add1155SRico Sonntag int $threshold = 1, 6048add1155SRico Sonntag int $number_of_surnames = 10, 6058add1155SRico Sonntag string $sorting = 'rcount' 6068add1155SRico Sonntag ): string { 6078add1155SRico Sonntag return $this->commonSurnamesQuery('nolist', true, $threshold, $number_of_surnames, $sorting); 6088add1155SRico Sonntag } 6098add1155SRico Sonntag 6108add1155SRico Sonntag /** 6118add1155SRico Sonntag * Find common surnames. 6128add1155SRico Sonntag * 6138add1155SRico Sonntag * @param int $threshold 6148add1155SRico Sonntag * @param int $number_of_surnames 6158add1155SRico Sonntag * @param string $sorting 6168add1155SRico Sonntag * 6178add1155SRico Sonntag * @return string 6188add1155SRico Sonntag */ 6198add1155SRico Sonntag public function commonSurnamesList( 6208add1155SRico Sonntag int $threshold = 1, 6218add1155SRico Sonntag int $number_of_surnames = 10, 6228add1155SRico Sonntag string $sorting = 'alpha' 6238add1155SRico Sonntag ): string { 6248add1155SRico Sonntag return $this->commonSurnamesQuery('list', false, $threshold, $number_of_surnames, $sorting); 6258add1155SRico Sonntag } 6268add1155SRico Sonntag 6278add1155SRico Sonntag /** 6288add1155SRico Sonntag * Find common surnames. 6298add1155SRico Sonntag * 6308add1155SRico Sonntag * @param int $threshold 6318add1155SRico Sonntag * @param int $number_of_surnames 6328add1155SRico Sonntag * @param string $sorting 6338add1155SRico Sonntag * 6348add1155SRico Sonntag * @return string 6358add1155SRico Sonntag */ 6368add1155SRico Sonntag public function commonSurnamesListTotals( 6378add1155SRico Sonntag int $threshold = 1, 6388add1155SRico Sonntag int $number_of_surnames = 10, 6398add1155SRico Sonntag string $sorting = 'rcount' 6408add1155SRico Sonntag ): string { 6418add1155SRico Sonntag return $this->commonSurnamesQuery('list', true, $threshold, $number_of_surnames, $sorting); 6428add1155SRico Sonntag } 6438add1155SRico Sonntag 6448add1155SRico Sonntag /** 645cde1d378SGreg Roach * Get a count of births by month. 6468add1155SRico Sonntag * 6478add1155SRico Sonntag * @param int $year1 6488add1155SRico Sonntag * @param int $year2 6498add1155SRico Sonntag * 650cde1d378SGreg Roach * @return Builder 6518add1155SRico Sonntag */ 652cde1d378SGreg Roach public function statsBirthQuery(int $year1 = -1, int $year2 = -1): Builder 6538add1155SRico Sonntag { 654d1a467e4SGreg Roach $query = DB::table('dates') 6553413ec75SRico Sonntag ->select(['d_month', DB::raw('COUNT(*) AS total')]) 656d1a467e4SGreg Roach ->where('d_file', '=', $this->tree->id()) 657d1a467e4SGreg Roach ->where('d_fact', '=', 'BIRT') 658d1a467e4SGreg Roach ->whereIn('d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 659d1a467e4SGreg Roach ->groupBy('d_month'); 6608add1155SRico Sonntag 6618add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 662d1a467e4SGreg Roach $query->whereBetween('d_year', [$year1, $year2]); 6638add1155SRico Sonntag } 6648add1155SRico Sonntag 665cde1d378SGreg Roach return $query; 666cde1d378SGreg Roach } 667cde1d378SGreg Roach 668cde1d378SGreg Roach /** 669cde1d378SGreg Roach * Get a count of births by month. 670cde1d378SGreg Roach * 671cde1d378SGreg Roach * @param int $year1 672cde1d378SGreg Roach * @param int $year2 673cde1d378SGreg Roach * 674cde1d378SGreg Roach * @return Builder 675cde1d378SGreg Roach */ 676cde1d378SGreg Roach public function statsBirthBySexQuery(int $year1 = -1, int $year2 = -1): Builder 677cde1d378SGreg Roach { 678cde1d378SGreg Roach return $this->statsBirthQuery($year1, $year2) 6793413ec75SRico Sonntag ->select(['d_month', 'i_sex', DB::raw('COUNT(*) AS total')]) 680d1a467e4SGreg Roach ->join('individuals', function (JoinClause $join): void { 681d1a467e4SGreg Roach $join 682d1a467e4SGreg Roach ->on('i_id', '=', 'd_gid') 683d1a467e4SGreg Roach ->on('i_file', '=', 'd_file'); 684d1a467e4SGreg Roach }) 6853413ec75SRico Sonntag ->groupBy('i_sex'); 6868add1155SRico Sonntag } 6878add1155SRico Sonntag 6888add1155SRico Sonntag /** 6898add1155SRico Sonntag * General query on births. 6908add1155SRico Sonntag * 6918add1155SRico Sonntag * @param string|null $color_from 6928add1155SRico Sonntag * @param string|null $color_to 6938add1155SRico Sonntag * 6948add1155SRico Sonntag * @return string 6958add1155SRico Sonntag */ 69688de55fdSRico Sonntag public function statsBirth(string $color_from = null, string $color_to = null): string 6978add1155SRico Sonntag { 6988add1155SRico Sonntag return (new ChartBirth($this->tree)) 69988de55fdSRico Sonntag ->chartBirth($color_from, $color_to); 7008add1155SRico Sonntag } 7018add1155SRico Sonntag 7028add1155SRico Sonntag /** 7038add1155SRico Sonntag * Get a list of death dates. 7048add1155SRico Sonntag * 7058add1155SRico Sonntag * @param int $year1 7068add1155SRico Sonntag * @param int $year2 7078add1155SRico Sonntag * 708cde1d378SGreg Roach * @return Builder 7098add1155SRico Sonntag */ 710cde1d378SGreg Roach public function statsDeathQuery(int $year1 = -1, int $year2 = -1): Builder 7118add1155SRico Sonntag { 712d1a467e4SGreg Roach $query = DB::table('dates') 7133413ec75SRico Sonntag ->select(['d_month', DB::raw('COUNT(*) AS total')]) 714d1a467e4SGreg Roach ->where('d_file', '=', $this->tree->id()) 715d1a467e4SGreg Roach ->where('d_fact', '=', 'DEAT') 716d1a467e4SGreg Roach ->whereIn('d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 717d1a467e4SGreg Roach ->groupBy('d_month'); 7188add1155SRico Sonntag 7198add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 720d1a467e4SGreg Roach $query->whereBetween('d_year', [$year1, $year2]); 7218add1155SRico Sonntag } 7228add1155SRico Sonntag 723cde1d378SGreg Roach return $query; 724cde1d378SGreg Roach } 725cde1d378SGreg Roach 726cde1d378SGreg Roach /** 727cde1d378SGreg Roach * Get a list of death dates. 728cde1d378SGreg Roach * 729cde1d378SGreg Roach * @param int $year1 730cde1d378SGreg Roach * @param int $year2 731cde1d378SGreg Roach * 732cde1d378SGreg Roach * @return Builder 733cde1d378SGreg Roach */ 734cde1d378SGreg Roach public function statsDeathBySexQuery(int $year1 = -1, int $year2 = -1): Builder 735cde1d378SGreg Roach { 736cde1d378SGreg Roach return $this->statsDeathQuery($year1, $year2) 7373413ec75SRico Sonntag ->select(['d_month', 'i_sex', DB::raw('COUNT(*) AS total')]) 738d1a467e4SGreg Roach ->join('individuals', function (JoinClause $join): void { 739d1a467e4SGreg Roach $join 740d1a467e4SGreg Roach ->on('i_id', '=', 'd_gid') 741d1a467e4SGreg Roach ->on('i_file', '=', 'd_file'); 742d1a467e4SGreg Roach }) 7433413ec75SRico Sonntag ->groupBy('i_sex'); 7448add1155SRico Sonntag } 7458add1155SRico Sonntag 7468add1155SRico Sonntag /** 7478add1155SRico Sonntag * General query on deaths. 7488add1155SRico Sonntag * 7498add1155SRico Sonntag * @param string|null $color_from 7508add1155SRico Sonntag * @param string|null $color_to 7518add1155SRico Sonntag * 7528add1155SRico Sonntag * @return string 7538add1155SRico Sonntag */ 75488de55fdSRico Sonntag public function statsDeath(string $color_from = null, string $color_to = null): string 7558add1155SRico Sonntag { 7568add1155SRico Sonntag return (new ChartDeath($this->tree)) 75788de55fdSRico Sonntag ->chartDeath($color_from, $color_to); 7588add1155SRico Sonntag } 7598add1155SRico Sonntag 7608add1155SRico Sonntag /** 7618add1155SRico Sonntag * General query on ages. 7628add1155SRico Sonntag * 7638add1155SRico Sonntag * @param string $related 7648add1155SRico Sonntag * @param string $sex 7658add1155SRico Sonntag * @param int $year1 7668add1155SRico Sonntag * @param int $year2 7678add1155SRico Sonntag * 7688add1155SRico Sonntag * @return array|string 7698add1155SRico Sonntag */ 7708add1155SRico Sonntag public function statsAgeQuery(string $related = 'BIRT', string $sex = 'BOTH', int $year1 = -1, int $year2 = -1) 7718add1155SRico Sonntag { 77244cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 7738add1155SRico Sonntag 77444cdc21eSGreg Roach $query = $this->birthAndDeathQuery($sex); 7758add1155SRico Sonntag 7768add1155SRico Sonntag if ($year1 >= 0 && $year2 >= 0) { 77744cdc21eSGreg Roach $query 77844cdc21eSGreg Roach ->whereIn('birth.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 77944cdc21eSGreg Roach ->whereIn('death.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']); 78044cdc21eSGreg Roach 7818add1155SRico Sonntag if ($related === 'BIRT') { 78244cdc21eSGreg Roach $query->whereBetween('birth.d_year', [$year1, $year2]); 7838add1155SRico Sonntag } elseif ($related === 'DEAT') { 78444cdc21eSGreg Roach $query->whereBetween('death.d_year', [$year1, $year2]); 7858add1155SRico Sonntag } 7868add1155SRico Sonntag } 7878add1155SRico Sonntag 78844cdc21eSGreg Roach return $query 78944cdc21eSGreg Roach ->select(DB::raw($prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1 AS days')) 790c8c87812SRico Sonntag ->orderBy('days', 'desc') 79144cdc21eSGreg Roach ->get() 79244cdc21eSGreg Roach ->all(); 7938add1155SRico Sonntag } 7948add1155SRico Sonntag 7958add1155SRico Sonntag /** 7968add1155SRico Sonntag * General query on ages. 7978add1155SRico Sonntag * 7988add1155SRico Sonntag * @return string 7998add1155SRico Sonntag */ 80088de55fdSRico Sonntag public function statsAge(): string 8018add1155SRico Sonntag { 80288de55fdSRico Sonntag return (new ChartAge($this->tree))->chartAge(); 8038add1155SRico Sonntag } 8048add1155SRico Sonntag 8058add1155SRico Sonntag /** 8068add1155SRico Sonntag * Lifespan 8078add1155SRico Sonntag * 8088add1155SRico Sonntag * @param string $type 8098add1155SRico Sonntag * @param string $sex 8108add1155SRico Sonntag * 8118add1155SRico Sonntag * @return string 8128add1155SRico Sonntag */ 8138add1155SRico Sonntag private function longlifeQuery(string $type, string $sex): string 8148add1155SRico Sonntag { 81544cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 8168add1155SRico Sonntag 81744cdc21eSGreg Roach $row = $this->birthAndDeathQuery($sex) 81844cdc21eSGreg Roach ->orderBy('days', 'desc') 81944cdc21eSGreg Roach ->select(['individuals.*', DB::raw($prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1 AS days')]) 82044cdc21eSGreg Roach ->first(); 82144cdc21eSGreg Roach 82244cdc21eSGreg Roach if ($row === null) { 8238add1155SRico Sonntag return ''; 8248add1155SRico Sonntag } 82544cdc21eSGreg Roach 82644cdc21eSGreg Roach /** @var Individual $individual */ 82744cdc21eSGreg Roach $individual = Individual::rowMapper()($row); 82844cdc21eSGreg Roach 82944cdc21eSGreg Roach if (!$individual->canShow()) { 83044cdc21eSGreg Roach return I18N::translate('This information is private and cannot be shown.'); 83144cdc21eSGreg Roach } 83244cdc21eSGreg Roach 8338add1155SRico Sonntag switch ($type) { 8348add1155SRico Sonntag default: 8358add1155SRico Sonntag case 'full': 83644cdc21eSGreg Roach return $individual->formatList(); 8378add1155SRico Sonntag 83844cdc21eSGreg Roach case 'age': 83944cdc21eSGreg Roach return I18N::number((int) ($row->days / 365.25)); 84044cdc21eSGreg Roach 84144cdc21eSGreg Roach case 'name': 84239ca88baSGreg Roach return '<a href="' . e($individual->url()) . '">' . $individual->fullName() . '</a>'; 84344cdc21eSGreg Roach } 8448add1155SRico Sonntag } 8458add1155SRico Sonntag 8468add1155SRico Sonntag /** 8478add1155SRico Sonntag * Find the longest lived individual. 8488add1155SRico Sonntag * 8498add1155SRico Sonntag * @return string 8508add1155SRico Sonntag */ 8518add1155SRico Sonntag public function longestLife(): string 8528add1155SRico Sonntag { 8538add1155SRico Sonntag return $this->longlifeQuery('full', 'BOTH'); 8548add1155SRico Sonntag } 8558add1155SRico Sonntag 8568add1155SRico Sonntag /** 8578add1155SRico Sonntag * Find the age of the longest lived individual. 8588add1155SRico Sonntag * 8598add1155SRico Sonntag * @return string 8608add1155SRico Sonntag */ 8618add1155SRico Sonntag public function longestLifeAge(): string 8628add1155SRico Sonntag { 8638add1155SRico Sonntag return $this->longlifeQuery('age', 'BOTH'); 8648add1155SRico Sonntag } 8658add1155SRico Sonntag 8668add1155SRico Sonntag /** 8678add1155SRico Sonntag * Find the name of the longest lived individual. 8688add1155SRico Sonntag * 8698add1155SRico Sonntag * @return string 8708add1155SRico Sonntag */ 8718add1155SRico Sonntag public function longestLifeName(): string 8728add1155SRico Sonntag { 8738add1155SRico Sonntag return $this->longlifeQuery('name', 'BOTH'); 8748add1155SRico Sonntag } 8758add1155SRico Sonntag 8768add1155SRico Sonntag /** 8778add1155SRico Sonntag * Find the longest lived female. 8788add1155SRico Sonntag * 8798add1155SRico Sonntag * @return string 8808add1155SRico Sonntag */ 8818add1155SRico Sonntag public function longestLifeFemale(): string 8828add1155SRico Sonntag { 8838add1155SRico Sonntag return $this->longlifeQuery('full', 'F'); 8848add1155SRico Sonntag } 8858add1155SRico Sonntag 8868add1155SRico Sonntag /** 8878add1155SRico Sonntag * Find the age of the longest lived female. 8888add1155SRico Sonntag * 8898add1155SRico Sonntag * @return string 8908add1155SRico Sonntag */ 8918add1155SRico Sonntag public function longestLifeFemaleAge(): string 8928add1155SRico Sonntag { 8938add1155SRico Sonntag return $this->longlifeQuery('age', 'F'); 8948add1155SRico Sonntag } 8958add1155SRico Sonntag 8968add1155SRico Sonntag /** 8978add1155SRico Sonntag * Find the name of the longest lived female. 8988add1155SRico Sonntag * 8998add1155SRico Sonntag * @return string 9008add1155SRico Sonntag */ 9018add1155SRico Sonntag public function longestLifeFemaleName(): string 9028add1155SRico Sonntag { 9038add1155SRico Sonntag return $this->longlifeQuery('name', 'F'); 9048add1155SRico Sonntag } 9058add1155SRico Sonntag 9068add1155SRico Sonntag /** 9078add1155SRico Sonntag * Find the longest lived male. 9088add1155SRico Sonntag * 9098add1155SRico Sonntag * @return string 9108add1155SRico Sonntag */ 9118add1155SRico Sonntag public function longestLifeMale(): string 9128add1155SRico Sonntag { 9138add1155SRico Sonntag return $this->longlifeQuery('full', 'M'); 9148add1155SRico Sonntag } 9158add1155SRico Sonntag 9168add1155SRico Sonntag /** 9178add1155SRico Sonntag * Find the age of the longest lived male. 9188add1155SRico Sonntag * 9198add1155SRico Sonntag * @return string 9208add1155SRico Sonntag */ 9218add1155SRico Sonntag public function longestLifeMaleAge(): string 9228add1155SRico Sonntag { 9238add1155SRico Sonntag return $this->longlifeQuery('age', 'M'); 9248add1155SRico Sonntag } 9258add1155SRico Sonntag 9268add1155SRico Sonntag /** 9278add1155SRico Sonntag * Find the name of the longest lived male. 9288add1155SRico Sonntag * 9298add1155SRico Sonntag * @return string 9308add1155SRico Sonntag */ 9318add1155SRico Sonntag public function longestLifeMaleName(): string 9328add1155SRico Sonntag { 9338add1155SRico Sonntag return $this->longlifeQuery('name', 'M'); 9348add1155SRico Sonntag } 9358add1155SRico Sonntag 9368add1155SRico Sonntag /** 9378add1155SRico Sonntag * Returns the calculated age the time of event. 9388add1155SRico Sonntag * 9398add1155SRico Sonntag * @param int $age The age from the database record 9408add1155SRico Sonntag * 9418add1155SRico Sonntag * @return string 9428add1155SRico Sonntag */ 9438add1155SRico Sonntag private function calculateAge(int $age): string 9448add1155SRico Sonntag { 9458add1155SRico Sonntag if ((int) ($age / 365.25) > 0) { 9468add1155SRico Sonntag $result = (int) ($age / 365.25) . 'y'; 9478add1155SRico Sonntag } elseif ((int) ($age / 30.4375) > 0) { 9488add1155SRico Sonntag $result = (int) ($age / 30.4375) . 'm'; 9498add1155SRico Sonntag } else { 9508add1155SRico Sonntag $result = $age . 'd'; 9518add1155SRico Sonntag } 9528add1155SRico Sonntag 9538add1155SRico Sonntag return FunctionsDate::getAgeAtEvent($result); 9548add1155SRico Sonntag } 9558add1155SRico Sonntag 9568add1155SRico Sonntag /** 9578add1155SRico Sonntag * Find the oldest individuals. 9588add1155SRico Sonntag * 9598add1155SRico Sonntag * @param string $sex 9608add1155SRico Sonntag * @param int $total 9618add1155SRico Sonntag * 9628add1155SRico Sonntag * @return array 9638add1155SRico Sonntag */ 9648add1155SRico Sonntag private function topTenOldestQuery(string $sex, int $total): array 9658add1155SRico Sonntag { 96644cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 9678add1155SRico Sonntag 96844cdc21eSGreg Roach $rows = $this->birthAndDeathQuery($sex) 96944cdc21eSGreg Roach ->groupBy(['i_id', 'i_file']) 97044cdc21eSGreg Roach ->orderBy('days', 'desc') 97144cdc21eSGreg Roach ->select(['individuals.*', DB::raw('MAX(' . $prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1) AS days')]) 97244cdc21eSGreg Roach ->take($total) 97344cdc21eSGreg Roach ->get(); 9748add1155SRico Sonntag 9758add1155SRico Sonntag $top10 = []; 9768add1155SRico Sonntag foreach ($rows as $row) { 97744cdc21eSGreg Roach /** @var Individual $individual */ 97844cdc21eSGreg Roach $individual = Individual::rowMapper()($row); 9798add1155SRico Sonntag 98044cdc21eSGreg Roach if ($individual->canShow()) { 9818add1155SRico Sonntag $top10[] = [ 98244cdc21eSGreg Roach 'person' => $individual, 98344cdc21eSGreg Roach 'age' => $this->calculateAge((int) $row->days), 9848add1155SRico Sonntag ]; 9858add1155SRico Sonntag } 9868add1155SRico Sonntag } 9878add1155SRico Sonntag 9888add1155SRico Sonntag return $top10; 9898add1155SRico Sonntag } 9908add1155SRico Sonntag 9918add1155SRico Sonntag /** 9928add1155SRico Sonntag * Find the oldest individuals. 9938add1155SRico Sonntag * 9948add1155SRico Sonntag * @param int $total 9958add1155SRico Sonntag * 9968add1155SRico Sonntag * @return string 9978add1155SRico Sonntag */ 9988add1155SRico Sonntag public function topTenOldest(int $total = 10): string 9998add1155SRico Sonntag { 10008add1155SRico Sonntag $records = $this->topTenOldestQuery('BOTH', $total); 10018add1155SRico Sonntag 1002c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 10038add1155SRico Sonntag 'records' => $records, 1004c0112ce8SGreg Roach ]); 10058add1155SRico Sonntag } 10068add1155SRico Sonntag 10078add1155SRico Sonntag /** 10088add1155SRico Sonntag * Find the oldest living individuals. 10098add1155SRico Sonntag * 10108add1155SRico Sonntag * @param int $total 10118add1155SRico Sonntag * 10128add1155SRico Sonntag * @return string 10138add1155SRico Sonntag */ 10148add1155SRico Sonntag public function topTenOldestList(int $total = 10): string 10158add1155SRico Sonntag { 10168add1155SRico Sonntag $records = $this->topTenOldestQuery('BOTH', $total); 10178add1155SRico Sonntag 1018c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 10198add1155SRico Sonntag 'records' => $records, 1020c0112ce8SGreg Roach ]); 10218add1155SRico Sonntag } 10228add1155SRico Sonntag 10238add1155SRico Sonntag /** 10248add1155SRico Sonntag * Find the oldest females. 10258add1155SRico Sonntag * 10268add1155SRico Sonntag * @param int $total 10278add1155SRico Sonntag * 10288add1155SRico Sonntag * @return string 10298add1155SRico Sonntag */ 10308add1155SRico Sonntag public function topTenOldestFemale(int $total = 10): string 10318add1155SRico Sonntag { 10328add1155SRico Sonntag $records = $this->topTenOldestQuery('F', $total); 10338add1155SRico Sonntag 1034c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 10358add1155SRico Sonntag 'records' => $records, 1036c0112ce8SGreg Roach ]); 10378add1155SRico Sonntag } 10388add1155SRico Sonntag 10398add1155SRico Sonntag /** 10408add1155SRico Sonntag * Find the oldest living females. 10418add1155SRico Sonntag * 10428add1155SRico Sonntag * @param int $total 10438add1155SRico Sonntag * 10448add1155SRico Sonntag * @return string 10458add1155SRico Sonntag */ 10468add1155SRico Sonntag public function topTenOldestFemaleList(int $total = 10): string 10478add1155SRico Sonntag { 10488add1155SRico Sonntag $records = $this->topTenOldestQuery('F', $total); 10498add1155SRico Sonntag 1050c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 10518add1155SRico Sonntag 'records' => $records, 1052c0112ce8SGreg Roach ]); 10538add1155SRico Sonntag } 10548add1155SRico Sonntag 10558add1155SRico Sonntag /** 10568add1155SRico Sonntag * Find the longest lived males. 10578add1155SRico Sonntag * 10588add1155SRico Sonntag * @param int $total 10598add1155SRico Sonntag * 10608add1155SRico Sonntag * @return string 10618add1155SRico Sonntag */ 10628add1155SRico Sonntag public function topTenOldestMale(int $total = 10): string 10638add1155SRico Sonntag { 10648add1155SRico Sonntag $records = $this->topTenOldestQuery('M', $total); 10658add1155SRico Sonntag 1066c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 10678add1155SRico Sonntag 'records' => $records, 1068c0112ce8SGreg Roach ]); 10698add1155SRico Sonntag } 10708add1155SRico Sonntag 10718add1155SRico Sonntag /** 10728add1155SRico Sonntag * Find the longest lived males. 10738add1155SRico Sonntag * 10748add1155SRico Sonntag * @param int $total 10758add1155SRico Sonntag * 10768add1155SRico Sonntag * @return string 10778add1155SRico Sonntag */ 10788add1155SRico Sonntag public function topTenOldestMaleList(int $total = 10): string 10798add1155SRico Sonntag { 10808add1155SRico Sonntag $records = $this->topTenOldestQuery('M', $total); 10818add1155SRico Sonntag 1082c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 10838add1155SRico Sonntag 'records' => $records, 1084c0112ce8SGreg Roach ]); 10858add1155SRico Sonntag } 10868add1155SRico Sonntag 10878add1155SRico Sonntag /** 10888add1155SRico Sonntag * Find the oldest living individuals. 10898add1155SRico Sonntag * 1090c0112ce8SGreg Roach * @param string $sex "M", "F" or "BOTH" 10918add1155SRico Sonntag * @param int $total 10928add1155SRico Sonntag * 10938add1155SRico Sonntag * @return array 10948add1155SRico Sonntag */ 1095c0112ce8SGreg Roach private function topTenOldestAliveQuery(string $sex, int $total): array 10968add1155SRico Sonntag { 1097d1a467e4SGreg Roach $query = DB::table('dates') 1098d1a467e4SGreg Roach ->join('individuals', function (JoinClause $join): void { 1099d1a467e4SGreg Roach $join 1100d1a467e4SGreg Roach ->on('i_id', '=', 'd_gid') 1101d1a467e4SGreg Roach ->on('i_file', '=', 'd_file'); 1102d1a467e4SGreg Roach }) 1103d1a467e4SGreg Roach ->where('d_file', '=', $this->tree->id()) 1104d1a467e4SGreg Roach ->where('d_julianday1', '<>', 0) 1105d1a467e4SGreg Roach ->where('d_fact', '=', 'BIRT') 1106d1a467e4SGreg Roach ->where('i_gedcom', 'NOT LIKE', "%\n1 DEAT%") 1107d1a467e4SGreg Roach ->where('i_gedcom', 'NOT LIKE', "%\n1 BURI%") 1108d1a467e4SGreg Roach ->where('i_gedcom', 'NOT LIKE', "%\n1 CREM%"); 1109d1a467e4SGreg Roach 1110d1a467e4SGreg Roach if ($sex === 'F' || $sex === 'M') { 1111d1a467e4SGreg Roach $query->where('i_sex', '=', $sex); 11128add1155SRico Sonntag } 11138add1155SRico Sonntag 1114c0112ce8SGreg Roach return $query 1115d1a467e4SGreg Roach ->groupBy(['i_id', 'i_file']) 1116d1a467e4SGreg Roach ->orderBy(DB::raw('MIN(d_julianday1)')) 1117d1a467e4SGreg Roach ->select('individuals.*') 1118d1a467e4SGreg Roach ->take($total) 1119d1a467e4SGreg Roach ->get() 1120d1a467e4SGreg Roach ->map(Individual::rowMapper()) 1121c0112ce8SGreg Roach ->filter(GedcomRecord::accessFilter()) 1122c0112ce8SGreg Roach ->map(function (Individual $individual): array { 1123d1a467e4SGreg Roach $birth_jd = $individual->getBirthDate()->minimumJulianDay(); 11248add1155SRico Sonntag 1125c0112ce8SGreg Roach return [ 1126d1a467e4SGreg Roach 'person' => $individual, 1127*269fd10dSGreg Roach 'age' => $this->calculateAge(unixtojd(Carbon::now()->timestamp) - $birth_jd), 11288add1155SRico Sonntag ]; 1129c0112ce8SGreg Roach }) 1130c0112ce8SGreg Roach ->all(); 11318add1155SRico Sonntag } 11328add1155SRico Sonntag 11338add1155SRico Sonntag /** 11348add1155SRico Sonntag * Find the oldest living individuals. 11358add1155SRico Sonntag * 11368add1155SRico Sonntag * @param int $total 11378add1155SRico Sonntag * 11388add1155SRico Sonntag * @return string 11398add1155SRico Sonntag */ 11408add1155SRico Sonntag public function topTenOldestAlive(int $total = 10): string 11418add1155SRico Sonntag { 11428add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 11438add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 11448add1155SRico Sonntag } 11458add1155SRico Sonntag 11468add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('BOTH', $total); 11478add1155SRico Sonntag 1148c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 11498add1155SRico Sonntag 'records' => $records, 1150c0112ce8SGreg Roach ]); 11518add1155SRico Sonntag } 11528add1155SRico Sonntag 11538add1155SRico Sonntag /** 11548add1155SRico Sonntag * Find the oldest living individuals. 11558add1155SRico Sonntag * 11568add1155SRico Sonntag * @param int $total 11578add1155SRico Sonntag * 11588add1155SRico Sonntag * @return string 11598add1155SRico Sonntag */ 11608add1155SRico Sonntag public function topTenOldestListAlive(int $total = 10): string 11618add1155SRico Sonntag { 11628add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 11638add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 11648add1155SRico Sonntag } 11658add1155SRico Sonntag 11668add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('BOTH', $total); 11678add1155SRico Sonntag 1168c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 11698add1155SRico Sonntag 'records' => $records, 1170c0112ce8SGreg Roach ]); 11718add1155SRico Sonntag } 11728add1155SRico Sonntag 11738add1155SRico Sonntag /** 11748add1155SRico Sonntag * Find the oldest living females. 11758add1155SRico Sonntag * 11768add1155SRico Sonntag * @param int $total 11778add1155SRico Sonntag * 11788add1155SRico Sonntag * @return string 11798add1155SRico Sonntag */ 11808add1155SRico Sonntag public function topTenOldestFemaleAlive(int $total = 10): string 11818add1155SRico Sonntag { 11828add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 11838add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 11848add1155SRico Sonntag } 11858add1155SRico Sonntag 11868add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('F', $total); 11878add1155SRico Sonntag 1188c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 11898add1155SRico Sonntag 'records' => $records, 1190c0112ce8SGreg Roach ]); 11918add1155SRico Sonntag } 11928add1155SRico Sonntag 11938add1155SRico Sonntag /** 11948add1155SRico Sonntag * Find the oldest living females. 11958add1155SRico Sonntag * 11968add1155SRico Sonntag * @param int $total 11978add1155SRico Sonntag * 11988add1155SRico Sonntag * @return string 11998add1155SRico Sonntag */ 12008add1155SRico Sonntag public function topTenOldestFemaleListAlive(int $total = 10): string 12018add1155SRico Sonntag { 12028add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 12038add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12048add1155SRico Sonntag } 12058add1155SRico Sonntag 12068add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('F', $total); 12078add1155SRico Sonntag 1208c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 12098add1155SRico Sonntag 'records' => $records, 1210c0112ce8SGreg Roach ]); 12118add1155SRico Sonntag } 12128add1155SRico Sonntag 12138add1155SRico Sonntag /** 12148add1155SRico Sonntag * Find the longest lived living males. 12158add1155SRico Sonntag * 12168add1155SRico Sonntag * @param int $total 12178add1155SRico Sonntag * 12188add1155SRico Sonntag * @return string 12198add1155SRico Sonntag */ 12208add1155SRico Sonntag public function topTenOldestMaleAlive(int $total = 10): string 12218add1155SRico Sonntag { 12228add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 12238add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12248add1155SRico Sonntag } 12258add1155SRico Sonntag 12268add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('M', $total); 12278add1155SRico Sonntag 1228c0112ce8SGreg Roach return view('statistics/individuals/top10-nolist', [ 12298add1155SRico Sonntag 'records' => $records, 1230c0112ce8SGreg Roach ]); 12318add1155SRico Sonntag } 12328add1155SRico Sonntag 12338add1155SRico Sonntag /** 12348add1155SRico Sonntag * Find the longest lived living males. 12358add1155SRico Sonntag * 12368add1155SRico Sonntag * @param int $total 12378add1155SRico Sonntag * 12388add1155SRico Sonntag * @return string 12398add1155SRico Sonntag */ 12408add1155SRico Sonntag public function topTenOldestMaleListAlive(int $total = 10): string 12418add1155SRico Sonntag { 12428add1155SRico Sonntag if (!Auth::isMember($this->tree)) { 12438add1155SRico Sonntag return I18N::translate('This information is private and cannot be shown.'); 12448add1155SRico Sonntag } 12458add1155SRico Sonntag 12468add1155SRico Sonntag $records = $this->topTenOldestAliveQuery('M', $total); 12478add1155SRico Sonntag 1248c0112ce8SGreg Roach return view('statistics/individuals/top10-list', [ 12498add1155SRico Sonntag 'records' => $records, 1250c0112ce8SGreg Roach ]); 12518add1155SRico Sonntag } 12528add1155SRico Sonntag 12538add1155SRico Sonntag /** 12548add1155SRico Sonntag * Find the average lifespan. 12558add1155SRico Sonntag * 1256c0112ce8SGreg Roach * @param string $sex "M", "F" or "BOTH" 12578add1155SRico Sonntag * @param bool $show_years 12588add1155SRico Sonntag * 12598add1155SRico Sonntag * @return string 12608add1155SRico Sonntag */ 1261c0112ce8SGreg Roach private function averageLifespanQuery(string $sex, bool $show_years): string 12628add1155SRico Sonntag { 126344cdc21eSGreg Roach $prefix = DB::connection()->getTablePrefix(); 12648add1155SRico Sonntag 126544cdc21eSGreg Roach $days = (int) $this->birthAndDeathQuery($sex) 1266e4235cb4SRico Sonntag ->select(DB::raw('AVG(' . $prefix . 'death.d_julianday2 - ' . $prefix . 'birth.d_julianday1) AS days')) 126744cdc21eSGreg Roach ->value('days'); 12688add1155SRico Sonntag 12698add1155SRico Sonntag if ($show_years) { 127044cdc21eSGreg Roach return $this->calculateAge($days); 12718add1155SRico Sonntag } 12728add1155SRico Sonntag 127344cdc21eSGreg Roach return I18N::number((int) ($days / 365.25)); 12748add1155SRico Sonntag } 12758add1155SRico Sonntag 12768add1155SRico Sonntag /** 12778add1155SRico Sonntag * Find the average lifespan. 12788add1155SRico Sonntag * 12798add1155SRico Sonntag * @param bool $show_years 12808add1155SRico Sonntag * 12818add1155SRico Sonntag * @return string 12828add1155SRico Sonntag */ 12838add1155SRico Sonntag public function averageLifespan($show_years = false): string 12848add1155SRico Sonntag { 12858add1155SRico Sonntag return $this->averageLifespanQuery('BOTH', $show_years); 12868add1155SRico Sonntag } 12878add1155SRico Sonntag 12888add1155SRico Sonntag /** 12898add1155SRico Sonntag * Find the average lifespan of females. 12908add1155SRico Sonntag * 12918add1155SRico Sonntag * @param bool $show_years 12928add1155SRico Sonntag * 12938add1155SRico Sonntag * @return string 12948add1155SRico Sonntag */ 12958add1155SRico Sonntag public function averageLifespanFemale($show_years = false): string 12968add1155SRico Sonntag { 12978add1155SRico Sonntag return $this->averageLifespanQuery('F', $show_years); 12988add1155SRico Sonntag } 12998add1155SRico Sonntag 13008add1155SRico Sonntag /** 13018add1155SRico Sonntag * Find the average male lifespan. 13028add1155SRico Sonntag * 13038add1155SRico Sonntag * @param bool $show_years 13048add1155SRico Sonntag * 13058add1155SRico Sonntag * @return string 13068add1155SRico Sonntag */ 13078add1155SRico Sonntag public function averageLifespanMale($show_years = false): string 13088add1155SRico Sonntag { 13098add1155SRico Sonntag return $this->averageLifespanQuery('M', $show_years); 13108add1155SRico Sonntag } 13118add1155SRico Sonntag 13128add1155SRico Sonntag /** 13138add1155SRico Sonntag * Convert totals into percentages. 13148add1155SRico Sonntag * 13158add1155SRico Sonntag * @param int $count 13168add1155SRico Sonntag * @param int $total 13178add1155SRico Sonntag * 13188add1155SRico Sonntag * @return string 13198add1155SRico Sonntag */ 13208add1155SRico Sonntag private function getPercentage(int $count, int $total): string 13218add1155SRico Sonntag { 1322f24db0ceSRico Sonntag return ($total !== 0) ? I18N::percentage($count / $total, 1) : ''; 13238add1155SRico Sonntag } 13248add1155SRico Sonntag 13258add1155SRico Sonntag /** 13268add1155SRico Sonntag * Returns how many individuals exist in the tree. 13278add1155SRico Sonntag * 13288add1155SRico Sonntag * @return int 13298add1155SRico Sonntag */ 13308add1155SRico Sonntag private function totalIndividualsQuery(): int 13318add1155SRico Sonntag { 13328add1155SRico Sonntag return DB::table('individuals') 13338add1155SRico Sonntag ->where('i_file', '=', $this->tree->id()) 13348add1155SRico Sonntag ->count(); 13358add1155SRico Sonntag } 13368add1155SRico Sonntag 13378add1155SRico Sonntag /** 13388add1155SRico Sonntag * Count the number of living individuals. 13398add1155SRico Sonntag * 13408add1155SRico Sonntag * The totalLiving/totalDeceased queries assume that every dead person will 13418add1155SRico Sonntag * have a DEAT record. It will not include individuals who were born more 13428add1155SRico Sonntag * than MAX_ALIVE_AGE years ago, and who have no DEAT record. 13438add1155SRico Sonntag * A good reason to run the “Add missing DEAT records” batch-update! 13448add1155SRico Sonntag * 13458add1155SRico Sonntag * @return int 13468add1155SRico Sonntag */ 13478add1155SRico Sonntag private function totalLivingQuery(): int 13488add1155SRico Sonntag { 13493dc8167dSGreg Roach $query = DB::table('individuals') 13503dc8167dSGreg Roach ->where('i_file', '=', $this->tree->id()); 13513dc8167dSGreg Roach 13523dc8167dSGreg Roach foreach (Gedcom::DEATH_EVENTS as $death_event) { 135368d9d7c9SRico Sonntag $query->where('i_gedcom', 'NOT LIKE', "%\n1 " . $death_event . '%'); 13543dc8167dSGreg Roach } 13553dc8167dSGreg Roach 13563dc8167dSGreg Roach return $query->count(); 13578add1155SRico Sonntag } 13588add1155SRico Sonntag 13598add1155SRico Sonntag /** 13608add1155SRico Sonntag * Count the number of dead individuals. 13618add1155SRico Sonntag * 13628add1155SRico Sonntag * @return int 13638add1155SRico Sonntag */ 13648add1155SRico Sonntag private function totalDeceasedQuery(): int 13658add1155SRico Sonntag { 13668add1155SRico Sonntag return DB::table('individuals') 13678add1155SRico Sonntag ->where('i_file', '=', $this->tree->id()) 13683dc8167dSGreg Roach ->where(function (Builder $query): void { 13693dc8167dSGreg Roach foreach (Gedcom::DEATH_EVENTS as $death_event) { 137068d9d7c9SRico Sonntag $query->orWhere('i_gedcom', 'LIKE', "%\n1 " . $death_event . '%'); 13713dc8167dSGreg Roach } 13723dc8167dSGreg Roach }) 13738add1155SRico Sonntag ->count(); 13748add1155SRico Sonntag } 13758add1155SRico Sonntag 13768add1155SRico Sonntag /** 13778add1155SRico Sonntag * Returns the total count of a specific sex. 13788add1155SRico Sonntag * 13798add1155SRico Sonntag * @param string $sex The sex to query 13808add1155SRico Sonntag * 13818add1155SRico Sonntag * @return int 13828add1155SRico Sonntag */ 13838add1155SRico Sonntag private function getTotalSexQuery(string $sex): int 13848add1155SRico Sonntag { 13858add1155SRico Sonntag return DB::table('individuals') 13868add1155SRico Sonntag ->where('i_file', '=', $this->tree->id()) 13878add1155SRico Sonntag ->where('i_sex', '=', $sex) 13888add1155SRico Sonntag ->count(); 13898add1155SRico Sonntag } 13908add1155SRico Sonntag 13918add1155SRico Sonntag /** 13928add1155SRico Sonntag * Returns the total number of males. 13938add1155SRico Sonntag * 13948add1155SRico Sonntag * @return int 13958add1155SRico Sonntag */ 13968add1155SRico Sonntag private function totalSexMalesQuery(): int 13978add1155SRico Sonntag { 13988add1155SRico Sonntag return $this->getTotalSexQuery('M'); 13998add1155SRico Sonntag } 14008add1155SRico Sonntag 14018add1155SRico Sonntag /** 14028add1155SRico Sonntag * Returns the total number of females. 14038add1155SRico Sonntag * 14048add1155SRico Sonntag * @return int 14058add1155SRico Sonntag */ 14068add1155SRico Sonntag private function totalSexFemalesQuery(): int 14078add1155SRico Sonntag { 14088add1155SRico Sonntag return $this->getTotalSexQuery('F'); 14098add1155SRico Sonntag } 14108add1155SRico Sonntag 14118add1155SRico Sonntag /** 14128add1155SRico Sonntag * Returns the total number of individuals with unknown sex. 14138add1155SRico Sonntag * 14148add1155SRico Sonntag * @return int 14158add1155SRico Sonntag */ 14168add1155SRico Sonntag private function totalSexUnknownQuery(): int 14178add1155SRico Sonntag { 14188add1155SRico Sonntag return $this->getTotalSexQuery('U'); 14198add1155SRico Sonntag } 14208add1155SRico Sonntag 14218add1155SRico Sonntag /** 14228add1155SRico Sonntag * Count the total families. 14238add1155SRico Sonntag * 14248add1155SRico Sonntag * @return int 14258add1155SRico Sonntag */ 14268add1155SRico Sonntag private function totalFamiliesQuery(): int 14278add1155SRico Sonntag { 14288add1155SRico Sonntag return DB::table('families') 14298add1155SRico Sonntag ->where('f_file', '=', $this->tree->id()) 14308add1155SRico Sonntag ->count(); 14318add1155SRico Sonntag } 14328add1155SRico Sonntag 14338add1155SRico Sonntag /** 14348add1155SRico Sonntag * How many individuals have one or more sources. 14358add1155SRico Sonntag * 14368add1155SRico Sonntag * @return int 14378add1155SRico Sonntag */ 14388add1155SRico Sonntag private function totalIndisWithSourcesQuery(): int 14398add1155SRico Sonntag { 14408add1155SRico Sonntag return DB::table('individuals') 14418add1155SRico Sonntag ->select(['i_id']) 14428add1155SRico Sonntag ->distinct() 14438add1155SRico Sonntag ->join('link', function (JoinClause $join) { 14448add1155SRico Sonntag $join->on('i_id', '=', 'l_from') 14458add1155SRico Sonntag ->on('i_file', '=', 'l_file'); 14468add1155SRico Sonntag }) 14478add1155SRico Sonntag ->where('l_file', '=', $this->tree->id()) 14488add1155SRico Sonntag ->where('l_type', '=', 'SOUR') 14498add1155SRico Sonntag ->count('i_id'); 14508add1155SRico Sonntag } 14518add1155SRico Sonntag 14528add1155SRico Sonntag /** 14538add1155SRico Sonntag * Count the families with source records. 14548add1155SRico Sonntag * 14558add1155SRico Sonntag * @return int 14568add1155SRico Sonntag */ 14578add1155SRico Sonntag private function totalFamsWithSourcesQuery(): int 14588add1155SRico Sonntag { 14598add1155SRico Sonntag return DB::table('families') 14608add1155SRico Sonntag ->select(['f_id']) 14618add1155SRico Sonntag ->distinct() 14628add1155SRico Sonntag ->join('link', function (JoinClause $join) { 14638add1155SRico Sonntag $join->on('f_id', '=', 'l_from') 14648add1155SRico Sonntag ->on('f_file', '=', 'l_file'); 14658add1155SRico Sonntag }) 14668add1155SRico Sonntag ->where('l_file', '=', $this->tree->id()) 14678add1155SRico Sonntag ->where('l_type', '=', 'SOUR') 14688add1155SRico Sonntag ->count('f_id'); 14698add1155SRico Sonntag } 14708add1155SRico Sonntag 14718add1155SRico Sonntag /** 14728add1155SRico Sonntag * Count the number of repositories. 14738add1155SRico Sonntag * 14748add1155SRico Sonntag * @return int 14758add1155SRico Sonntag */ 14768add1155SRico Sonntag private function totalRepositoriesQuery(): int 14778add1155SRico Sonntag { 14788add1155SRico Sonntag return DB::table('other') 14798add1155SRico Sonntag ->where('o_file', '=', $this->tree->id()) 14808add1155SRico Sonntag ->where('o_type', '=', 'REPO') 14818add1155SRico Sonntag ->count(); 14828add1155SRico Sonntag } 14838add1155SRico Sonntag 14848add1155SRico Sonntag /** 14858add1155SRico Sonntag * Count the total number of sources. 14868add1155SRico Sonntag * 14878add1155SRico Sonntag * @return int 14888add1155SRico Sonntag */ 14898add1155SRico Sonntag private function totalSourcesQuery(): int 14908add1155SRico Sonntag { 14918add1155SRico Sonntag return DB::table('sources') 14928add1155SRico Sonntag ->where('s_file', '=', $this->tree->id()) 14938add1155SRico Sonntag ->count(); 14948add1155SRico Sonntag } 14958add1155SRico Sonntag 14968add1155SRico Sonntag /** 14978add1155SRico Sonntag * Count the number of notes. 14988add1155SRico Sonntag * 14998add1155SRico Sonntag * @return int 15008add1155SRico Sonntag */ 15018add1155SRico Sonntag private function totalNotesQuery(): int 15028add1155SRico Sonntag { 15038add1155SRico Sonntag return DB::table('other') 15048add1155SRico Sonntag ->where('o_file', '=', $this->tree->id()) 15058add1155SRico Sonntag ->where('o_type', '=', 'NOTE') 15068add1155SRico Sonntag ->count(); 15078add1155SRico Sonntag } 15088add1155SRico Sonntag 15098add1155SRico Sonntag /** 15108add1155SRico Sonntag * Returns the total number of records. 15118add1155SRico Sonntag * 15128add1155SRico Sonntag * @return int 15138add1155SRico Sonntag */ 15148add1155SRico Sonntag private function totalRecordsQuery(): int 15158add1155SRico Sonntag { 15168add1155SRico Sonntag return $this->totalIndividualsQuery() 15178add1155SRico Sonntag + $this->totalFamiliesQuery() 15188add1155SRico Sonntag + $this->totalNotesQuery() 15198add1155SRico Sonntag + $this->totalRepositoriesQuery() 15208add1155SRico Sonntag + $this->totalSourcesQuery(); 15218add1155SRico Sonntag } 15228add1155SRico Sonntag 15238add1155SRico Sonntag /** 15248add1155SRico Sonntag * @inheritDoc 15258add1155SRico Sonntag */ 15268add1155SRico Sonntag public function totalRecords(): string 15278add1155SRico Sonntag { 15288add1155SRico Sonntag return I18N::number($this->totalRecordsQuery()); 15298add1155SRico Sonntag } 15308add1155SRico Sonntag 15318add1155SRico Sonntag /** 15328add1155SRico Sonntag * @inheritDoc 15338add1155SRico Sonntag */ 15348add1155SRico Sonntag public function totalIndividuals(): string 15358add1155SRico Sonntag { 15368add1155SRico Sonntag return I18N::number($this->totalIndividualsQuery()); 15378add1155SRico Sonntag } 15388add1155SRico Sonntag 15398add1155SRico Sonntag /** 15408add1155SRico Sonntag * Count the number of living individuals. 15418add1155SRico Sonntag * 15428add1155SRico Sonntag * @return string 15438add1155SRico Sonntag */ 15448add1155SRico Sonntag public function totalLiving(): string 15458add1155SRico Sonntag { 15468add1155SRico Sonntag return I18N::number($this->totalLivingQuery()); 15478add1155SRico Sonntag } 15488add1155SRico Sonntag 15498add1155SRico Sonntag /** 15508add1155SRico Sonntag * Count the number of dead individuals. 15518add1155SRico Sonntag * 15528add1155SRico Sonntag * @return string 15538add1155SRico Sonntag */ 15548add1155SRico Sonntag public function totalDeceased(): string 15558add1155SRico Sonntag { 15568add1155SRico Sonntag return I18N::number($this->totalDeceasedQuery()); 15578add1155SRico Sonntag } 15588add1155SRico Sonntag 15598add1155SRico Sonntag /** 15608add1155SRico Sonntag * @inheritDoc 15618add1155SRico Sonntag */ 15628add1155SRico Sonntag public function totalSexMales(): string 15638add1155SRico Sonntag { 15648add1155SRico Sonntag return I18N::number($this->totalSexMalesQuery()); 15658add1155SRico Sonntag } 15668add1155SRico Sonntag 15678add1155SRico Sonntag /** 15688add1155SRico Sonntag * @inheritDoc 15698add1155SRico Sonntag */ 15708add1155SRico Sonntag public function totalSexFemales(): string 15718add1155SRico Sonntag { 15728add1155SRico Sonntag return I18N::number($this->totalSexFemalesQuery()); 15738add1155SRico Sonntag } 15748add1155SRico Sonntag 15758add1155SRico Sonntag /** 15768add1155SRico Sonntag * @inheritDoc 15778add1155SRico Sonntag */ 15788add1155SRico Sonntag public function totalSexUnknown(): string 15798add1155SRico Sonntag { 15808add1155SRico Sonntag return I18N::number($this->totalSexUnknownQuery()); 15818add1155SRico Sonntag } 15828add1155SRico Sonntag 15838add1155SRico Sonntag /** 15848add1155SRico Sonntag * @inheritDoc 15858add1155SRico Sonntag */ 15868add1155SRico Sonntag public function totalFamilies(): string 15878add1155SRico Sonntag { 15888add1155SRico Sonntag return I18N::number($this->totalFamiliesQuery()); 15898add1155SRico Sonntag } 15908add1155SRico Sonntag 15918add1155SRico Sonntag /** 15928add1155SRico Sonntag * How many individuals have one or more sources. 15938add1155SRico Sonntag * 15948add1155SRico Sonntag * @return string 15958add1155SRico Sonntag */ 15968add1155SRico Sonntag public function totalIndisWithSources(): string 15978add1155SRico Sonntag { 15988add1155SRico Sonntag return I18N::number($this->totalIndisWithSourcesQuery()); 15998add1155SRico Sonntag } 16008add1155SRico Sonntag 16018add1155SRico Sonntag /** 16028add1155SRico Sonntag * Count the families with with source records. 16038add1155SRico Sonntag * 16048add1155SRico Sonntag * @return string 16058add1155SRico Sonntag */ 16068add1155SRico Sonntag public function totalFamsWithSources(): string 16078add1155SRico Sonntag { 16088add1155SRico Sonntag return I18N::number($this->totalFamsWithSourcesQuery()); 16098add1155SRico Sonntag } 16108add1155SRico Sonntag 16118add1155SRico Sonntag /** 16128add1155SRico Sonntag * @inheritDoc 16138add1155SRico Sonntag */ 16148add1155SRico Sonntag public function totalRepositories(): string 16158add1155SRico Sonntag { 16168add1155SRico Sonntag return I18N::number($this->totalRepositoriesQuery()); 16178add1155SRico Sonntag } 16188add1155SRico Sonntag 16198add1155SRico Sonntag /** 16208add1155SRico Sonntag * @inheritDoc 16218add1155SRico Sonntag */ 16228add1155SRico Sonntag public function totalSources(): string 16238add1155SRico Sonntag { 16248add1155SRico Sonntag return I18N::number($this->totalSourcesQuery()); 16258add1155SRico Sonntag } 16268add1155SRico Sonntag 16278add1155SRico Sonntag /** 16288add1155SRico Sonntag * @inheritDoc 16298add1155SRico Sonntag */ 16308add1155SRico Sonntag public function totalNotes(): string 16318add1155SRico Sonntag { 16328add1155SRico Sonntag return I18N::number($this->totalNotesQuery()); 16338add1155SRico Sonntag } 16348add1155SRico Sonntag 16358add1155SRico Sonntag /** 16368add1155SRico Sonntag * @inheritDoc 16378add1155SRico Sonntag */ 16388add1155SRico Sonntag public function totalIndividualsPercentage(): string 16398add1155SRico Sonntag { 16408add1155SRico Sonntag return $this->getPercentage( 16418add1155SRico Sonntag $this->totalIndividualsQuery(), 16428add1155SRico Sonntag $this->totalRecordsQuery() 16438add1155SRico Sonntag ); 16448add1155SRico Sonntag } 16458add1155SRico Sonntag 16468add1155SRico Sonntag /** 16478add1155SRico Sonntag * @inheritDoc 16488add1155SRico Sonntag */ 16498add1155SRico Sonntag public function totalFamiliesPercentage(): string 16508add1155SRico Sonntag { 16518add1155SRico Sonntag return $this->getPercentage( 16528add1155SRico Sonntag $this->totalFamiliesQuery(), 16538add1155SRico Sonntag $this->totalRecordsQuery() 16548add1155SRico Sonntag ); 16558add1155SRico Sonntag } 16568add1155SRico Sonntag 16578add1155SRico Sonntag /** 16588add1155SRico Sonntag * @inheritDoc 16598add1155SRico Sonntag */ 16608add1155SRico Sonntag public function totalRepositoriesPercentage(): string 16618add1155SRico Sonntag { 16628add1155SRico Sonntag return $this->getPercentage( 16638add1155SRico Sonntag $this->totalRepositoriesQuery(), 16648add1155SRico Sonntag $this->totalRecordsQuery() 16658add1155SRico Sonntag ); 16668add1155SRico Sonntag } 16678add1155SRico Sonntag 16688add1155SRico Sonntag /** 16698add1155SRico Sonntag * @inheritDoc 16708add1155SRico Sonntag */ 16718add1155SRico Sonntag public function totalSourcesPercentage(): string 16728add1155SRico Sonntag { 16738add1155SRico Sonntag return $this->getPercentage( 16748add1155SRico Sonntag $this->totalSourcesQuery(), 16758add1155SRico Sonntag $this->totalRecordsQuery() 16768add1155SRico Sonntag ); 16778add1155SRico Sonntag } 16788add1155SRico Sonntag 16798add1155SRico Sonntag /** 16808add1155SRico Sonntag * @inheritDoc 16818add1155SRico Sonntag */ 16828add1155SRico Sonntag public function totalNotesPercentage(): string 16838add1155SRico Sonntag { 16848add1155SRico Sonntag return $this->getPercentage( 16858add1155SRico Sonntag $this->totalNotesQuery(), 16868add1155SRico Sonntag $this->totalRecordsQuery() 16878add1155SRico Sonntag ); 16888add1155SRico Sonntag } 16898add1155SRico Sonntag 16908add1155SRico Sonntag /** 16918add1155SRico Sonntag * @inheritDoc 16928add1155SRico Sonntag */ 16938add1155SRico Sonntag public function totalLivingPercentage(): string 16948add1155SRico Sonntag { 16958add1155SRico Sonntag return $this->getPercentage( 16968add1155SRico Sonntag $this->totalLivingQuery(), 16978add1155SRico Sonntag $this->totalIndividualsQuery() 16988add1155SRico Sonntag ); 16998add1155SRico Sonntag } 17008add1155SRico Sonntag 17018add1155SRico Sonntag /** 17028add1155SRico Sonntag * @inheritDoc 17038add1155SRico Sonntag */ 17048add1155SRico Sonntag public function totalDeceasedPercentage(): string 17058add1155SRico Sonntag { 17068add1155SRico Sonntag return $this->getPercentage( 17078add1155SRico Sonntag $this->totalDeceasedQuery(), 17088add1155SRico Sonntag $this->totalIndividualsQuery() 17098add1155SRico Sonntag ); 17108add1155SRico Sonntag } 17118add1155SRico Sonntag 17128add1155SRico Sonntag /** 17138add1155SRico Sonntag * @inheritDoc 17148add1155SRico Sonntag */ 17158add1155SRico Sonntag public function totalSexMalesPercentage(): string 17168add1155SRico Sonntag { 17178add1155SRico Sonntag return $this->getPercentage( 17188add1155SRico Sonntag $this->totalSexMalesQuery(), 17198add1155SRico Sonntag $this->totalIndividualsQuery() 17208add1155SRico Sonntag ); 17218add1155SRico Sonntag } 17228add1155SRico Sonntag 17238add1155SRico Sonntag /** 17248add1155SRico Sonntag * @inheritDoc 17258add1155SRico Sonntag */ 17268add1155SRico Sonntag public function totalSexFemalesPercentage(): string 17278add1155SRico Sonntag { 17288add1155SRico Sonntag return $this->getPercentage( 17298add1155SRico Sonntag $this->totalSexFemalesQuery(), 17308add1155SRico Sonntag $this->totalIndividualsQuery() 17318add1155SRico Sonntag ); 17328add1155SRico Sonntag } 17338add1155SRico Sonntag 17348add1155SRico Sonntag /** 17358add1155SRico Sonntag * @inheritDoc 17368add1155SRico Sonntag */ 17378add1155SRico Sonntag public function totalSexUnknownPercentage(): string 17388add1155SRico Sonntag { 17398add1155SRico Sonntag return $this->getPercentage( 17408add1155SRico Sonntag $this->totalSexUnknownQuery(), 17418add1155SRico Sonntag $this->totalIndividualsQuery() 17428add1155SRico Sonntag ); 17438add1155SRico Sonntag } 17448add1155SRico Sonntag 17458add1155SRico Sonntag /** 17468add1155SRico Sonntag * Create a chart of common given names. 17478add1155SRico Sonntag * 17488add1155SRico Sonntag * @param string|null $color_from 17498add1155SRico Sonntag * @param string|null $color_to 17508add1155SRico Sonntag * @param int $maxtoshow 17518add1155SRico Sonntag * 17528add1155SRico Sonntag * @return string 17538add1155SRico Sonntag */ 17548add1155SRico Sonntag public function chartCommonGiven( 17558add1155SRico Sonntag string $color_from = null, 17568add1155SRico Sonntag string $color_to = null, 17578add1155SRico Sonntag int $maxtoshow = 7 17588add1155SRico Sonntag ): string { 17598add1155SRico Sonntag $tot_indi = $this->totalIndividualsQuery(); 17608add1155SRico Sonntag $given = $this->commonGivenQuery('B', 'chart', false, 1, $maxtoshow); 17618add1155SRico Sonntag 176288de55fdSRico Sonntag if (empty($given)) { 1763dd7dd2a1SRico Sonntag return I18N::translate('This information is not available.'); 176488de55fdSRico Sonntag } 176588de55fdSRico Sonntag 176693ccd686SRico Sonntag return (new ChartCommonGiven()) 176788de55fdSRico Sonntag ->chartCommonGiven($tot_indi, $given, $color_from, $color_to); 17688add1155SRico Sonntag } 17698add1155SRico Sonntag 17708add1155SRico Sonntag /** 17718add1155SRico Sonntag * Create a chart of common surnames. 17728add1155SRico Sonntag * 17738add1155SRico Sonntag * @param string|null $color_from 17748add1155SRico Sonntag * @param string|null $color_to 17758add1155SRico Sonntag * @param int $number_of_surnames 17768add1155SRico Sonntag * 17778add1155SRico Sonntag * @return string 17788add1155SRico Sonntag */ 17798add1155SRico Sonntag public function chartCommonSurnames( 17808add1155SRico Sonntag string $color_from = null, 17818add1155SRico Sonntag string $color_to = null, 17828add1155SRico Sonntag int $number_of_surnames = 10 17838add1155SRico Sonntag ): string { 17848add1155SRico Sonntag $tot_indi = $this->totalIndividualsQuery(); 17858add1155SRico Sonntag $all_surnames = $this->topSurnames($number_of_surnames, 0); 17868add1155SRico Sonntag 178788de55fdSRico Sonntag if (empty($all_surnames)) { 1788dd7dd2a1SRico Sonntag return I18N::translate('This information is not available.'); 178988de55fdSRico Sonntag } 179088de55fdSRico Sonntag 17918add1155SRico Sonntag return (new ChartCommonSurname($this->tree)) 179288de55fdSRico Sonntag ->chartCommonSurnames($tot_indi, $all_surnames, $color_from, $color_to); 17938add1155SRico Sonntag } 17948add1155SRico Sonntag 17958add1155SRico Sonntag /** 17968add1155SRico Sonntag * Create a chart showing mortality. 17978add1155SRico Sonntag * 17988add1155SRico Sonntag * @param string|null $color_living 17998add1155SRico Sonntag * @param string|null $color_dead 18008add1155SRico Sonntag * 18018add1155SRico Sonntag * @return string 18028add1155SRico Sonntag */ 180388de55fdSRico Sonntag public function chartMortality(string $color_living = null, string $color_dead = null): string 18048add1155SRico Sonntag { 18058add1155SRico Sonntag $tot_l = $this->totalLivingQuery(); 18068add1155SRico Sonntag $tot_d = $this->totalDeceasedQuery(); 18078add1155SRico Sonntag 180893ccd686SRico Sonntag return (new ChartMortality()) 180988de55fdSRico Sonntag ->chartMortality($tot_l, $tot_d, $color_living, $color_dead); 18108add1155SRico Sonntag } 18118add1155SRico Sonntag 18128add1155SRico Sonntag /** 18138add1155SRico Sonntag * Create a chart showing individuals with/without sources. 18148add1155SRico Sonntag * 18158add1155SRico Sonntag * @param string|null $color_from 18168add1155SRico Sonntag * @param string|null $color_to 18178add1155SRico Sonntag * 18188add1155SRico Sonntag * @return string 18198add1155SRico Sonntag */ 18208add1155SRico Sonntag public function chartIndisWithSources( 18218add1155SRico Sonntag string $color_from = null, 18228add1155SRico Sonntag string $color_to = null 18238add1155SRico Sonntag ): string { 18248add1155SRico Sonntag $tot_indi = $this->totalIndividualsQuery(); 18258add1155SRico Sonntag $tot_indi_source = $this->totalIndisWithSourcesQuery(); 18268add1155SRico Sonntag 182793ccd686SRico Sonntag return (new ChartIndividualWithSources()) 182888de55fdSRico Sonntag ->chartIndisWithSources($tot_indi, $tot_indi_source, $color_from, $color_to); 18298add1155SRico Sonntag } 18308add1155SRico Sonntag 18318add1155SRico Sonntag /** 18328add1155SRico Sonntag * Create a chart of individuals with/without sources. 18338add1155SRico Sonntag * 18348add1155SRico Sonntag * @param string|null $color_from 18358add1155SRico Sonntag * @param string|null $color_to 18368add1155SRico Sonntag * 18378add1155SRico Sonntag * @return string 18388add1155SRico Sonntag */ 18398add1155SRico Sonntag public function chartFamsWithSources( 18408add1155SRico Sonntag string $color_from = null, 18418add1155SRico Sonntag string $color_to = null 18428add1155SRico Sonntag ): string { 18438add1155SRico Sonntag $tot_fam = $this->totalFamiliesQuery(); 18448add1155SRico Sonntag $tot_fam_source = $this->totalFamsWithSourcesQuery(); 18458add1155SRico Sonntag 184693ccd686SRico Sonntag return (new ChartFamilyWithSources()) 184788de55fdSRico Sonntag ->chartFamsWithSources($tot_fam, $tot_fam_source, $color_from, $color_to); 18488add1155SRico Sonntag } 18498add1155SRico Sonntag 18508add1155SRico Sonntag /** 18518add1155SRico Sonntag * @inheritDoc 18528add1155SRico Sonntag */ 18538add1155SRico Sonntag public function chartSex( 18548add1155SRico Sonntag string $color_female = null, 18558add1155SRico Sonntag string $color_male = null, 18568add1155SRico Sonntag string $color_unknown = null 18578add1155SRico Sonntag ): string { 18588add1155SRico Sonntag $tot_m = $this->totalSexMalesQuery(); 18598add1155SRico Sonntag $tot_f = $this->totalSexFemalesQuery(); 18608add1155SRico Sonntag $tot_u = $this->totalSexUnknownQuery(); 18618add1155SRico Sonntag 186293ccd686SRico Sonntag return (new ChartSex()) 186388de55fdSRico Sonntag ->chartSex($tot_m, $tot_f, $tot_u, $color_female, $color_male, $color_unknown); 18648add1155SRico Sonntag } 186544cdc21eSGreg Roach 186644cdc21eSGreg Roach /** 186744cdc21eSGreg Roach * Query individuals, with their births and deaths. 186844cdc21eSGreg Roach * 186944cdc21eSGreg Roach * @param string $sex 187044cdc21eSGreg Roach * 187144cdc21eSGreg Roach * @return Builder 187244cdc21eSGreg Roach */ 1873e2cbf57aSGreg Roach private function birthAndDeathQuery(string $sex): Builder 1874e2cbf57aSGreg Roach { 187544cdc21eSGreg Roach $query = DB::table('individuals') 187644cdc21eSGreg Roach ->where('i_file', '=', $this->tree->id()) 187744cdc21eSGreg Roach ->join('dates AS birth', function (JoinClause $join): void { 187844cdc21eSGreg Roach $join 187944cdc21eSGreg Roach ->on('birth.d_file', '=', 'i_file') 188044cdc21eSGreg Roach ->on('birth.d_gid', '=', 'i_id'); 188144cdc21eSGreg Roach }) 188244cdc21eSGreg Roach ->join('dates AS death', function (JoinClause $join): void { 188344cdc21eSGreg Roach $join 188444cdc21eSGreg Roach ->on('death.d_file', '=', 'i_file') 188544cdc21eSGreg Roach ->on('death.d_gid', '=', 'i_id'); 188644cdc21eSGreg Roach }) 188744cdc21eSGreg Roach ->where('birth.d_fact', '=', 'BIRT') 188844cdc21eSGreg Roach ->where('death.d_fact', '=', 'DEAT') 188944cdc21eSGreg Roach ->whereColumn('death.d_julianday1', '>=', 'birth.d_julianday2') 189044cdc21eSGreg Roach ->where('birth.d_julianday2', '<>', 0); 189144cdc21eSGreg Roach 189244cdc21eSGreg Roach if ($sex === 'M' || $sex === 'F') { 189344cdc21eSGreg Roach $query->where('i_sex', '=', $sex); 189444cdc21eSGreg Roach } 189544cdc21eSGreg Roach 189644cdc21eSGreg Roach return $query; 189744cdc21eSGreg Roach } 18988add1155SRico Sonntag} 1899