1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2019 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Statistics\Google; 21 22use Fisharebest\Localization\Locale\LocaleInterface; 23use Fisharebest\Webtrees\I18N; 24use Fisharebest\Webtrees\Statistics\Service\CenturyService; 25use Fisharebest\Webtrees\Tree; 26use Illuminate\Database\Capsule\Manager as DB; 27use Illuminate\Database\Query\Expression; 28use Illuminate\Database\Query\JoinClause; 29use Psr\Http\Message\ServerRequestInterface; 30use stdClass; 31 32use function app; 33use function assert; 34 35/** 36 * A chart showing the marriage ages by century. 37 */ 38class ChartMarriageAge 39{ 40 /** 41 * @var Tree 42 */ 43 private $tree; 44 45 /** 46 * @var CenturyService 47 */ 48 private $century_service; 49 50 /** 51 * Constructor. 52 * 53 * @param Tree $tree 54 */ 55 public function __construct(Tree $tree) 56 { 57 $this->tree = $tree; 58 $this->century_service = new CenturyService(); 59 } 60 61 /** 62 * Returns the related database records. 63 * 64 * @return stdClass[] 65 */ 66 private function queryRecords(): array 67 { 68 $prefix = DB::connection()->getTablePrefix(); 69 70 $male = DB::table('dates as married') 71 ->select([ 72 new Expression('ROUND(AVG(' . $prefix . 'married.d_julianday2 - ' . $prefix . 'birth.d_julianday1 - 182.5) / 365.25, 1) AS age'), 73 new Expression('ROUND((' . $prefix . 'married.d_year + 49) / 100) AS century'), 74 new Expression("'M' as sex") 75 ]) 76 ->join('families as fam', static function (JoinClause $join): void { 77 $join->on('fam.f_id', '=', 'married.d_gid') 78 ->on('fam.f_file', '=', 'married.d_file'); 79 }) 80 ->join('dates as birth', static function (JoinClause $join): void { 81 $join->on('birth.d_gid', '=', 'fam.f_husb') 82 ->on('birth.d_file', '=', 'fam.f_file'); 83 }) 84 ->whereIn('married.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 85 ->where('married.d_file', '=', $this->tree->id()) 86 ->where('married.d_fact', '=', 'MARR') 87 ->where('married.d_julianday1', '>', 'birth.d_julianday1') 88 ->whereIn('birth.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 89 ->where('birth.d_fact', '=', 'BIRT') 90 ->where('birth.d_julianday1', '<>', 0) 91 ->groupBy(['century', 'sex']); 92 93 $female = DB::table('dates as married') 94 ->select([ 95 new Expression('ROUND(AVG(' . $prefix . 'married.d_julianday2 - ' . $prefix . 'birth.d_julianday1 - 182.5) / 365.25, 1) AS age'), 96 new Expression('ROUND((' . $prefix . 'married.d_year + 49) / 100) AS century'), 97 new Expression("'F' as sex") 98 ]) 99 ->join('families as fam', static function (JoinClause $join): void { 100 $join->on('fam.f_id', '=', 'married.d_gid') 101 ->on('fam.f_file', '=', 'married.d_file'); 102 }) 103 ->join('dates as birth', static function (JoinClause $join): void { 104 $join->on('birth.d_gid', '=', 'fam.f_wife') 105 ->on('birth.d_file', '=', 'fam.f_file'); 106 }) 107 ->whereIn('married.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 108 ->where('married.d_file', '=', $this->tree->id()) 109 ->where('married.d_fact', '=', 'MARR') 110 ->where('married.d_julianday1', '>', 'birth.d_julianday1') 111 ->whereIn('birth.d_type', ['@#DGREGORIAN@', '@#DJULIAN@']) 112 ->where('birth.d_fact', '=', 'BIRT') 113 ->where('birth.d_julianday1', '<>', 0) 114 ->groupBy(['century', 'sex']); 115 116 return $male->unionAll($female) 117 ->orderBy('century') 118 ->get() 119 ->all(); 120 } 121 122 /** 123 * General query on ages at marriage. 124 * 125 * @return string 126 */ 127 public function chartMarriageAge(): string 128 { 129 $out = []; 130 131 foreach ($this->queryRecords() as $record) { 132 $out[(int) $record->century][$record->sex] = (float) $record->age; 133 } 134 135 $data = [ 136 [ 137 I18N::translate('Century'), 138 I18N::translate('Males'), 139 I18N::translate('Females'), 140 I18N::translate('Average age'), 141 ] 142 ]; 143 144 foreach ($out as $century => $values) { 145 $female_age = $values['F'] ?? 0; 146 $male_age = $values['M'] ?? 0; 147 $average_age = ($female_age + $male_age) / 2.0; 148 149 $data[] = [ 150 $this->century_service->centuryName($century), 151 $male_age, 152 $female_age, 153 $average_age, 154 ]; 155 } 156 157 $chart_title = I18N::translate('Average age in century of marriage'); 158 $chart_options = [ 159 'title' => $chart_title, 160 'subtitle' => I18N::translate('Average age at marriage'), 161 'vAxis' => [ 162 'title' => I18N::translate('Age'), 163 ], 164 'hAxis' => [ 165 'title' => I18N::translate('Century'), 166 ], 167 'colors' => [ 168 '#84beff', 169 '#ffd1dc', 170 '#ff0000', 171 ], 172 ]; 173 174 $locale = app(ServerRequestInterface::class)->getAttribute('locale'); 175 assert($locale instanceof LocaleInterface); 176 177 return view('statistics/other/charts/combo', [ 178 'data' => $data, 179 'chart_options' => $chart_options, 180 'chart_title' => $chart_title, 181 'language' => $locale->languageTag(), 182 ]); 183 } 184} 185