Statistics   F
last analyzed

Complexity

Total Complexity 344

Size/Duplication

Total Lines 2463
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 949
dl 0
loc 2463
rs 1.6509
c 0
b 0
f 0
wmc 344

283 Methods

Rating   Name   Duplication   Size   Complexity  
A lastBirth() 0 3 1
A totalNotes() 0 3 1
A commonBirthPlacesList() 0 3 1
A totalMediaPainting() 0 3 1
A hitCountNote() 0 3 1
A totalMediaPhoto() 0 3 1
A minAgeOfMarriageFamilies() 0 3 1
A firstEventType() 0 3 1
A firstDeath() 0 3 1
A lastBirthName() 0 3 1
A noChildrenFamiliesList() 0 3 1
A lastDivorceYear() 0 3 1
A firstEventName() 0 3 1
A totalDivorces() 0 3 1
A totalMedia() 0 3 1
A youngestMarriageMaleAge() 0 3 1
A topAgeBetweenSiblings() 0 3 1
A commonGivenOtherListTotals() 0 5 1
A totalSexFemalesPercentage() 0 5 1
A totalNonAdmins() 0 3 1
A firstDeathPlace() 0 3 1
A totalMediaCertificate() 0 3 1
A totalMediaTombstone() 0 3 1
A commonGivenTable() 0 5 1
A oldestMarriageFemale() 0 3 1
A chartSex() 0 20 1
A totalMediaOther() 0 3 1
A firstEventYear() 0 3 1
A totalTreeNews() 0 3 1
A totalIndisWithSources() 0 3 1
A totalSexMalesPercentage() 0 5 1
A hitCountUser() 0 3 1
A chartDistribution() 0 6 1
A chartMortality() 0 22 3
A firstMarriagePlace() 0 3 1
A ageBetweenSpousesMF() 0 3 1
A minAgeOfMarriageFamily() 0 3 1
A totalIndividuals() 0 3 1
A chartNoChildrenFamilies() 0 74 3
A totalEventsDeath() 0 3 1
A topTenOldestAlive() 0 15 2
A totalSexOther() 0 3 1
A totalMediaManuscript() 0 3 1
A oldestMotherName() 0 3 1
A chartMedia() 0 10 1
A largestFamily() 0 9 2
A oldestFather() 0 3 1
A totalMediaBook() 0 3 1
A gedcomFavorites() 0 3 1
A latestUserLoggedin() 0 7 2
A totalSexUnknownPercentage() 0 5 1
A totalBirths() 0 3 1
A contactWebmaster() 0 12 2
A totalGedcomFavorites() 0 3 1
A topTenOldest() 0 11 1
A latestUserRegTime() 0 11 2
A commonGivenOtherTotals() 0 7 1
A totalSexUnknown() 0 3 1
A contactGedcom() 0 12 2
A longestLifeFemale() 0 9 2
A lastDivorceName() 0 3 1
A embedTags() 0 3 1
A noChildrenFamilies() 0 3 1
A topAgeOfMarriageFamiliesList() 0 3 1
A commonCountriesList() 0 3 1
A lastMarriageYear() 0 3 1
A lastEventType() 0 3 1
A commonGivenUnknownListTotals() 0 5 1
A commonDeathPlacesList() 0 3 1
A commonGivenFemaleTable() 0 5 1
A commonGivenTotals() 0 7 1
A commonGivenUnknownList() 0 5 1
A totalMarriedMales() 0 3 1
A topTenOldestFemale() 0 11 1
A totalEventsMarriage() 0 3 1
A commonGivenFemaleTotals() 0 7 1
A topAgeBetweenSiblingsFullName() 0 3 1
A commonGivenMaleTotals() 0 7 1
A largestFamilyName() 0 3 1
A totalMediaElectronic() 0 3 1
A chartIndisWithSources() 0 17 1
A totalFamilies() 0 3 1
A userId() 0 3 1
A lastEventName() 0 3 1
A serverDate() 0 3 1
A longestLifeFemaleAge() 0 9 2
A statsDiv() 0 11 1
A totalGivennames() 0 3 1
A totalMediaFilm() 0 3 1
A topTenOldestMaleAlive() 0 15 2
A getCommonSurname() 0 5 1
A averageLifespan() 0 5 2
A firstDivorceName() 0 3 1
A lastEventYear() 0 3 1
A usersLoggedInList() 0 3 1
A firstBirthPlace() 0 3 1
A gedcomDate() 0 17 4
A commonGivenMale() 0 7 1
A oldestMarriageFemaleAge() 0 3 1
A getAllTagsTable() 0 22 3
A totalAdmins() 0 3 1
A commonGivenMaleTable() 0 5 1
A firstMarriage() 0 3 1
A youngestMotherAge() 0 3 1
A commonGivenFemaleList() 0 5 1
A youngestMarriageFemaleAge() 0 3 1
A topTenLargestGrandFamily() 0 3 1
A topAgeOfMarriageFamilies() 0 3 1
A hitCountFam() 0 3 1
A latestUserRegDate() 0 11 2
A totalFamsWithSources() 0 3 1
A totalUserJournal() 0 3 1
A userFullName() 0 3 2
A lastBirthYear() 0 3 1
A longestLifeAge() 0 9 2
A topAgeBetweenSiblingsList() 0 3 1
A commonSurnamesTotals() 0 3 1
A ageBetweenSpousesMFList() 0 3 1
A oldestMarriageMaleAge() 0 3 1
A latestUserId() 0 5 1
A chartCommonGiven() 0 38 4
A firstDeathName() 0 3 1
A totalMediaCard() 0 3 1
A gedcomTitle() 0 3 1
A serverTimezone() 0 3 1
A youngestFather() 0 3 1
A youngestMarriageFemale() 0 3 1
A totalMediaDocument() 0 3 1
A totalMarriages() 0 3 1
A totalDeaths() 0 3 1
A topTenOldestList() 0 11 1
A totalIndisWithSourcesPercentage() 0 5 1
A hitCountObje() 0 3 1
A lastEventPlace() 0 3 1
A callBlock() 0 24 5
A lastDeathPlace() 0 3 1
A oldestFatherAge() 0 3 1
A lastMarriage() 0 3 1
A statsAge() 0 56 3
A totalFamiliesPercentage() 0 5 1
A totalEventsBirth() 0 3 1
A statsChildrenQuery() 0 3 1
A lastDivorce() 0 3 1
A longestLifeName() 0 3 1
A chartLargestFamilies() 0 23 1
A hitCountIndi() 0 3 1
A firstDivorceYear() 0 3 1
A commonGivenUnknownTable() 0 5 1
A browserDate() 0 3 1
A youngestFatherName() 0 3 1
A statsDeath() 0 11 1
A commonGivenOtherList() 0 5 1
A hitCount() 0 3 1
A ageBetweenSpousesFMList() 0 3 1
A webtreesVersion() 0 3 1
A commonGivenOtherTable() 0 5 1
A minAgeOfMarriageFamiliesList() 0 3 1
A totalIndividualsPercentage() 0 5 1
A totalLiving() 0 3 1
A latestUserName() 0 5 1
A lastMarriagePlace() 0 3 1
A firstBirthName() 0 3 1
A topAgeOfMarriage() 0 3 1
A youngestMother() 0 3 1
A youngestMarriageMale() 0 3 1
A ageBetweenSpousesFM() 0 3 1
A commonGiven() 0 7 1
A commonSurnamesList() 0 3 1
A oldestMotherAge() 0 3 1
A firstMarriageName() 0 3 1
A lastDeathYear() 0 3 1
A chartFamsWithSources() 0 17 1
A lastDivorcePlace() 0 3 1
A statsMarrAgeQuery() 0 3 1
A totalChildren() 0 3 1
A totalNotesPercentage() 0 5 1
A youngestMarriageFemaleName() 0 3 1
A totalRepositories() 0 3 1
A firstBirth() 0 3 1
A totalMediaMagazine() 0 3 1
A commonGivenListTotals() 0 5 1
A oldestMarriageMale() 0 3 1
A totalDeceasedPercentage() 0 5 1
A topTenOldestMaleListAlive() 0 15 2
A commonGivenMaleListTotals() 0 5 1
A statsChildren() 0 60 2
A lastBirthPlace() 0 3 1
A totalSexFemales() 0 3 1
A latestUserFullName() 0 5 1
A totalMediaMap() 0 3 1
A gedcomUpdated() 0 14 2
A topTenLargestGrandFamilyList() 0 3 1
A firstDivorce() 0 3 1
A getTags() 0 17 3
A gedcomRootId() 0 3 1
A commonGivenFemaleListTotals() 0 5 1
A topTenLargestFamilyList() 0 3 1
A commonGivenList() 0 5 1
A firstEvent() 0 3 1
A totalFamsWithSourcesPercentage() 0 5 1
A hitCountRepo() 0 3 1
A topTenLargestFamily() 0 3 1
A gedcomFilename() 0 3 1
A totalUserMessages() 0 3 1
A topTenOldestMale() 0 11 1
A commonMarriagePlacesList() 0 3 1
A youngestFatherAge() 0 3 1
A totalEventsOther() 0 8 1
A longestLifeMale() 0 9 2
A topTenOldestFemaleList() 0 11 1
A totalMediaUnknown() 0 3 1
A lastDeathName() 0 3 1
A averageChildren() 0 3 1
A youngestMarriageMaleName() 0 3 1
A gedcomCreatedSoftware() 0 13 3
A minAgeOfMarriage() 0 3 1
A totalUserFavorites() 0 3 1
A averageLifespanFemale() 0 5 2
A totalPlaces() 0 3 1
A topTenOldestMaleList() 0 11 1
A commonGivenOther() 0 7 1
A topTenOldestFemaleListAlive() 0 15 2
A oldestMarriageMaleName() 0 3 1
A userFavorites() 0 3 1
A lastMarriageName() 0 3 1
A totalSexMales() 0 3 1
A browserTime() 0 3 1
A firstDeathYear() 0 3 1
A serverTime24() 0 3 1
A commonGivenMaleList() 0 5 1
A serverTime() 0 3 1
A browserTimezone() 0 3 1
B chartCommonSurnames() 0 76 7
A youngestMotherName() 0 3 1
A commonGivenUnknown() 0 7 1
A firstMarriageYear() 0 3 1
A totalRepositoriesPercentage() 0 5 1
A topTenOldestFemaleAlive() 0 15 2
A totalSourcesPercentage() 0 5 1
A commonSurnamesListTotals() 0 3 1
A statsMarr() 0 11 1
A commonSurnames() 0 3 1
A __construct() 0 7 1
A longestLifeMaleName() 0 3 1
A totalRecords() 0 3 1
A totalSexOtherPercentage() 0 5 1
A totalSurnames() 0 3 1
A totalMediaCoatOfArms() 0 3 1
A totalMediaVideo() 0 3 1
A totalSources() 0 3 1
A largestFamilySize() 0 3 1
A averageLifespanMale() 0 5 2
A oldestFatherName() 0 3 1
A totalMediaFiche() 0 3 1
A oldestMother() 0 3 1
A totalEventsDivorce() 0 3 1
A lastDeath() 0 3 1
A longestLifeFemaleName() 0 3 1
A totalDeceased() 0 3 1
A oldestMarriageFemaleName() 0 3 1
A topAgeOfMarriageFamily() 0 3 1
A totalMediaNewspaper() 0 3 1
A lastEvent() 0 3 1
A topTenOldestListAlive() 0 15 2
A gedcomCreatedVersion() 0 26 5
A longestLifeMaleAge() 0 9 2
B statsMarrAge() 0 110 3
A statsBirth() 0 11 1
A longestLife() 0 9 2
A firstEventPlace() 0 3 1
A totalUsers() 0 3 1
A firstDivorcePlace() 0 3 1
A totalMediaAudio() 0 3 1
A usersLoggedIn() 0 3 1
A topAgeBetweenSiblingsName() 0 13 2
A commonGivenUnknownTotals() 0 7 1
A totalLivingPercentage() 0 5 1
A totalMarriedFemales() 0 3 1
A firstBirthYear() 0 3 1
A hitCountSour() 0 3 1
A userName() 0 11 3
A commonGivenFemale() 0 7 1
A totalEvents() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Statistics often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Statistics, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2025 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 <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees;
21
22
use Fisharebest\Webtrees\Contracts\UserInterface;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Contracts\UserInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use Fisharebest\Webtrees\Module\ModuleBlockInterface;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Module\ModuleBlockInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
24
use Fisharebest\Webtrees\Module\ModuleInterface;
25
use Fisharebest\Webtrees\Services\ModuleService;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Services\ModuleService was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
26
use Fisharebest\Webtrees\Services\UserService;
27
use Fisharebest\Webtrees\SurnameTradition\PolishSurnameTradition;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Sur...\PolishSurnameTradition was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
28
use Illuminate\Database\Query\Expression;
29
use Illuminate\Database\Query\JoinClause;
30
use Illuminate\Support\Collection;
31
use InvalidArgumentException;
32
use Psr\Http\Message\ServerRequestInterface;
33
use ReflectionClass;
34
use ReflectionException;
35
use ReflectionMethod;
36
use ReflectionNamedType;
37
38
use function array_keys;
39
use function array_shift;
40
use function array_sum;
41
use function count;
42
use function e;
43
use function htmlspecialchars_decode;
44
use function implode;
45
use function in_array;
46
use function preg_replace;
47
use function round;
48
use function strip_tags;
49
use function strpos;
50
use function substr;
51
use function view;
52
53
/**
54
 * A selection of pre-formatted statistical queries.
55
 * These are primarily used for embedded keywords on HTML blocks, but
56
 * are also used elsewhere in the code.
57
 */
58
class Statistics
59
{
60
    private readonly StatisticsData $data;
61
62
    private readonly StatisticsFormat $format;
63
64
    public function __construct(
65
        private readonly ModuleService $module_service,
66
        private readonly Tree $tree,
67
        private readonly UserService $user_service
68
    ) {
69
        $this->data   = new StatisticsData($tree, $user_service);
0 ignored issues
show
Bug introduced by
The property data is declared read-only in Fisharebest\Webtrees\Statistics.
Loading history...
70
        $this->format = new StatisticsFormat();
0 ignored issues
show
Bug introduced by
The property format is declared read-only in Fisharebest\Webtrees\Statistics.
Loading history...
71
    }
72
73
    public function ageBetweenSpousesFM(string $limit = '10'): string
74
    {
75
        return $this->data->ageBetweenSpousesFM((int) $limit);
76
    }
77
78
    public function ageBetweenSpousesFMList(string $limit = '10'): string
79
    {
80
        return $this->data->ageBetweenSpousesFMList((int) $limit);
81
    }
82
83
    public function ageBetweenSpousesMF(string $limit = '10'): string
84
    {
85
        return $this->data->ageBetweenSpousesMF((int) $limit);
86
    }
87
88
    public function ageBetweenSpousesMFList(string $limit = '10'): string
89
    {
90
        return $this->data->ageBetweenSpousesMFList((int) $limit);
91
    }
92
93
    public function averageChildren(): string
94
    {
95
        return I18N::number($this->data->averageChildrenPerFamily(), 2);
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\I18N was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
96
    }
97
98
    public function averageLifespan(string $show_years = '0'): string
99
    {
100
        $days = $this->data->averageLifespanDays('ALL');
101
102
        return $show_years ? $this->format->age($days) : I18N::number((int) ($days / 365.25));
103
    }
104
105
    public function averageLifespanFemale(string $show_years = '0'): string
106
    {
107
        $days = $this->data->averageLifespanDays('F');
108
109
        return $show_years ? $this->format->age($days) : I18N::number((int) ($days / 365.25));
110
    }
111
112
    public function averageLifespanMale(string $show_years = '0'): string
113
    {
114
        $days = $this->data->averageLifespanDays('M');
115
116
        return $show_years ? $this->format->age($days) : I18N::number((int) ($days / 365.25));
117
    }
118
119
    public function browserDate(): string
120
    {
121
        return Registry::timestampFactory()->now()->format(strtr(I18N::dateFormat(), ['%' => '']));
122
    }
123
124
    public function browserTime(): string
125
    {
126
        return Registry::timestampFactory()->now()->format(strtr(I18N::timeFormat(), ['%' => '']));
127
    }
128
129
    public function browserTimezone(): string
130
    {
131
        return Registry::timestampFactory()->now()->format('T');
132
    }
133
134
    /**
135
     * Create any of the other blocks.
136
     * Use as #callBlock:block_name#
137
     *
138
     * @param string ...$params
139
     */
140
    public function callBlock(string $block = '', ...$params): string
141
    {
142
        $module = $this->module_service
143
            ->findByComponent(ModuleBlockInterface::class, $this->tree, Auth::user())
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Auth was not found. Did you mean Auth? If so, make sure to prefix the type with \.
Loading history...
144
            ->first(static fn (ModuleInterface $module): bool => $module->name() === $block && $module->name() !== 'html');
145
146
        if ($module === null) {
147
            return '';
148
        }
149
150
        // Build the config array
151
        $cfg = [];
152
        foreach ($params as $config) {
153
            $bits = explode('=', $config);
154
155
            if (count($bits) < 2) {
156
                continue;
157
            }
158
159
            $v       = array_shift($bits);
160
            $cfg[$v] = implode('=', $bits);
161
        }
162
163
        return $module->getBlock($this->tree, 0, ModuleBlockInterface::CONTEXT_EMBED, $cfg);
164
    }
165
166
    public function chartCommonGiven(string $color1 = 'ffffff', string $color2 = '84beff', string $limit = '7'): string
167
    {
168
        $given = $this->data->commonGivenNames('ALL', 1, (int) $limit)->all();
169
170
        if ($given === []) {
171
            return I18N::translate('This information is not available.');
172
        }
173
174
        $tot = 0;
175
        foreach ($given as $count) {
176
            $tot += $count;
177
        }
178
179
        $data = [
180
            [
181
                I18N::translate('Name'),
182
                I18N::translate('Total'),
183
            ],
184
        ];
185
186
        foreach ($given as $name => $count) {
187
            $data[] = [$name, $count];
188
        }
189
190
        $count_all_names = $this->data->commonGivenNames('ALL', 1, PHP_INT_MAX)->sum();
191
192
        $data[] = [
193
            I18N::translate('Other'),
194
            $count_all_names - $tot,
195
        ];
196
197
        $colors = $this->format->interpolateRgb($color1, $color2, count($data) - 1);
198
199
        return view('statistics/other/charts/pie', [
200
            'title'    => null,
201
            'data'     => $data,
202
            'colors'   => $colors,
203
            'language' => I18N::languageTag(),
204
        ]);
205
    }
206
207
    public function chartCommonSurnames(
208
        string $color1 = 'ffffff',
209
        string $color2 = '84beff',
210
        string $limit = '10'
211
    ): string {
212
        $all_surnames = $this->data->commonSurnames((int) $limit, 0, 'count');
213
214
        if ($all_surnames === []) {
215
            return I18N::translate('This information is not available.');
216
        }
217
218
        $surname_tradition = Registry::surnameTraditionFactory()
219
            ->make($this->tree->getPreference('SURNAME_TRADITION'));
220
221
        $tot = 0;
222
        foreach ($all_surnames as $surnames) {
223
            $tot += array_sum($surnames);
224
        }
225
226
        $data = [
227
            [
228
                I18N::translate('Name'),
229
                I18N::translate('Total')
230
            ],
231
        ];
232
233
        foreach ($all_surnames as $surns) {
234
            $max_name  = 0;
235
            $count_per = 0;
236
            $top_name  = '';
237
238
            foreach ($surns as $spfxsurn => $count) {
239
                $per = $count;
240
                $count_per += $per;
241
242
                // select most common surname from all variants
243
                if ($per > $max_name) {
244
                    $max_name = $per;
245
                    $top_name = $spfxsurn;
246
                }
247
            }
248
249
            if ($surname_tradition instanceof PolishSurnameTradition) {
250
                // Most common surname should be in male variant (Kowalski, not Kowalska)
251
                $top_name = preg_replace(
252
                    [
253
                        '/ska$/',
254
                        '/cka$/',
255
                        '/dzka$/',
256
                        '/żka$/',
257
                    ],
258
                    [
259
                        'ski',
260
                        'cki',
261
                        'dzki',
262
                        'żki',
263
                    ],
264
                    $top_name
265
                );
266
            }
267
268
            $data[] = [(string) $top_name, $count_per];
269
        }
270
271
        $data[] = [
272
            I18N::translate('Other'),
273
            $this->data->countIndividuals() - $tot
274
        ];
275
276
        $colors = $this->format->interpolateRgb($color1, $color2, count($data) - 1);
277
278
        return view('statistics/other/charts/pie', [
279
            'title'    => null,
280
            'data'     => $data,
281
            'colors'   => $colors,
282
            'language' => I18N::languageTag(),
283
        ]);
284
    }
285
286
    public function chartDistribution(
287
        string $chart_shows = 'world',
288
        string $chart_type = '',
289
        string $surname = ''
290
    ): string {
291
        return $this->data->chartDistribution($chart_shows, $chart_type, $surname);
292
    }
293
294
    public function chartFamsWithSources(string $color1 = 'c2dfff', string $color2 = '84beff'): string
295
    {
296
        $total_families              = $this->data->countFamilies();
297
        $total_families_with_sources = $this->data->countFamiliesWithSources();
298
299
        $data = [
300
            [I18N::translate('Without sources'), $total_families - $total_families_with_sources],
301
            [I18N::translate('With sources'), $total_families_with_sources],
302
        ];
303
304
        return $this->format->pieChart(
305
            $data,
306
            [$color1, $color2],
307
            I18N::translate('Families with sources'),
308
            I18N::translate('Type'),
309
            I18N::translate('Total'),
310
            true
311
        );
312
    }
313
314
    public function chartIndisWithSources(string $color1 = 'c2dfff', string $color2 = '84beff'): string
315
    {
316
        $total_individuals              = $this->data->countIndividuals();
317
        $total_individuals_with_sources = $this->data->countIndividualsWithSources();
318
319
        $data = [
320
            [I18N::translate('Without sources'), $total_individuals - $total_individuals_with_sources],
321
            [I18N::translate('With sources'), $total_individuals_with_sources],
322
        ];
323
324
        return $this->format->pieChart(
325
            $data,
326
            [$color1, $color2],
327
            I18N::translate('Individuals with sources'),
328
            I18N::translate('Type'),
329
            I18N::translate('Total'),
330
            true
331
        );
332
    }
333
334
    public function chartLargestFamilies(
335
        string $color1 = 'ffffff',
336
        string $color2 = '84beff',
337
        string $limit = '7'
338
    ): string {
339
        $data = DB::table('families')
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\DB was not found. Did you mean DB? If so, make sure to prefix the type with \.
Loading history...
340
            ->select(['f_numchil AS total', 'f_id AS id'])
341
            ->where('f_file', '=', $this->tree->id())
342
            ->orderBy('total', 'desc')
343
            ->limit((int) $limit)
344
            ->get()
345
            ->map(fn (object $row): array => [
346
                htmlspecialchars_decode(strip_tags(Registry::familyFactory()->make($row->id, $this->tree)->fullName())),
347
                (int) $row->total,
348
            ])
349
            ->all();
350
351
        return $this->format->pieChart(
352
            $data,
353
            $this->format->interpolateRgb($color1, $color2, count($data)),
354
            I18N::translate('Largest families'),
355
            I18N::translate('Family'),
356
            I18N::translate('Children')
357
        );
358
    }
359
360
    public function chartMedia(string $color1 = 'ffffff', string $color2 = '84beff'): string
361
    {
362
        $data = $this->data->countMediaByType();
363
364
        return $this->format->pieChart(
365
            $data,
366
            $this->format->interpolateRgb($color1, $color2, count($data)),
367
            I18N::translate('Media by type'),
368
            I18N::translate('Type'),
369
            I18N::translate('Total'),
370
        );
371
    }
372
373
    public function chartMortality(string $color_living = '#ffffff', string $color_dead = '#cccccc'): string
374
    {
375
        $total_living = $this->data->countIndividualsLiving();
376
        $total_dead   = $this->data->countIndividualsDeceased();
377
378
        $data = [
379
            [I18N::translate('Century'), I18N::translate('Total')],
380
        ];
381
382
        if ($total_living > 0 || $total_dead > 0) {
383
            $data[] = [I18N::translate('Living'), $total_living];
384
            $data[] = [I18N::translate('Dead'), $total_dead];
385
        }
386
387
        $colors = $this->format->interpolateRgb($color_living, $color_dead, count($data) - 1);
388
389
        return view('statistics/other/charts/pie', [
390
            'title'            => null,
391
            'data'             => $data,
392
            'colors'           => $colors,
393
            'labeledValueText' => 'percentage',
394
            'language'         => I18N::languageTag(),
395
        ]);
396
    }
397
398
    public function chartNoChildrenFamilies(): string
399
    {
400
        $data = [
401
            [
402
                I18N::translate('Century'),
403
                I18N::translate('Total'),
404
            ],
405
        ];
406
407
        $records = DB::table('families')
408
            ->selectRaw('ROUND((d_year + 49) / 100, 0) AS century')
409
            ->selectRaw('COUNT(*) AS total')
410
            ->join('dates', static function (JoinClause $join): void {
411
                $join->on('d_file', '=', 'f_file')
412
                    ->on('d_gid', '=', 'f_id');
413
            })
414
            ->where('f_file', '=', $this->tree->id())
415
            ->where('f_numchil', '=', 0)
416
            ->where('d_fact', '=', 'MARR')
417
            ->whereIn('d_type', ['@#DGREGORIAN@', '@#DJULIAN@'])
418
            ->groupBy(['century'])
419
            ->orderBy('century')
420
            ->get()
421
            ->map(static fn (object $row): object => (object) [
422
                'century' => (int) $row->century,
423
                'total'   => (int) $row->total,
424
            ])
425
            ->all();
426
427
        $total = 0;
428
429
        foreach ($records as $record) {
430
            $total += $record->total;
431
432
            $data[] = [
433
                $this->format->century($record->century),
434
                $record->total,
435
            ];
436
        }
437
438
        $families_with_no_children = $this->data->countFamiliesWithNoChildren();
439
440
        if ($families_with_no_children - $total > 0) {
441
            $data[] = [
442
                I18N::translateContext('unknown century', 'Unknown'),
443
                $families_with_no_children - $total,
444
            ];
445
        }
446
447
        $chart_title   = I18N::translate('Number of families without children');
448
        $chart_options = [
449
            'title'    => $chart_title,
450
            'subtitle' => '',
451
            'legend'   => [
452
                'position' => 'none',
453
            ],
454
            'vAxis'    => [
455
                'title' => I18N::translate('Total families'),
456
            ],
457
            'hAxis'    => [
458
                'showTextEvery' => 1,
459
                'slantedText'   => false,
460
                'title'         => I18N::translate('Century'),
461
            ],
462
            'colors'   => [
463
                '#84beff',
464
            ],
465
        ];
466
467
        return view('statistics/other/charts/column', [
468
            'data'          => $data,
469
            'chart_options' => $chart_options,
470
            'chart_title'   => $chart_title,
471
            'language'      => I18N::languageTag(),
472
        ]);
473
    }
474
475
    public function chartSex(
476
        string $color_female = '#ffd1dc',
477
        string $color_male = '#84beff',
478
        string $color_unknown = '#777777',
479
        string $color_other = '#777777'
480
    ): string {
481
        $data = [
482
            [I18N::translate('Males'), $this->data->countIndividualsBySex('M')],
483
            [I18N::translate('Females'), $this->data->countIndividualsBySex('F')],
484
            [I18N::translate('Unknown'), $this->data->countIndividualsBySex('U')],
485
            [I18N::translate('Other'), $this->data->countIndividualsBySex('X')],
486
        ];
487
488
        return $this->format->pieChart(
489
            $data,
490
            [$color_male, $color_female, $color_unknown, $color_other],
491
            I18N::translate('Sex'),
492
            I18N::translate('Sex'),
493
            I18N::translate('Total'),
494
            true
495
        );
496
    }
497
498
    public function commonBirthPlacesList(string $limit = '10'): string
499
    {
500
        return view('statistics/other/top10-list', ['records' => $this->data->countPlacesForIndividuals('BIRT', (int) $limit)]);
501
    }
502
503
    public function commonCountriesList(string $limit = '10'): string
504
    {
505
        return view('statistics/other/top10-list', ['records' => $this->data->countCountries((int) $limit)]);
506
    }
507
508
    public function commonDeathPlacesList(string $limit = '10'): string
509
    {
510
        return view('statistics/other/top10-list', ['records' => $this->data->countPlacesForIndividuals('DEAT', (int) $limit)]);
511
    }
512
513
    public function commonGiven(string $threshold = '1', string $limit = '10'): string
514
    {
515
        return $this->data->commonGivenNames('ALL', (int) $threshold, (int) $limit)
516
            ->mapWithKeys(static fn (int $value, int|string $key): array => [
517
                $key => '<bdi>' . e($key) . '</bdi>',
518
            ])
519
            ->implode(I18N::$list_separator);
520
    }
521
522
    public function commonGivenFemale(string $threshold = '1', string $limit = '10'): string
523
    {
524
        return $this->data->commonGivenNames('F', (int) $threshold, (int) $limit)
525
            ->mapWithKeys(static fn (int $value, int|string $key): array => [
526
                $key => '<bdi>' . e($key) . '</bdi>',
527
            ])
528
            ->implode(I18N::$list_separator);
529
    }
530
531
    public function commonGivenFemaleList(string $threshold = '1', string $limit = '10'): string
532
    {
533
        return view('lists/given-names-list', [
534
            'given_names' => $this->data->commonGivenNames('F', (int) $threshold, (int) $limit)->all(),
535
            'show_totals' => false,
536
        ]);
537
    }
538
539
    public function commonGivenFemaleListTotals(string $threshold = '1', string $limit = '10'): string
540
    {
541
        return view('lists/given-names-list', [
542
            'given_names' => $this->data->commonGivenNames('F', (int) $threshold, (int) $limit)->all(),
543
            'show_totals' => true,
544
        ]);
545
    }
546
547
    public function commonGivenFemaleTable(string $threshold = '1', string $limit = '10'): string
548
    {
549
        return view('lists/given-names-table', [
550
            'given_names' => $this->data->commonGivenNames('F', (int) $threshold, (int) $limit)->all(),
551
            'order'       => [[1, 'desc']],
552
        ]);
553
    }
554
555
    public function commonGivenFemaleTotals(string $threshold = '1', string $limit = '10'): string
556
    {
557
        return $this->data->commonGivenNames('F', (int) $threshold, (int) $limit)
558
            ->mapWithKeys(static fn (int $value, int|string $key): array => [
559
                $key => '<bdi>' . e($key) . '</bdi> (' . I18N::number($value) . ')',
560
            ])
561
            ->implode(I18N::$list_separator);
562
    }
563
564
    public function commonGivenList(string $threshold = '1', string $limit = '10'): string
565
    {
566
        return view('lists/given-names-list', [
567
            'given_names' => $this->data->commonGivenNames('ALL', (int) $threshold, (int) $limit)->all(),
568
            'show_totals' => false,
569
        ]);
570
    }
571
572
    public function commonGivenListTotals(string $threshold = '1', string $limit = '10'): string
573
    {
574
        return view('lists/given-names-list', [
575
            'given_names' => $this->data->commonGivenNames('ALL', (int) $threshold, (int) $limit)->all(),
576
            'show_totals' => true,
577
        ]);
578
    }
579
580
    public function commonGivenMale(string $threshold = '1', string $limit = '10'): string
581
    {
582
        return $this->data->commonGivenNames('M', (int) $threshold, (int) $limit)
583
            ->mapWithKeys(static fn (int $value, int|string $key): array => [
584
                $key => '<bdi>' . e($key) . '</bdi>',
585
            ])
586
            ->implode(I18N::$list_separator);
587
    }
588
589
    public function commonGivenMaleList(string $threshold = '1', string $limit = '10'): string
590
    {
591
        return view('lists/given-names-list', [
592
            'given_names' => $this->data->commonGivenNames('M', (int) $threshold, (int) $limit)->all(),
593
            'show_totals' => false,
594
        ]);
595
    }
596
597
    public function commonGivenMaleListTotals(string $threshold = '1', string $limit = '10'): string
598
    {
599
        return view('lists/given-names-list', [
600
            'given_names' => $this->data->commonGivenNames('M', (int) $threshold, (int) $limit)->all(),
601
            'show_totals' => true,
602
        ]);
603
    }
604
605
    public function commonGivenMaleTable(string $threshold = '1', string $limit = '10'): string
606
    {
607
        return view('lists/given-names-table', [
608
            'given_names' => $this->data->commonGivenNames('M', (int) $threshold, (int) $limit)->all(),
609
            'order'       => [[1, 'desc']],
610
        ]);
611
    }
612
613
    public function commonGivenMaleTotals(string $threshold = '1', string $limit = '10'): string
614
    {
615
        return $this->data->commonGivenNames('M', (int) $threshold, (int) $limit)
616
            ->mapWithKeys(static fn (int $value, int|string $key): array => [
617
                $key => '<bdi>' . e($key) . '</bdi> (' . I18N::number($value) . ')',
618
            ])
619
            ->implode(I18N::$list_separator);
620
    }
621
622
    public function commonGivenOther(string $threshold = '1', string $limit = '10'): string
623
    {
624
        return $this->data->commonGivenNames('X', (int) $threshold, (int) $limit)
625
            ->mapWithKeys(static fn (int $value, int|string $key): array => [
626
                $key => '<bdi>' . e($key) . '</bdi>',
627
            ])
628
            ->implode(I18N::$list_separator);
629
    }
630
631
    public function commonGivenOtherList(string $threshold = '1', string $limit = '10'): string
632
    {
633
        return view('lists/given-names-list', [
634
            'given_names' => $this->data->commonGivenNames('X', (int) $threshold, (int) $limit)->all(),
635
            'show_totals' => false,
636
        ]);
637
    }
638
639
    public function commonGivenOtherListTotals(string $threshold = '1', string $limit = '10'): string
640
    {
641
        return view('lists/given-names-list', [
642
            'given_names' => $this->data->commonGivenNames('X', (int) $threshold, (int) $limit)->all(),
643
            'show_totals' => true,
644
        ]);
645
    }
646
647
    public function commonGivenOtherTable(string $threshold = '1', string $limit = '10'): string
648
    {
649
        return view('lists/given-names-table', [
650
            'given_names' => $this->data->commonGivenNames('X', (int) $threshold, (int) $limit)->all(),
651
            'order'       => [[1, 'desc']],
652
        ]);
653
    }
654
655
    public function commonGivenOtherTotals(string $threshold = '1', string $limit = '10'): string
656
    {
657
        return $this->data->commonGivenNames('X', (int) $threshold, (int) $limit)
658
            ->mapWithKeys(static fn (int $value, int|string $key): array => [
659
                $key => '<bdi>' . e($key) . '</bdi> (' . I18N::number($value) . ')',
660
            ])
661
            ->implode(I18N::$list_separator);
662
    }
663
664
    public function commonGivenTable(string $threshold = '1', string $limit = '10'): string
665
    {
666
        return view('lists/given-names-table', [
667
            'given_names' => $this->data->commonGivenNames('ALL', (int) $threshold, (int) $limit)->all(),
668
            'order'       => [[1, 'desc']],
669
        ]);
670
    }
671
672
    public function commonGivenTotals(string $threshold = '1', string $limit = '10'): string
673
    {
674
        return $this->data->commonGivenNames('ALL', (int) $threshold, (int) $limit)
675
            ->mapWithKeys(static fn (int $value, int|string $key): array => [
676
                $key => '<bdi>' . e($key) . '</bdi> (' . I18N::number($value) . ')',
677
            ])
678
            ->implode(I18N::$list_separator);
679
    }
680
681
    public function commonGivenUnknown(string $threshold = '1', string $limit = '10'): string
682
    {
683
        return $this->data->commonGivenNames('U', (int) $threshold, (int) $limit)
684
            ->mapWithKeys(static fn (int $value, int|string $key): array => [
685
                $key => '<bdi>' . e($key) . '</bdi>',
686
            ])
687
            ->implode(I18N::$list_separator);
688
    }
689
690
    public function commonGivenUnknownList(string $threshold = '1', string $limit = '10'): string
691
    {
692
        return view('lists/given-names-list', [
693
            'given_names' => $this->data->commonGivenNames('U', (int) $threshold, (int) $limit)->all(),
694
            'show_totals' => false,
695
        ]);
696
    }
697
698
    public function commonGivenUnknownListTotals(string $threshold = '1', string $limit = '10'): string
699
    {
700
        return view('lists/given-names-list', [
701
            'given_names' => $this->data->commonGivenNames('U', (int) $threshold, (int) $limit)->all(),
702
            'show_totals' => true,
703
        ]);
704
    }
705
706
    public function commonGivenUnknownTable(string $threshold = '1', string $limit = '10'): string
707
    {
708
        return view('lists/given-names-table', [
709
            'given_names' => $this->data->commonGivenNames('U', (int) $threshold, (int) $limit)->all(),
710
            'order'       => [[1, 'desc']],
711
        ]);
712
    }
713
714
    public function commonGivenUnknownTotals(string $threshold = '1', string $limit = '10'): string
715
    {
716
        return $this->data->commonGivenNames('U', (int) $threshold, (int) $limit)
717
            ->mapWithKeys(static fn (int $value, int|string $key): array => [
718
                $key => '<bdi>' . e($key) . '</bdi> (' . I18N::number($value) . ')',
719
            ])
720
            ->implode(I18N::$list_separator);
721
    }
722
723
    public function commonMarriagePlacesList(string $limit = '10'): string
724
    {
725
        return view('statistics/other/top10-list', ['records' => $this->data->countPlacesForFamilies('MARR', (int) $limit)]);
726
    }
727
728
    public function commonSurnames(string $threshold = '1', string $limit = '10', string $sort = 'alpha'): string
729
    {
730
        return $this->data->commonSurnamesQuery('nolist', false, (int) $threshold, (int) $limit, $sort);
731
    }
732
733
    public function commonSurnamesList(string $threshold = '1', string $limit = '10', string $sort = 'alpha'): string
734
    {
735
        return $this->data->commonSurnamesQuery('list', false, (int) $threshold, (int) $limit, $sort);
736
    }
737
738
    public function commonSurnamesListTotals(string $threshold = '1', string $limit = '10', string $sort = 'count'): string
739
    {
740
        return $this->data->commonSurnamesQuery('list', true, (int) $threshold, (int) $limit, $sort);
741
    }
742
743
    public function commonSurnamesTotals(string $threshold = '1', string $limit = '10', string $sort = 'count'): string
744
    {
745
        return $this->data->commonSurnamesQuery('nolist', true, (int) $threshold, (int) $limit, $sort);
746
    }
747
748
    public function contactGedcom(): string
749
    {
750
        $user_id = (int) $this->tree->getPreference('CONTACT_USER_ID');
751
        $user    = $this->user_service->find($user_id);
752
753
        if ($user instanceof User) {
754
            $request = Registry::container()->get(ServerRequestInterface::class);
755
756
            return $this->user_service->contactLink($user, $request);
757
        }
758
759
        return '';
760
    }
761
762
    public function contactWebmaster(): string
763
    {
764
        $user_id = (int) $this->tree->getPreference('WEBMASTER_USER_ID');
765
        $user    = $this->user_service->find($user_id);
766
767
        if ($user instanceof User) {
768
            $request = Registry::container()->get(ServerRequestInterface::class);
769
770
            return $this->user_service->contactLink($user, $request);
771
        }
772
773
        return '';
774
    }
775
776
    public function embedTags(string $text): string
777
    {
778
        return strtr($text, $this->getTags($text));
779
    }
780
781
    public function firstBirth(): string
782
    {
783
        return $this->data->firstEventRecord(['BIRT'], true);
784
    }
785
786
    public function firstBirthName(): string
787
    {
788
        return $this->data->firstEventName(['BIRT'], true);
789
    }
790
791
    public function firstBirthPlace(): string
792
    {
793
        return $this->data->firstEventPlace(['BIRT'], true);
794
    }
795
796
    public function firstBirthYear(): string
797
    {
798
        return $this->data->firstEventYear(['BIRT'], true);
799
    }
800
801
    public function firstDeath(): string
802
    {
803
        return $this->data->firstEventRecord(['DEAT'], true);
804
    }
805
806
    public function firstDeathName(): string
807
    {
808
        return $this->data->firstEventName(['DEAT'], true);
809
    }
810
811
    public function firstDeathPlace(): string
812
    {
813
        return $this->data->firstEventPlace(['DEAT'], true);
814
    }
815
816
    public function firstDeathYear(): string
817
    {
818
        return $this->data->firstEventYear(['DEAT'], true);
819
    }
820
821
    public function firstDivorce(): string
822
    {
823
        return $this->data->firstEventRecord(['DIV'], true);
824
    }
825
826
    public function firstDivorceName(): string
827
    {
828
        return $this->data->firstEventName(['DIV'], true);
829
    }
830
831
    public function firstDivorcePlace(): string
832
    {
833
        return $this->data->firstEventPlace(['DIV'], true);
834
    }
835
836
    public function firstDivorceYear(): string
837
    {
838
        return $this->data->firstEventYear(['DIV'], true);
839
    }
840
841
    public function firstEvent(): string
842
    {
843
        return $this->data->firstEventRecord([], true);
844
    }
845
846
    public function firstEventName(): string
847
    {
848
        return $this->data->firstEventName([], true);
849
    }
850
851
    public function firstEventPlace(): string
852
    {
853
        return $this->data->firstEventPlace([], true);
854
    }
855
856
    public function firstEventType(): string
857
    {
858
        return $this->data->firstEventType([], true);
859
    }
860
861
    public function firstEventYear(): string
862
    {
863
        return $this->data->firstEventYear([], true);
864
    }
865
866
    public function firstMarriage(): string
867
    {
868
        return $this->data->firstEventRecord(['MARR'], true);
869
    }
870
871
    public function firstMarriageName(): string
872
    {
873
        return $this->data->firstEventName(['MARR'], true);
874
    }
875
876
    public function firstMarriagePlace(): string
877
    {
878
        return $this->data->firstEventPlace(['MARR'], true);
879
    }
880
881
    public function firstMarriageYear(): string
882
    {
883
        return $this->data->firstEventYear(['MARR'], true);
884
    }
885
886
    public function gedcomCreatedSoftware(): string
887
    {
888
        $head = Registry::headerFactory()->make('HEAD', $this->tree);
889
890
        if ($head instanceof Header) {
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Header was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
891
            $sour = $head->facts(['SOUR'])->first();
892
893
            if ($sour instanceof Fact) {
894
                return $sour->attribute('NAME');
895
            }
896
        }
897
898
        return '';
899
    }
900
901
    public function gedcomCreatedVersion(): string
902
    {
903
        $head = Registry::headerFactory()->make('HEAD', $this->tree);
904
905
        if ($head instanceof Header) {
906
            $sour = $head->facts(['SOUR'])->first();
907
908
            if ($sour instanceof Fact) {
909
                $version = $sour->attribute('VERS');
910
911
                if (str_contains($version, 'Family Tree Maker ')) {
912
                    $p       = strpos($version, '(') + 1;
913
                    $p2      = strpos($version, ')');
914
                    $version = substr($version, $p, $p2 - $p);
915
                }
916
917
                // Fix EasyTree version
918
                if ($sour->value() === 'EasyTree') {
919
                    $version = substr($version, 1);
920
                }
921
922
                return $version;
923
            }
924
        }
925
926
        return '';
927
    }
928
929
    public function gedcomDate(): string
930
    {
931
        $head = Registry::headerFactory()->make('HEAD', $this->tree);
932
933
        if ($head instanceof Header) {
934
            $fact = $head->facts(['DATE'])->first();
935
936
            if ($fact instanceof Fact) {
937
                try {
938
                    return Registry::timestampFactory()->fromString($fact->value(), 'j M Y')->isoFormat('LL');
939
                } catch (InvalidArgumentException) {
940
                    // HEAD:DATE invalid.
941
                }
942
            }
943
        }
944
945
        return '';
946
    }
947
948
    public function gedcomFavorites(): string
949
    {
950
        return $this->callBlock('gedcom_favorites');
951
    }
952
953
    public function gedcomFilename(): string
954
    {
955
        return $this->tree->name();
956
    }
957
958
    public function gedcomRootId(): string
959
    {
960
        return $this->tree->getPreference('PEDIGREE_ROOT_ID');
961
    }
962
963
    public function gedcomTitle(): string
964
    {
965
        return e($this->tree->title());
966
    }
967
968
    public function gedcomUpdated(): string
969
    {
970
        $row = DB::table('change')
971
            ->where('gedcom_id', '=', $this->tree->id())
972
            ->where('status', '=', 'accepted')
973
            ->orderBy('change_id', 'DESC')
974
            ->select(['change_time'])
975
            ->first();
976
977
        if ($row === null) {
978
            return $this->gedcomDate();
979
        }
980
981
        return Registry::timestampFactory()->fromString($row->change_time)->isoFormat('LL');
982
    }
983
984
    public function getAllTagsTable(): string
985
    {
986
        try {
987
            $class = new ReflectionClass($this);
988
989
            $public_methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
990
991
            $exclude = ['embedTags', 'getAllTagsTable'];
992
993
            $examples = Collection::make($public_methods)
0 ignored issues
show
Bug introduced by
$public_methods of type ReflectionMethod[] is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $items of Illuminate\Support\Collection::make(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

993
            $examples = Collection::make(/** @scrutinizer ignore-type */ $public_methods)
Loading history...
994
                ->filter(static fn (ReflectionMethod $method): bool => !in_array($method->getName(), $exclude, true))
995
                ->filter(static fn (ReflectionMethod $method): bool => $method->getReturnType() instanceof ReflectionNamedType && $method->getReturnType()->getName() === 'string')
996
                ->sort(static fn (ReflectionMethod $x, ReflectionMethod $y): int => $x->getName() <=> $y->getName())
997
                ->map(function (ReflectionMethod $method): string {
998
                    $tag = $method->getName();
999
1000
                    return '<dt>#' . $tag . '#</dt><dd>' . $this->$tag() . '</dd>';
1001
                });
1002
1003
            return '<dl>' . $examples->implode('') . '</dl>';
1004
        } catch (ReflectionException $ex) {
1005
            return $ex->getMessage();
1006
        }
1007
    }
1008
1009
    public function getCommonSurname(): string
1010
    {
1011
        $top_surname = $this->data->commonSurnames(1, 0, 'count');
1012
1013
        return implode(I18N::$list_separator, array_keys(array_shift($top_surname) ?? []));
1014
    }
1015
1016
    /**
1017
     * @return array<string,string>
1018
     */
1019
    private function getTags(string $text): array
1020
    {
1021
        $tags    = [];
1022
        $matches = [];
1023
1024
        preg_match_all('/#([^#\n]+)(?=#)/', $text, $matches, PREG_SET_ORDER);
1025
1026
        foreach ($matches as $match) {
1027
            $params = explode(':', $match[1]);
1028
            $method = array_shift($params);
1029
1030
            if (method_exists($this, $method)) {
1031
                $tags[$match[0] . '#'] = $this->$method(...$params);
1032
            }
1033
        }
1034
1035
        return $tags;
1036
    }
1037
1038
    public function hitCount(): string
1039
    {
1040
        return $this->format->hitCount($this->data->countHits('index.php', 'gedcom:' . $this->tree->id()));
1041
    }
1042
1043
    public function hitCountFam(string $xref = ''): string
1044
    {
1045
        return $this->format->hitCount($this->data->countHits('family.php', $xref));
1046
    }
1047
1048
    public function hitCountIndi(string $xref = ''): string
1049
    {
1050
        return $this->format->hitCount($this->data->countHits('individual.php', $xref));
1051
    }
1052
1053
    public function hitCountNote(string $xref = ''): string
1054
    {
1055
        return $this->format->hitCount($this->data->countHits('note.php', $xref));
1056
    }
1057
1058
    public function hitCountObje(string $xref = ''): string
1059
    {
1060
        return $this->format->hitCount($this->data->countHits('mediaviewer.php', $xref));
1061
    }
1062
1063
    public function hitCountRepo(string $xref = ''): string
1064
    {
1065
        return $this->format->hitCount($this->data->countHits('repo.php', $xref));
1066
    }
1067
1068
    public function hitCountSour(string $xref = ''): string
1069
    {
1070
        return $this->format->hitCount($this->data->countHits('source.php', $xref));
1071
    }
1072
1073
    public function hitCountUser(): string
1074
    {
1075
        return $this->format->hitCount($this->data->countHits('index.php', 'user:' . Auth::id()));
1076
    }
1077
1078
    public function largestFamily(): string
1079
    {
1080
        $family = $this->data->familiesWithTheMostChildren(1)[0]->family ?? null;
1081
1082
        if ($family === null) {
1083
            return $this->format->missing();
1084
        }
1085
1086
        return $family->formatList();
1087
    }
1088
1089
    public function largestFamilyName(): string
1090
    {
1091
        return $this->format->record($this->data->familiesWithTheMostChildren(1)[0]->family ?? null);
1092
    }
1093
1094
    public function largestFamilySize(): string
1095
    {
1096
        return I18N::number($this->data->familiesWithTheMostChildren(1)[0]->children ?? 0);
1097
    }
1098
1099
    public function lastBirth(): string
1100
    {
1101
        return $this->data->firstEventRecord(['BIRT'], false);
1102
    }
1103
1104
    public function lastBirthName(): string
1105
    {
1106
        return $this->data->firstEventName(['BIRT'], false);
1107
    }
1108
1109
    public function lastBirthPlace(): string
1110
    {
1111
        return $this->data->firstEventPlace(['BIRT'], false);
1112
    }
1113
1114
    public function lastBirthYear(): string
1115
    {
1116
        return $this->data->firstEventYear(['BIRT'], false);
1117
    }
1118
1119
    public function lastDeath(): string
1120
    {
1121
        return $this->data->firstEventRecord(['DEAT'], false);
1122
    }
1123
1124
    public function lastDeathName(): string
1125
    {
1126
        return $this->data->firstEventName(['DEAT'], false);
1127
    }
1128
1129
    public function lastDeathPlace(): string
1130
    {
1131
        return $this->data->firstEventPlace(['DEAT'], false);
1132
    }
1133
1134
    public function lastDeathYear(): string
1135
    {
1136
        return $this->data->firstEventYear(['DEAT'], false);
1137
    }
1138
1139
    public function lastDivorce(): string
1140
    {
1141
        return $this->data->firstEventRecord(['DIV'], false);
1142
    }
1143
1144
    public function lastDivorceName(): string
1145
    {
1146
        return $this->data->firstEventName(['DIV'], true);
1147
    }
1148
1149
    public function lastDivorcePlace(): string
1150
    {
1151
        return $this->data->firstEventPlace(['DIV'], true);
1152
    }
1153
1154
    public function lastDivorceYear(): string
1155
    {
1156
        return $this->data->firstEventYear(['DIV'], true);
1157
    }
1158
1159
    public function lastEvent(): string
1160
    {
1161
        return $this->data->firstEventRecord([], false);
1162
    }
1163
1164
    public function lastEventName(): string
1165
    {
1166
        return $this->data->firstEventName([], false);
1167
    }
1168
1169
    public function lastEventPlace(): string
1170
    {
1171
        return $this->data->firstEventPlace([], false);
1172
    }
1173
1174
    public function lastEventType(): string
1175
    {
1176
        return $this->data->firstEventType([], false);
1177
    }
1178
1179
    public function lastEventYear(): string
1180
    {
1181
        return $this->data->firstEventYear([], false);
1182
    }
1183
1184
    public function lastMarriage(): string
1185
    {
1186
        return $this->data->firstEventRecord(['MARR'], false);
1187
    }
1188
1189
    public function lastMarriageName(): string
1190
    {
1191
        return $this->data->firstEventName(['MARR'], false);
1192
    }
1193
1194
    public function lastMarriagePlace(): string
1195
    {
1196
        return $this->data->firstEventPlace(['MARR'], false);
1197
    }
1198
1199
    public function lastMarriageYear(): string
1200
    {
1201
        return $this->data->firstEventYear(['MARR'], false);
1202
    }
1203
1204
    public function latestUserFullName(): string
1205
    {
1206
        $user = $this->user_service->find($this->data->latestUserId()) ?? Auth::user();
1207
1208
        return e($user->realName());
1209
    }
1210
1211
    public function latestUserId(): string
1212
    {
1213
        $user = $this->user_service->find($this->data->latestUserId()) ?? Auth::user();
1214
1215
        return (string) $user->id();
1216
    }
1217
1218
    public function latestUserLoggedin(string|null $yes = null, string|null $no = null): string
1219
    {
1220
        if ($this->data->isUserLoggedIn($this->data->latestUserId())) {
1221
            return $yes ?? I18N::translate('Yes');
1222
        }
1223
1224
        return $no ?? I18N::translate('No');
1225
    }
1226
1227
    public function latestUserName(): string
1228
    {
1229
        $user = $this->user_service->find($this->data->latestUserId()) ?? Auth::user();
1230
1231
        return e($user->userName());
1232
    }
1233
1234
    public function latestUserRegDate(string|null $format = null): string
1235
    {
1236
        $format    ??= I18N::dateFormat();
1237
        $user      = $this->user_service->find($this->data->latestUserId()) ?? Auth::user();
1238
        $timestamp = (int) $user->getPreference(UserInterface::PREF_TIMESTAMP_REGISTERED);
1239
1240
        if ($timestamp === 0) {
1241
            return I18N::translate('Never');
1242
        }
1243
1244
        return Registry::timestampFactory()->make($timestamp)->format(strtr($format, ['%' => '']));
1245
    }
1246
1247
    public function latestUserRegTime(string|null $format = null): string
1248
    {
1249
        $format    ??= I18N::timeFormat();
1250
        $user      = $this->user_service->find($this->data->latestUserId()) ?? Auth::user();
1251
        $timestamp = (int) $user->getPreference(UserInterface::PREF_TIMESTAMP_REGISTERED);
1252
1253
        if ($timestamp === 0) {
1254
            return I18N::translate('Never');
1255
        }
1256
1257
        return Registry::timestampFactory()->make($timestamp)->format(strtr($format, ['%' => '']));
1258
    }
1259
1260
    public function longestLife(): string
1261
    {
1262
        $row = $this->data->longlifeQuery('ALL');
1263
1264
        if ($row === null) {
1265
            return '';
1266
        }
1267
1268
        return $row->individual->formatList();
1269
    }
1270
1271
    public function longestLifeAge(): string
1272
    {
1273
        $row = $this->data->longlifeQuery('ALL');
1274
1275
        if ($row === null) {
1276
            return '';
1277
        }
1278
1279
        return I18N::number((int) ($row->days / 365.25));
1280
    }
1281
1282
    public function longestLifeFemale(): string
1283
    {
1284
        $row = $this->data->longlifeQuery('F');
1285
1286
        if ($row === null) {
1287
            return '';
1288
        }
1289
1290
        return $row->individual->formatList();
1291
    }
1292
1293
    public function longestLifeFemaleAge(): string
1294
    {
1295
        $row = $this->data->longlifeQuery('F');
1296
1297
        if ($row === null) {
1298
            return '';
1299
        }
1300
1301
        return I18N::number((int) ($row->days / 365.25));
1302
    }
1303
1304
    public function longestLifeFemaleName(): string
1305
    {
1306
        return $this->format->record($this->data->longlifeQuery('F')->individual ?? null);
1307
    }
1308
1309
    public function longestLifeMale(): string
1310
    {
1311
        $row = $this->data->longlifeQuery('M');
1312
1313
        if ($row === null) {
1314
            return '';
1315
        }
1316
1317
        return $row->individual->formatList();
1318
    }
1319
1320
    public function longestLifeMaleAge(): string
1321
    {
1322
        $row = $this->data->longlifeQuery('M');
1323
1324
        if ($row === null) {
1325
            return '';
1326
        }
1327
1328
        return I18N::number((int) ($row->days / 365.25));
1329
    }
1330
1331
    public function longestLifeMaleName(): string
1332
    {
1333
        return $this->format->record($this->data->longlifeQuery('M')->individual ?? null);
1334
    }
1335
1336
    public function longestLifeName(): string
1337
    {
1338
        return $this->format->record($this->data->longlifeQuery('ALL')->individual ?? null);
1339
    }
1340
1341
    public function minAgeOfMarriage(): string
1342
    {
1343
        return $this->data->ageOfMarriageQuery('age', 'ASC', 1);
1344
    }
1345
1346
    public function minAgeOfMarriageFamilies(string $limit = '10'): string
1347
    {
1348
        return $this->data->ageOfMarriageQuery('nolist', 'ASC', (int) $limit);
1349
    }
1350
1351
    public function minAgeOfMarriageFamiliesList(string $limit = '10'): string
1352
    {
1353
        return $this->data->ageOfMarriageQuery('list', 'ASC', (int) $limit);
1354
    }
1355
1356
    public function minAgeOfMarriageFamily(): string
1357
    {
1358
        return $this->data->ageOfMarriageQuery('name', 'ASC', 1);
1359
    }
1360
1361
    public function noChildrenFamilies(): string
1362
    {
1363
        return I18N::number($this->data->countFamiliesWithNoChildren());
1364
    }
1365
1366
    public function noChildrenFamiliesList(string $type = 'list'): string
1367
    {
1368
        return $this->data->noChildrenFamiliesList($type);
1369
    }
1370
1371
    public function oldestFather(): string
1372
    {
1373
        return $this->data->parentsQuery('full', 'DESC', 'M', false);
1374
    }
1375
1376
    public function oldestFatherAge(string $show_years = '0'): string
1377
    {
1378
        return $this->data->parentsQuery('age', 'DESC', 'M', (bool) $show_years);
1379
    }
1380
1381
    public function oldestFatherName(): string
1382
    {
1383
        return $this->data->parentsQuery('name', 'DESC', 'M', false);
1384
    }
1385
1386
    public function oldestMarriageFemale(): string
1387
    {
1388
        return $this->data->marriageQuery('full', 'DESC', 'F', false);
1389
    }
1390
1391
    public function oldestMarriageFemaleAge(string $show_years = '0'): string
1392
    {
1393
        return $this->data->marriageQuery('age', 'DESC', 'F', (bool) $show_years);
1394
    }
1395
1396
    public function oldestMarriageFemaleName(): string
1397
    {
1398
        return $this->data->marriageQuery('name', 'DESC', 'F', false);
1399
    }
1400
1401
    public function oldestMarriageMale(): string
1402
    {
1403
        return $this->data->marriageQuery('full', 'DESC', 'M', false);
1404
    }
1405
1406
    public function oldestMarriageMaleAge(string $show_years = '0'): string
1407
    {
1408
        return $this->data->marriageQuery('age', 'DESC', 'M', (bool) $show_years);
1409
    }
1410
1411
    public function oldestMarriageMaleName(): string
1412
    {
1413
        return $this->data->marriageQuery('name', 'DESC', 'M', false);
1414
    }
1415
1416
    public function oldestMother(): string
1417
    {
1418
        return $this->data->parentsQuery('full', 'DESC', 'F', false);
1419
    }
1420
1421
    public function oldestMotherAge(string $show_years = '0'): string
1422
    {
1423
        return $this->data->parentsQuery('age', 'DESC', 'F', (bool) $show_years);
1424
    }
1425
1426
    public function oldestMotherName(): string
1427
    {
1428
        return $this->data->parentsQuery('name', 'DESC', 'F', false);
1429
    }
1430
1431
    public function serverDate(): string
1432
    {
1433
        return Registry::timestampFactory()->now(new SiteUser())->format(strtr(I18N::dateFormat(), ['%' => '']));
1434
    }
1435
1436
    public function serverTime(): string
1437
    {
1438
        return Registry::timestampFactory()->now(new SiteUser())->format(strtr(I18N::timeFormat(), ['%' => '']));
1439
    }
1440
1441
    public function serverTime24(): string
1442
    {
1443
        return Registry::timestampFactory()->now(new SiteUser())->format('G:i');
1444
    }
1445
1446
    public function serverTimezone(): string
1447
    {
1448
        return Registry::timestampFactory()->now(new SiteUser())->format('T');
1449
    }
1450
1451
    public function statsAge(): string
1452
    {
1453
        $records = $this->data->statsAge();
1454
1455
        $out = [];
1456
1457
        foreach ($records as $record) {
1458
            $out[$record->century][$record->sex] = $record->age;
1459
        }
1460
1461
        $data = [
1462
            [
1463
                I18N::translate('Century'),
1464
                I18N::translate('Males'),
1465
                I18N::translate('Females'),
1466
                I18N::translate('Average age'),
1467
            ]
1468
        ];
1469
1470
        foreach ($out as $century => $values) {
1471
            $female_age  = $values['F'] ?? 0;
1472
            $male_age    = $values['M'] ?? 0;
1473
            $average_age = ($female_age + $male_age) / 2.0;
1474
1475
            $data[] = [
1476
                $this->format->century($century),
1477
                round($male_age, 1),
1478
                round($female_age, 1),
1479
                round($average_age, 1),
1480
            ];
1481
        }
1482
1483
        $chart_title   = I18N::translate('Average age related to death century');
1484
        $chart_options = [
1485
            'title' => $chart_title,
1486
            'subtitle' => I18N::translate('Average age at death'),
1487
            'vAxis' => [
1488
                'title' => I18N::translate('Age'),
1489
            ],
1490
            'hAxis' => [
1491
                'showTextEvery' => 1,
1492
                'slantedText'   => false,
1493
                'title'         => I18N::translate('Century'),
1494
            ],
1495
            'colors' => [
1496
                '#84beff',
1497
                '#ffd1dc',
1498
                '#ff0000',
1499
            ],
1500
        ];
1501
1502
        return view('statistics/other/charts/combo', [
1503
            'data'          => $data,
1504
            'chart_options' => $chart_options,
1505
            'chart_title'   => $chart_title,
1506
            'language'      => I18N::languageTag(),
1507
        ]);
1508
    }
1509
1510
    public function statsBirth(string $color1 = 'ffffff', string $color2 = '84beff'): string
1511
    {
1512
        $data   = $this->data->countEventsByCentury('BIRT');
1513
        $colors = $this->format->interpolateRgb($color1, $color2, count($data));
1514
1515
        return $this->format->pieChart(
1516
            $data,
1517
            $colors,
1518
            I18N::translate('Births by century'),
1519
            I18N::translate('Century'),
1520
            I18N::translate('Total'),
1521
        );
1522
    }
1523
1524
    public function statsChildren(): string
1525
    {
1526
        $records = DB::table('families')
1527
            ->selectRaw('AVG(f_numchil) AS total')
1528
            ->selectRaw('ROUND((d_year + 49) / 100, 0) AS century')
1529
            ->join('dates', static function (JoinClause $join): void {
1530
                $join->on('d_file', '=', 'f_file')
1531
                    ->on('d_gid', '=', 'f_id');
1532
            })
1533
            ->where('f_file', '=', $this->tree->id())
1534
            ->where('d_julianday1', '<>', 0)
1535
            ->where('d_fact', '=', 'MARR')
1536
            ->whereIn('d_type', ['@#DGREGORIAN@', '@#DJULIAN@'])
1537
            ->groupBy(['century'])
1538
            ->orderBy('century')
1539
            ->get()
1540
            ->map(static fn (object $row): object => (object) [
1541
                'century' => (int) $row->century,
1542
                'total'   => (float) $row->total,
1543
            ]);
1544
1545
        $data = [
1546
            [
1547
                I18N::translate('Century'),
1548
                I18N::translate('Average number'),
1549
            ],
1550
        ];
1551
1552
        foreach ($records as $record) {
1553
            $data[] = [
1554
                $this->format->century($record->century),
1555
                round($record->total, 2),
1556
            ];
1557
        }
1558
1559
        $chart_title   = I18N::translate('Average number of children per family');
1560
        $chart_options = [
1561
            'title'    => $chart_title,
1562
            'subtitle' => '',
1563
            'legend'   => [
1564
                'position' => 'none',
1565
            ],
1566
            'vAxis'    => [
1567
                'title' => I18N::translate('Number of children'),
1568
            ],
1569
            'hAxis'    => [
1570
                'showTextEvery' => 1,
1571
                'slantedText'   => false,
1572
                'title'         => I18N::translate('Century'),
1573
            ],
1574
            'colors'   => [
1575
                '#84beff',
1576
            ],
1577
        ];
1578
1579
        return view('statistics/other/charts/column', [
1580
            'data'          => $data,
1581
            'chart_options' => $chart_options,
1582
            'chart_title'   => $chart_title,
1583
            'language'      => I18N::languageTag(),
1584
        ]);
1585
    }
1586
1587
    /**
1588
     * @return array<object{f_numchil:int,total:int}>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<object{f_numchil:int,total:int}> at position 2 could not be parsed: Expected '>' at position 2, but found 'object'.
Loading history...
1589
     */
1590
    public function statsChildrenQuery(int $year1 = 0, int $year2 = 0): array
1591
    {
1592
        return $this->data->statsChildrenQuery($year1, $year2);
1593
    }
1594
1595
    public function statsDeath(string $color1 = 'ffffff', string $color2 = '84beff'): string
1596
    {
1597
        $data   = $this->data->countEventsByCentury('DEAT');
1598
        $colors = $this->format->interpolateRgb($color1, $color2, count($data));
1599
1600
        return $this->format->pieChart(
1601
            $data,
1602
            $colors,
1603
            I18N::translate('Births by century'),
1604
            I18N::translate('Century'),
1605
            I18N::translate('Total'),
1606
        );
1607
    }
1608
1609
    public function statsDiv(string $color1 = 'ffffff', string $color2 = '84beff'): string
1610
    {
1611
        $data   = $this->data->countEventsByCentury('DIV');
1612
        $colors = $this->format->interpolateRgb($color1, $color2, count($data));
1613
1614
        return $this->format->pieChart(
1615
            $data,
1616
            $colors,
1617
            I18N::translate('Divorces by century'),
1618
            I18N::translate('Century'),
1619
            I18N::translate('Total'),
1620
        );
1621
    }
1622
1623
    public function statsMarr(string $color1 = 'ffffff', string $color2 = '84beff'): string
1624
    {
1625
        $data   = $this->data->countEventsByCentury('MARR');
1626
        $colors = $this->format->interpolateRgb($color1, $color2, count($data));
1627
1628
        return $this->format->pieChart(
1629
            $data,
1630
            $colors,
1631
            I18N::translate('Marriages by century'),
1632
            I18N::translate('Century'),
1633
            I18N::translate('Total'),
1634
        );
1635
    }
1636
1637
    public function statsMarrAge(): string
1638
    {
1639
        $out = [];
1640
1641
        $male = DB::table('dates as married')
1642
            ->select([
1643
                new Expression('AVG(' . DB::prefix('married.d_julianday2') . ' - ' . DB::prefix('birth.d_julianday1') . ' - 182.5) / 365.25 AS age'),
0 ignored issues
show
Bug introduced by
'AVG(' . Fisharebest\Web...182.5) / 365.25 AS age' of type string is incompatible with the type Illuminate\Database\Query\TValue expected by parameter $value of Illuminate\Database\Quer...pression::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1643
                new Expression(/** @scrutinizer ignore-type */ 'AVG(' . DB::prefix('married.d_julianday2') . ' - ' . DB::prefix('birth.d_julianday1') . ' - 182.5) / 365.25 AS age'),
Loading history...
1644
                new Expression('ROUND((' . DB::prefix('married.d_year') . ' + 49) / 100, 0) AS century'),
1645
                new Expression("'M' as sex"),
1646
            ])
1647
            ->join('families as fam', static function (JoinClause $join): void {
1648
                $join->on('fam.f_id', '=', 'married.d_gid')
1649
                    ->on('fam.f_file', '=', 'married.d_file');
1650
            })
1651
            ->join('dates as birth', static function (JoinClause $join): void {
1652
                $join->on('birth.d_gid', '=', 'fam.f_husb')
1653
                    ->on('birth.d_file', '=', 'fam.f_file');
1654
            })
1655
            ->whereIn('married.d_type', ['@#DGREGORIAN@', '@#DJULIAN@'])
1656
            ->where('married.d_file', '=', $this->tree->id())
1657
            ->where('married.d_fact', '=', 'MARR')
1658
            ->where('married.d_julianday1', '>', new Expression(DB::prefix('birth.d_julianday1')))
1659
            ->whereIn('birth.d_type', ['@#DGREGORIAN@', '@#DJULIAN@'])
1660
            ->where('birth.d_fact', '=', 'BIRT')
1661
            ->where('birth.d_julianday1', '<>', 0)
1662
            ->groupBy(['century', 'sex']);
1663
1664
        $female = DB::table('dates as married')
1665
            ->select([
1666
                new Expression('ROUND(AVG(' . DB::prefix('married.d_julianday2') . ' - ' . DB::prefix('birth.d_julianday1') . ' - 182.5) / 365.25, 1) AS age'),
1667
                new Expression('ROUND((' . DB::prefix('married.d_year') . ' + 49) / 100, 0) AS century'),
1668
                new Expression("'F' as sex"),
1669
            ])
1670
            ->join('families as fam', static function (JoinClause $join): void {
1671
                $join->on('fam.f_id', '=', 'married.d_gid')
1672
                    ->on('fam.f_file', '=', 'married.d_file');
1673
            })
1674
            ->join('dates as birth', static function (JoinClause $join): void {
1675
                $join->on('birth.d_gid', '=', 'fam.f_wife')
1676
                    ->on('birth.d_file', '=', 'fam.f_file');
1677
            })
1678
            ->whereIn('married.d_type', ['@#DGREGORIAN@', '@#DJULIAN@'])
1679
            ->where('married.d_file', '=', $this->tree->id())
1680
            ->where('married.d_fact', '=', 'MARR')
1681
            ->where('married.d_julianday1', '>', new Expression(DB::prefix('birth.d_julianday1')))
1682
            ->whereIn('birth.d_type', ['@#DGREGORIAN@', '@#DJULIAN@'])
1683
            ->where('birth.d_fact', '=', 'BIRT')
1684
            ->where('birth.d_julianday1', '<>', 0)
1685
            ->groupBy(['century', 'sex']);
1686
1687
        $records = $male->unionAll($female)
1688
            ->orderBy('century')
1689
            ->get()
1690
            ->map(static fn (object $row): object => (object) [
1691
                'age'     => (float) $row->age,
1692
                'century' => (int) $row->century,
1693
                'sex'     => $row->sex,
1694
            ]);
1695
1696
1697
        foreach ($records as $record) {
1698
            $out[$record->century][$record->sex] = $record->age;
1699
        }
1700
1701
        $data = [
1702
            [
1703
                I18N::translate('Century'),
1704
                I18N::translate('Males'),
1705
                I18N::translate('Females'),
1706
                I18N::translate('Average age'),
1707
            ],
1708
        ];
1709
1710
        foreach ($out as $century => $values) {
1711
            $female_age  = $values['F'] ?? 0;
1712
            $male_age    = $values['M'] ?? 0;
1713
            $average_age = ($female_age + $male_age) / 2.0;
1714
1715
            $data[] = [
1716
                $this->format->century($century),
1717
                round($male_age, 1),
1718
                round($female_age, 1),
1719
                round($average_age, 1),
1720
            ];
1721
        }
1722
1723
        $chart_title   = I18N::translate('Average age in century of marriage');
1724
        $chart_options = [
1725
            'title'    => $chart_title,
1726
            'subtitle' => I18N::translate('Average age at marriage'),
1727
            'vAxis'    => [
1728
                'title' => I18N::translate('Age'),
1729
            ],
1730
            'hAxis'    => [
1731
                'showTextEvery' => 1,
1732
                'slantedText'   => false,
1733
                'title'         => I18N::translate('Century'),
1734
            ],
1735
            'colors'   => [
1736
                '#84beff',
1737
                '#ffd1dc',
1738
                '#ff0000',
1739
            ],
1740
        ];
1741
1742
        return view('statistics/other/charts/combo', [
1743
            'data'          => $data,
1744
            'chart_options' => $chart_options,
1745
            'chart_title'   => $chart_title,
1746
            'language'      => I18N::languageTag(),
1747
        ]);
1748
    }
1749
1750
    /**
1751
     * @return array<object{f_id:string,d_gid:string,age:int}>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<object{f_id:string,d_gid:string,age:int}> at position 2 could not be parsed: Expected '>' at position 2, but found 'object'.
Loading history...
1752
     */
1753
    public function statsMarrAgeQuery(string $sex, int $year1 = 0, int $year2 = 0): array
1754
    {
1755
        return $this->data->statsMarrAgeQuery($sex, $year1, $year2);
1756
    }
1757
1758
    public function topAgeBetweenSiblings(): string
1759
    {
1760
        return $this->data->topAgeBetweenSiblings();
1761
    }
1762
1763
    public function topAgeBetweenSiblingsFullName(): string
1764
    {
1765
        return $this->data->topAgeBetweenSiblingsFullName();
1766
    }
1767
1768
    public function topAgeBetweenSiblingsList(string $limit = '10', string $one = '0'): string
1769
    {
1770
        return $this->data->topAgeBetweenSiblingsList((int) $limit, (bool) $one);
1771
    }
1772
1773
    public function topAgeBetweenSiblingsName(): string
1774
    {
1775
        $row = $this->data->maximumAgeBetweenSiblings(1)[0] ?? null;
1776
1777
        if ($row === null) {
1778
            return $this->format->missing();
1779
        }
1780
1781
        return
1782
            $this->format->record($row->child2) . ' ' .
1783
            I18N::translate('and') . ' ' .
1784
            $this->format->record($row->child1) . ' ' .
1785
            '<a href="' . e($row->family->url()) . '">[' . I18N::translate('View this family') . ']</a>';
1786
    }
1787
1788
    public function topAgeOfMarriage(): string
1789
    {
1790
        return $this->data->ageOfMarriageQuery('age', 'DESC', 1);
1791
    }
1792
1793
    public function topAgeOfMarriageFamilies(string $limit = '10'): string
1794
    {
1795
        return $this->data->ageOfMarriageQuery('nolist', 'DESC', (int) $limit);
1796
    }
1797
1798
    public function topAgeOfMarriageFamiliesList(string $limit = '10'): string
1799
    {
1800
        return $this->data->ageOfMarriageQuery('list', 'DESC', (int) $limit);
1801
    }
1802
1803
    public function topAgeOfMarriageFamily(): string
1804
    {
1805
        return $this->data->ageOfMarriageQuery('name', 'DESC', 1);
1806
    }
1807
1808
    public function topTenLargestFamily(string $limit = '10'): string
1809
    {
1810
        return $this->data->topTenLargestFamily((int) $limit);
1811
    }
1812
1813
    public function topTenLargestFamilyList(string $limit = '10'): string
1814
    {
1815
        return $this->data->topTenLargestFamilyList((int) $limit);
1816
    }
1817
1818
    public function topTenLargestGrandFamily(string $limit = '10'): string
1819
    {
1820
        return $this->data->topTenLargestGrandFamily((int) $limit);
1821
    }
1822
1823
    public function topTenLargestGrandFamilyList(string $limit = '10'): string
1824
    {
1825
        return $this->data->topTenLargestGrandFamilyList((int) $limit);
1826
    }
1827
1828
    public function topTenOldest(string $limit = '10'): string
1829
    {
1830
        $records = $this->data->topTenOldestQuery('ALL', (int) $limit)
1831
            ->map(fn (object $row): array => [
1832
                'person' => $row->individual,
1833
                'age'    => $this->format->age($row->days),
1834
            ])
1835
            ->all();
1836
1837
        return view('statistics/individuals/top10-nolist', [
1838
            'records' => $records,
1839
        ]);
1840
    }
1841
1842
    public function topTenOldestAlive(string $limit = '10'): string
1843
    {
1844
        if (!Auth::isMember($this->tree)) {
1845
            return I18N::translate('This information is private and cannot be shown.');
1846
        }
1847
1848
        $records = $this->data->topTenOldestAliveQuery('ALL', (int) $limit)
1849
            ->map(fn (Individual $individual): array => [
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Individual was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1850
                'person' => $individual,
1851
                'age'    => $this->format->age(Registry::timestampFactory()->now()->julianDay() - $individual->getBirthDate()->minimumJulianDay()),
1852
            ])
1853
            ->all();
1854
1855
        return view('statistics/individuals/top10-nolist', [
1856
            'records' => $records,
1857
        ]);
1858
    }
1859
1860
    public function topTenOldestFemale(string $limit = '10'): string
1861
    {
1862
        $records = $this->data->topTenOldestQuery('F', (int) $limit)
1863
            ->map(fn (object $row): array => [
1864
                'person' => $row->individual,
1865
                'age'    => $this->format->age($row->days),
1866
            ])
1867
            ->all();
1868
1869
        return view('statistics/individuals/top10-nolist', [
1870
            'records' => $records,
1871
        ]);
1872
    }
1873
1874
    public function topTenOldestFemaleAlive(string $limit = '10'): string
1875
    {
1876
        if (!Auth::isMember($this->tree)) {
1877
            return I18N::translate('This information is private and cannot be shown.');
1878
        }
1879
1880
        $records = $this->data->topTenOldestAliveQuery('F', (int) $limit)
1881
            ->map(fn (Individual $individual): array => [
1882
                'person' => $individual,
1883
                'age'    => $this->format->age(Registry::timestampFactory()->now()->julianDay() - $individual->getBirthDate()->minimumJulianDay()),
1884
            ])
1885
            ->all();
1886
1887
        return view('statistics/individuals/top10-nolist', [
1888
            'records' => $records,
1889
        ]);
1890
    }
1891
1892
    public function topTenOldestFemaleList(string $limit = '10'): string
1893
    {
1894
        $records = $this->data->topTenOldestQuery('F', (int) $limit)
1895
            ->map(fn (object $row): array => [
1896
                'person' => $row->individual,
1897
                'age'    => $this->format->age($row->days),
1898
            ])
1899
            ->all();
1900
1901
        return view('statistics/individuals/top10-list', [
1902
            'records' => $records,
1903
        ]);
1904
    }
1905
1906
    public function topTenOldestFemaleListAlive(string $limit = '10'): string
1907
    {
1908
        if (!Auth::isMember($this->tree)) {
1909
            return I18N::translate('This information is private and cannot be shown.');
1910
        }
1911
1912
        $records = $this->data->topTenOldestAliveQuery('F', (int) $limit)
1913
            ->map(fn (Individual $individual): array => [
1914
                'person' => $individual,
1915
                'age'    => $this->format->age(Registry::timestampFactory()->now()->julianDay() - $individual->getBirthDate()->minimumJulianDay()),
1916
            ])
1917
            ->all();
1918
1919
        return view('statistics/individuals/top10-list', [
1920
            'records' => $records,
1921
        ]);
1922
    }
1923
1924
    public function topTenOldestList(string $limit = '10'): string
1925
    {
1926
        $records = $this->data->topTenOldestQuery('ALL', (int) $limit)
1927
            ->map(fn (object $row): array => [
1928
                'person' => $row->individual,
1929
                'age'    => $this->format->age($row->days),
1930
            ])
1931
            ->all();
1932
1933
        return view('statistics/individuals/top10-list', [
1934
            'records' => $records,
1935
        ]);
1936
    }
1937
1938
    public function topTenOldestListAlive(string $limit = '10'): string
1939
    {
1940
        if (!Auth::isMember($this->tree)) {
1941
            return I18N::translate('This information is private and cannot be shown.');
1942
        }
1943
1944
        $records = $this->data->topTenOldestAliveQuery('ALL', (int) $limit)
1945
            ->map(fn (Individual $individual): array => [
1946
                'person' => $individual,
1947
                'age'    => $this->format->age(Registry::timestampFactory()->now()->julianDay() - $individual->getBirthDate()->minimumJulianDay()),
1948
            ])
1949
            ->all();
1950
1951
        return view('statistics/individuals/top10-list', [
1952
            'records' => $records,
1953
        ]);
1954
    }
1955
1956
    public function topTenOldestMale(string $limit = '10'): string
1957
    {
1958
        $records = $this->data->topTenOldestQuery('M', (int) $limit)
1959
            ->map(fn (object $row): array => [
1960
                'person' => $row->individual,
1961
                'age'    => $this->format->age($row->days),
1962
            ])
1963
            ->all();
1964
1965
        return view('statistics/individuals/top10-nolist', [
1966
            'records' => $records,
1967
        ]);
1968
    }
1969
1970
    public function topTenOldestMaleAlive(string $limit = '10'): string
1971
    {
1972
        if (!Auth::isMember($this->tree)) {
1973
            return I18N::translate('This information is private and cannot be shown.');
1974
        }
1975
1976
        $records = $this->data->topTenOldestAliveQuery('M', (int) $limit)
1977
            ->map(fn (Individual $individual): array => [
1978
                'person' => $individual,
1979
                'age'    => $this->format->age(Registry::timestampFactory()->now()->julianDay() - $individual->getBirthDate()->minimumJulianDay()),
1980
            ])
1981
            ->all();
1982
1983
        return view('statistics/individuals/top10-nolist', [
1984
            'records' => $records,
1985
        ]);
1986
    }
1987
1988
    public function topTenOldestMaleList(string $limit = '10'): string
1989
    {
1990
        $records = $this->data->topTenOldestQuery('M', (int) $limit)
1991
            ->map(fn (object $row): array => [
1992
                'person' => $row->individual,
1993
                'age'    => $this->format->age($row->days),
1994
            ])
1995
            ->all();
1996
1997
        return view('statistics/individuals/top10-list', [
1998
            'records' => $records,
1999
        ]);
2000
    }
2001
2002
    public function topTenOldestMaleListAlive(string $limit = '10'): string
2003
    {
2004
        if (!Auth::isMember($this->tree)) {
2005
            return I18N::translate('This information is private and cannot be shown.');
2006
        }
2007
2008
        $records = $this->data->topTenOldestAliveQuery('M', (int) $limit)
2009
            ->map(fn (Individual $individual): array => [
2010
                'person' => $individual,
2011
                'age'    => $this->format->age(Registry::timestampFactory()->now()->julianDay() - $individual->getBirthDate()->minimumJulianDay()),
2012
            ])
2013
            ->all();
2014
2015
        return view('statistics/individuals/top10-list', [
2016
            'records' => $records,
2017
        ]);
2018
    }
2019
2020
    public function totalAdmins(): string
2021
    {
2022
        return I18N::number($this->user_service->administrators()->count());
2023
    }
2024
2025
    public function totalBirths(): string
2026
    {
2027
        return I18N::number($this->data->countIndividualsWithEvents(['BIRT']));
2028
    }
2029
2030
    public function totalChildren(): string
2031
    {
2032
        return I18N::number($this->data->countChildren());
2033
    }
2034
2035
    public function totalDeaths(): string
2036
    {
2037
        return I18N::number($this->data->countIndividualsWithEvents(['DEAT']));
2038
    }
2039
2040
    public function totalDeceased(): string
2041
    {
2042
        return I18N::number($this->data->countIndividualsDeceased());
2043
    }
2044
2045
    public function totalDeceasedPercentage(): string
2046
    {
2047
        return $this->format->percentage(
2048
            $this->data->countIndividualsDeceased(),
2049
            $this->data->countIndividuals()
2050
        );
2051
    }
2052
2053
    public function totalDivorces(): string
2054
    {
2055
        return I18N::number($this->data->countFamiliesWithEvents(['DIV']));
2056
    }
2057
2058
    public function totalEvents(): string
2059
    {
2060
        return I18N::number($this->data->countOtherEvents(['CHAN']));
2061
    }
2062
2063
    public function totalEventsBirth(): string
2064
    {
2065
        return I18N::number($this->data->countAllEvents(Gedcom::BIRTH_EVENTS));
2066
    }
2067
2068
    public function totalEventsDeath(): string
2069
    {
2070
        return I18N::number($this->data->countAllEvents(Gedcom::DEATH_EVENTS));
2071
    }
2072
2073
    public function totalEventsDivorce(): string
2074
    {
2075
        return I18N::number($this->data->countAllEvents(Gedcom::DIVORCE_EVENTS));
2076
    }
2077
2078
    public function totalEventsMarriage(): string
2079
    {
2080
        return I18N::number($this->data->countAllEvents(Gedcom::MARRIAGE_EVENTS));
2081
    }
2082
2083
    public function totalEventsOther(): string
2084
    {
2085
        return I18N::number($this->data->countOtherEvents([
2086
            'CHAN',
2087
            ...Gedcom::BIRTH_EVENTS,
2088
            ...Gedcom::DEATH_EVENTS,
2089
            ...Gedcom::MARRIAGE_EVENTS,
2090
            ...Gedcom::DIVORCE_EVENTS,
2091
        ]));
2092
    }
2093
2094
    public function totalFamilies(): string
2095
    {
2096
        return I18N::number($this->data->countFamilies());
2097
    }
2098
2099
    public function totalFamiliesPercentage(): string
2100
    {
2101
        return $this->format->percentage(
2102
            $this->data->countFamilies(),
2103
            $this->data->countAllRecords()
2104
        );
2105
    }
2106
2107
    public function totalFamsWithSources(): string
2108
    {
2109
        return I18N::number($this->data->countFamiliesWithSources());
2110
    }
2111
2112
    public function totalFamsWithSourcesPercentage(): string
2113
    {
2114
        return $this->format->percentage(
2115
            $this->data->countFamiliesWithSources(),
2116
            $this->data->countFamilies()
2117
        );
2118
    }
2119
2120
    public function totalGedcomFavorites(): string
2121
    {
2122
        return I18N::number($this->data->countTreeFavorites());
2123
    }
2124
2125
    public function totalGivennames(string ...$names): string
2126
    {
2127
        return I18N::number($this->data->countGivenNames($names));
2128
    }
2129
2130
    public function totalIndisWithSources(): string
2131
    {
2132
        return I18N::number($this->data->countIndividualsWithSources());
2133
    }
2134
2135
    public function totalIndisWithSourcesPercentage(): string
2136
    {
2137
        return $this->format->percentage(
2138
            $this->data->countIndividualsWithSources(),
2139
            $this->data->countIndividuals()
2140
        );
2141
    }
2142
2143
    public function totalIndividuals(): string
2144
    {
2145
        return I18N::number($this->data->countIndividuals());
2146
    }
2147
2148
    public function totalIndividualsPercentage(): string
2149
    {
2150
        return $this->format->percentage(
2151
            $this->data->countIndividuals(),
2152
            $this->data->countAllRecords()
2153
        );
2154
    }
2155
2156
    public function totalLiving(): string
2157
    {
2158
        return I18N::number($this->data->countIndividualsLiving());
2159
    }
2160
2161
    public function totalLivingPercentage(): string
2162
    {
2163
        return $this->format->percentage(
2164
            $this->data->countIndividualsLiving(),
2165
            $this->data->countIndividuals()
2166
        );
2167
    }
2168
2169
    public function totalMarriages(): string
2170
    {
2171
        return I18N::number($this->data->countFamiliesWithEvents(['MARR']));
2172
    }
2173
2174
    public function totalMarriedFemales(): string
2175
    {
2176
        return I18N::number($this->data->countMarriedFemales());
2177
    }
2178
2179
    public function totalMarriedMales(): string
2180
    {
2181
        return I18N::number($this->data->countMarriedMales());
2182
    }
2183
2184
    public function totalMedia(): string
2185
    {
2186
        return I18N::number($this->data->countMedia());
2187
    }
2188
2189
    public function totalMediaAudio(): string
2190
    {
2191
        return I18N::number($this->data->countMedia('audio'));
2192
    }
2193
2194
    public function totalMediaBook(): string
2195
    {
2196
        return I18N::number($this->data->countMedia('book'));
2197
    }
2198
2199
    public function totalMediaCard(): string
2200
    {
2201
        return I18N::number($this->data->countMedia('card'));
2202
    }
2203
2204
    public function totalMediaCertificate(): string
2205
    {
2206
        return I18N::number($this->data->countMedia('certificate'));
2207
    }
2208
2209
    public function totalMediaCoatOfArms(): string
2210
    {
2211
        return I18N::number($this->data->countMedia('coat'));
2212
    }
2213
2214
    public function totalMediaDocument(): string
2215
    {
2216
        return I18N::number($this->data->countMedia('document'));
2217
    }
2218
2219
    public function totalMediaElectronic(): string
2220
    {
2221
        return I18N::number($this->data->countMedia('electronic'));
2222
    }
2223
2224
    public function totalMediaFiche(): string
2225
    {
2226
        return I18N::number($this->data->countMedia('fiche'));
2227
    }
2228
2229
    public function totalMediaFilm(): string
2230
    {
2231
        return I18N::number($this->data->countMedia('film'));
2232
    }
2233
2234
    public function totalMediaMagazine(): string
2235
    {
2236
        return I18N::number($this->data->countMedia('magazine'));
2237
    }
2238
2239
    public function totalMediaManuscript(): string
2240
    {
2241
        return I18N::number($this->data->countMedia('manuscript'));
2242
    }
2243
2244
    public function totalMediaMap(): string
2245
    {
2246
        return I18N::number($this->data->countMedia('map'));
2247
    }
2248
2249
    public function totalMediaNewspaper(): string
2250
    {
2251
        return I18N::number($this->data->countMedia('newspaper'));
2252
    }
2253
2254
    public function totalMediaOther(): string
2255
    {
2256
        return I18N::number($this->data->countMedia('other'));
2257
    }
2258
2259
    public function totalMediaPainting(): string
2260
    {
2261
        return I18N::number($this->data->countMedia('painting'));
2262
    }
2263
2264
    public function totalMediaPhoto(): string
2265
    {
2266
        return I18N::number($this->data->countMedia('photo'));
2267
    }
2268
2269
    public function totalMediaTombstone(): string
2270
    {
2271
        return I18N::number($this->data->countMedia('tombstone'));
2272
    }
2273
2274
    public function totalMediaUnknown(): string
2275
    {
2276
        return I18N::number($this->data->countMedia(''));
2277
    }
2278
2279
    public function totalMediaVideo(): string
2280
    {
2281
        return I18N::number($this->data->countMedia('video'));
2282
    }
2283
2284
    public function totalNonAdmins(): string
2285
    {
2286
        return I18N::number($this->user_service->all()->count() - $this->user_service->administrators()->count());
2287
    }
2288
2289
    public function totalNotes(): string
2290
    {
2291
        return I18N::number($this->data->countNotes());
2292
    }
2293
2294
    public function totalNotesPercentage(): string
2295
    {
2296
        return $this->format->percentage(
2297
            $this->data->countNotes(),
2298
            $this->data->countAllRecords()
2299
        );
2300
    }
2301
2302
    public function totalPlaces(): string
2303
    {
2304
        return I18N::number($this->data->countAllPlaces());
2305
    }
2306
2307
    public function totalRecords(): string
2308
    {
2309
        return I18N::number($this->data->countAllRecords());
2310
    }
2311
2312
    public function totalRepositories(): string
2313
    {
2314
        return I18N::number($this->data->countRepositories());
2315
    }
2316
2317
    public function totalRepositoriesPercentage(): string
2318
    {
2319
        return $this->format->percentage(
2320
            $this->data->countRepositories(),
2321
            $this->data->countAllRecords()
2322
        );
2323
    }
2324
2325
    public function totalSexFemales(): string
2326
    {
2327
        return I18N::number($this->data->countIndividualsBySex('F'));
2328
    }
2329
2330
    public function totalSexFemalesPercentage(): string
2331
    {
2332
        return $this->format->percentage(
2333
            $this->data->countIndividualsBySex('F'),
2334
            $this->data->countIndividuals()
2335
        );
2336
    }
2337
2338
    public function totalSexMales(): string
2339
    {
2340
        return I18N::number($this->data->countIndividualsBySex('M'));
2341
    }
2342
2343
    public function totalSexMalesPercentage(): string
2344
    {
2345
        return $this->format->percentage(
2346
            $this->data->countIndividualsBySex('M'),
2347
            $this->data->countIndividuals()
2348
        );
2349
    }
2350
2351
    public function totalSexOther(): string
2352
    {
2353
        return I18N::number($this->data->countIndividualsBySex('X'));
2354
    }
2355
2356
    public function totalSexOtherPercentage(): string
2357
    {
2358
        return $this->format->percentage(
2359
            $this->data->countIndividualsBySex('X'),
2360
            $this->data->countIndividuals()
2361
        );
2362
    }
2363
2364
    public function totalSexUnknown(): string
2365
    {
2366
        return I18N::number($this->data->countIndividualsBySex('U'));
2367
    }
2368
2369
    public function totalSexUnknownPercentage(): string
2370
    {
2371
        return $this->format->percentage(
2372
            $this->data->countIndividualsBySex('U'),
2373
            $this->data->countIndividuals()
2374
        );
2375
    }
2376
2377
    public function totalSources(): string
2378
    {
2379
        return I18N::number($this->data->countSources());
2380
    }
2381
2382
    public function totalSourcesPercentage(): string
2383
    {
2384
        return $this->format->percentage(
2385
            $this->data->countSources(),
2386
            $this->data->countAllRecords()
2387
        );
2388
    }
2389
2390
    public function totalSurnames(string ...$names): string
2391
    {
2392
        return I18N::number($this->data->countSurnames($names));
2393
    }
2394
2395
    public function totalTreeNews(): string
2396
    {
2397
        return I18N::number($this->data->countTreeNews());
2398
    }
2399
2400
    public function totalUserFavorites(): string
2401
    {
2402
        return I18N::number($this->data->countUserfavorites());
2403
    }
2404
2405
    public function totalUserJournal(): string
2406
    {
2407
        return I18N::number($this->data->countUserJournal());
2408
    }
2409
2410
    public function totalUserMessages(): string
2411
    {
2412
        return I18N::number($this->data->countUserMessages());
2413
    }
2414
2415
    public function totalUsers(): string
2416
    {
2417
        return I18N::number($this->user_service->all()->count());
2418
    }
2419
2420
    public function userFavorites(): string
2421
    {
2422
        return $this->callBlock('user_favorites');
2423
    }
2424
2425
    public function userFullName(): string
2426
    {
2427
        return Auth::check() ? '<bdi>' . e(Auth::user()->realName()) . '</bdi>' : '';
2428
    }
2429
2430
    public function userId(): string
2431
    {
2432
        return (string) Auth::id();
2433
    }
2434
2435
    public function userName(string $visitor_text = ''): string
2436
    {
2437
        if (Auth::check()) {
2438
            return e(Auth::user()->userName());
2439
        }
2440
2441
        if ($visitor_text === '') {
2442
            return I18N::translate('Visitor');
2443
        }
2444
2445
        return e($visitor_text);
2446
    }
2447
2448
    public function usersLoggedIn(): string
2449
    {
2450
        return $this->data->usersLoggedIn();
2451
    }
2452
2453
    public function usersLoggedInList(): string
2454
    {
2455
        return $this->data->usersLoggedInList();
2456
    }
2457
2458
    public function webtreesVersion(): string
2459
    {
2460
        return Webtrees::VERSION;
0 ignored issues
show
Bug introduced by
The type Fisharebest\Webtrees\Webtrees was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2461
    }
2462
2463
    public function youngestFather(): string
2464
    {
2465
        return $this->data->parentsQuery('full', 'ASC', 'M', false);
2466
    }
2467
2468
    public function youngestFatherAge(string $show_years = '0'): string
2469
    {
2470
        return $this->data->parentsQuery('age', 'ASC', 'M', (bool) $show_years);
2471
    }
2472
2473
    public function youngestFatherName(): string
2474
    {
2475
        return $this->data->parentsQuery('name', 'ASC', 'M', false);
2476
    }
2477
2478
    public function youngestMarriageFemale(): string
2479
    {
2480
        return $this->data->marriageQuery('full', 'ASC', 'F', false);
2481
    }
2482
2483
    public function youngestMarriageFemaleAge(string $show_years = '0'): string
2484
    {
2485
        return $this->data->marriageQuery('age', 'ASC', 'F', (bool) $show_years);
2486
    }
2487
2488
    public function youngestMarriageFemaleName(): string
2489
    {
2490
        return $this->data->marriageQuery('name', 'ASC', 'F', false);
2491
    }
2492
2493
    public function youngestMarriageMale(): string
2494
    {
2495
        return $this->data->marriageQuery('full', 'ASC', 'M', false);
2496
    }
2497
2498
    public function youngestMarriageMaleAge(string $show_years = '0'): string
2499
    {
2500
        return $this->data->marriageQuery('age', 'ASC', 'M', (bool) $show_years);
2501
    }
2502
2503
    public function youngestMarriageMaleName(): string
2504
    {
2505
        return $this->data->marriageQuery('name', 'ASC', 'M', false);
2506
    }
2507
2508
    public function youngestMother(): string
2509
    {
2510
        return $this->data->parentsQuery('full', 'ASC', 'F', false);
2511
    }
2512
2513
    public function youngestMotherAge(string $show_years = '0'): string
2514
    {
2515
        return $this->data->parentsQuery('age', 'ASC', 'F', (bool) $show_years);
2516
    }
2517
2518
    public function youngestMotherName(): string
2519
    {
2520
        return $this->data->parentsQuery('name', 'ASC', 'F', false);
2521
    }
2522
}
2523