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