xref: /webtrees/app/Statistics.php (revision 992ad95a3b71cdc2194e7663a57b01db9e5e7719)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2021 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
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees;
21
22use Fisharebest\Webtrees\Module\ModuleBlockInterface;
23use Fisharebest\Webtrees\Module\ModuleInterface;
24use Fisharebest\Webtrees\Services\ModuleService;
25use Fisharebest\Webtrees\Services\UserService;
26use Fisharebest\Webtrees\Statistics\Repository\BrowserRepository;
27use Fisharebest\Webtrees\Statistics\Repository\ContactRepository;
28use Fisharebest\Webtrees\Statistics\Repository\EventRepository;
29use Fisharebest\Webtrees\Statistics\Repository\FamilyDatesRepository;
30use Fisharebest\Webtrees\Statistics\Repository\FamilyRepository;
31use Fisharebest\Webtrees\Statistics\Repository\FavoritesRepository;
32use Fisharebest\Webtrees\Statistics\Repository\GedcomRepository;
33use Fisharebest\Webtrees\Statistics\Repository\HitCountRepository;
34use Fisharebest\Webtrees\Statistics\Repository\IndividualRepository;
35use Fisharebest\Webtrees\Statistics\Repository\Interfaces\BrowserRepositoryInterface;
36use Fisharebest\Webtrees\Statistics\Repository\Interfaces\ContactRepositoryInterface;
37use Fisharebest\Webtrees\Statistics\Repository\Interfaces\EventRepositoryInterface;
38use Fisharebest\Webtrees\Statistics\Repository\Interfaces\FamilyDatesRepositoryInterface;
39use Fisharebest\Webtrees\Statistics\Repository\Interfaces\FavoritesRepositoryInterface;
40use Fisharebest\Webtrees\Statistics\Repository\Interfaces\GedcomRepositoryInterface;
41use Fisharebest\Webtrees\Statistics\Repository\Interfaces\HitCountRepositoryInterface;
42use Fisharebest\Webtrees\Statistics\Repository\Interfaces\IndividualRepositoryInterface;
43use Fisharebest\Webtrees\Statistics\Repository\Interfaces\LatestUserRepositoryInterface;
44use Fisharebest\Webtrees\Statistics\Repository\Interfaces\MediaRepositoryInterface;
45use Fisharebest\Webtrees\Statistics\Repository\Interfaces\MessageRepositoryInterface;
46use Fisharebest\Webtrees\Statistics\Repository\Interfaces\NewsRepositoryInterface;
47use Fisharebest\Webtrees\Statistics\Repository\Interfaces\PlaceRepositoryInterface;
48use Fisharebest\Webtrees\Statistics\Repository\Interfaces\ServerRepositoryInterface;
49use Fisharebest\Webtrees\Statistics\Repository\Interfaces\UserRepositoryInterface;
50use Fisharebest\Webtrees\Statistics\Repository\LatestUserRepository;
51use Fisharebest\Webtrees\Statistics\Repository\MediaRepository;
52use Fisharebest\Webtrees\Statistics\Repository\MessageRepository;
53use Fisharebest\Webtrees\Statistics\Repository\NewsRepository;
54use Fisharebest\Webtrees\Statistics\Repository\PlaceRepository;
55use Fisharebest\Webtrees\Statistics\Repository\ServerRepository;
56use Fisharebest\Webtrees\Statistics\Repository\UserRepository;
57use Fisharebest\Webtrees\Statistics\Service\CountryService;
58use Illuminate\Database\Query\Builder;
59use Illuminate\Support\Collection;
60use ReflectionClass;
61use ReflectionException;
62use ReflectionMethod;
63use ReflectionNamedType;
64
65use function call_user_func;
66use function count;
67use function in_array;
68use function str_contains;
69
70/**
71 * A selection of pre-formatted statistical queries.
72 * These are primarily used for embedded keywords on HTML blocks, but
73 * are also used elsewhere in the code.
74 */
75class Statistics implements
76    GedcomRepositoryInterface,
77    IndividualRepositoryInterface,
78    EventRepositoryInterface,
79    MediaRepositoryInterface,
80    UserRepositoryInterface,
81    ServerRepositoryInterface,
82    BrowserRepositoryInterface,
83    HitCountRepositoryInterface,
84    LatestUserRepositoryInterface,
85    FavoritesRepositoryInterface,
86    NewsRepositoryInterface,
87    MessageRepositoryInterface,
88    ContactRepositoryInterface,
89    FamilyDatesRepositoryInterface,
90    PlaceRepositoryInterface
91{
92    /**
93     * Generate statistics for a specified tree.
94     *
95     * @var Tree
96     */
97    private $tree;
98    /**
99     * @var GedcomRepository
100     */
101    private $gedcomRepository;
102
103    /**
104     * @var IndividualRepository
105     */
106    private $individualRepository;
107
108    /**
109     * @var FamilyRepository
110     */
111    private $familyRepository;
112
113    /**
114     * @var MediaRepository
115     */
116    private $mediaRepository;
117
118    /**
119     * @var EventRepository
120     */
121    private $eventRepository;
122
123    /**
124     * @var UserRepository
125     */
126    private $userRepository;
127
128    /**
129     * @var ServerRepository
130     */
131    private $serverRepository;
132
133    /**
134     * @var BrowserRepository
135     */
136    private $browserRepository;
137
138    /**
139     * @var HitCountRepository
140     */
141    private $hitCountRepository;
142
143    /**
144     * @var LatestUserRepository
145     */
146    private $latestUserRepository;
147
148    /**
149     * @var FavoritesRepository
150     */
151    private $favoritesRepository;
152
153    /**
154     * @var NewsRepository
155     */
156    private $newsRepository;
157
158    /**
159     * @var MessageRepository
160     */
161    private $messageRepository;
162
163    /**
164     * @var ContactRepository
165     */
166    private $contactRepository;
167
168    /**
169     * @var FamilyDatesRepository
170     */
171    private $familyDatesRepository;
172
173    /**
174     * @var PlaceRepository
175     */
176    private $placeRepository;
177
178    /**
179     * @var ModuleService
180     */
181    private $module_service;
182
183    /**
184     * Create the statistics for a tree.
185     *
186     * @param CountryService $country_service
187     * @param ModuleService  $module_service
188     * @param Tree           $tree Generate statistics for this tree
189     * @param UserService    $user_service
190     */
191    public function __construct(
192        CountryService $country_service,
193        ModuleService $module_service,
194        Tree $tree,
195        UserService $user_service
196    ) {
197        $this->tree                  = $tree;
198        $this->gedcomRepository      = new GedcomRepository($tree);
199        $this->individualRepository  = new IndividualRepository($tree);
200        $this->familyRepository      = new FamilyRepository($tree);
201        $this->familyDatesRepository = new FamilyDatesRepository($tree);
202        $this->mediaRepository       = new MediaRepository($tree);
203        $this->eventRepository       = new EventRepository($tree);
204        $this->userRepository        = new UserRepository($tree, $user_service);
205        $this->serverRepository      = new ServerRepository();
206        $this->browserRepository     = new BrowserRepository();
207        $this->hitCountRepository    = new HitCountRepository($tree, $user_service);
208        $this->latestUserRepository  = new LatestUserRepository($user_service);
209        $this->favoritesRepository   = new FavoritesRepository($tree, $module_service);
210        $this->newsRepository        = new NewsRepository($tree);
211        $this->messageRepository     = new MessageRepository();
212        $this->contactRepository     = new ContactRepository($tree, $user_service);
213        $this->placeRepository       = new PlaceRepository($tree, $country_service);
214        $this->module_service        = $module_service;
215    }
216
217    /**
218     * Return a string of all supported tags and an example of its output in table row form.
219     *
220     * @return string
221     */
222    public function getAllTagsTable(): string
223    {
224        try {
225            $class = new ReflectionClass($this);
226
227            $public_methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
228
229            $examples = Collection::make($public_methods)
230                ->filter(static function (ReflectionMethod $method): bool {
231                    return !in_array($method->getName(), ['embedTags', 'getAllTagsTable'], true);
232                })
233                ->filter(static function (ReflectionMethod $method): bool {
234                    $type = $method->getReturnType();
235
236                    return $type instanceof ReflectionNamedType && $type->getName() === 'string';
237                })
238                ->sort(static function (ReflectionMethod $x, ReflectionMethod $y): int {
239                    return $x->getName() <=> $y->getName();
240                })
241                ->map(function (ReflectionMethod $method): string {
242                    $tag = $method->getName();
243
244                    return '<dt>#' . $tag . '#</dt><dd>' . call_user_func([$this, $tag]) . '</dd>';
245                });
246
247            return '<dl>' . $examples->implode('') . '</dl>';
248        } catch (ReflectionException $ex) {
249            return $ex->getMessage();
250        }
251    }
252
253    /**
254     * Embed tags in text
255     *
256     * @param string $text
257     *
258     * @return string
259     */
260    public function embedTags(string $text): string
261    {
262        if (str_contains($text, '#')) {
263            $text = strtr($text, $this->getTags($text));
264        }
265
266        return $text;
267    }
268
269    /**
270     * @return string
271     */
272    public function gedcomFilename(): string
273    {
274        return $this->gedcomRepository->gedcomFilename();
275    }
276
277    /**
278     * @return int
279     */
280    public function gedcomId(): int
281    {
282        return $this->gedcomRepository->gedcomId();
283    }
284
285    /**
286     * @return string
287     */
288    public function gedcomTitle(): string
289    {
290        return $this->gedcomRepository->gedcomTitle();
291    }
292
293    /**
294     * @return string
295     */
296    public function gedcomCreatedSoftware(): string
297    {
298        return $this->gedcomRepository->gedcomCreatedSoftware();
299    }
300
301    /**
302     * @return string
303     */
304    public function gedcomCreatedVersion(): string
305    {
306        return $this->gedcomRepository->gedcomCreatedVersion();
307    }
308
309    /**
310     * @return string
311     */
312    public function gedcomDate(): string
313    {
314        return $this->gedcomRepository->gedcomDate();
315    }
316
317    /**
318     * @return string
319     */
320    public function gedcomUpdated(): string
321    {
322        return $this->gedcomRepository->gedcomUpdated();
323    }
324
325    /**
326     * @return string
327     */
328    public function gedcomRootId(): string
329    {
330        return $this->gedcomRepository->gedcomRootId();
331    }
332
333    /**
334     * @return string
335     */
336    public function totalRecords(): string
337    {
338        return $this->individualRepository->totalRecords();
339    }
340
341    /**
342     * @return string
343     */
344    public function totalIndividuals(): string
345    {
346        return $this->individualRepository->totalIndividuals();
347    }
348
349    /**
350     * @return string
351     */
352    public function totalIndisWithSources(): string
353    {
354        return $this->individualRepository->totalIndisWithSources();
355    }
356
357    /**
358     * @param string|null $color_from
359     * @param string|null $color_to
360     *
361     * @return string
362     */
363    public function chartIndisWithSources(
364        string $color_from = null,
365        string $color_to = null
366    ): string {
367        return $this->individualRepository->chartIndisWithSources($color_from, $color_to);
368    }
369
370    /**
371     * @return string
372     */
373    public function totalIndividualsPercentage(): string
374    {
375        return $this->individualRepository->totalIndividualsPercentage();
376    }
377
378    /**
379     * @return string
380     */
381    public function totalFamilies(): string
382    {
383        return $this->individualRepository->totalFamilies();
384    }
385
386    /**
387     * @return string
388     */
389    public function totalFamiliesPercentage(): string
390    {
391        return $this->individualRepository->totalFamiliesPercentage();
392    }
393
394    /**
395     * @return string
396     */
397    public function totalFamsWithSources(): string
398    {
399        return $this->individualRepository->totalFamsWithSources();
400    }
401
402    /**
403     * @param string|null $color_from
404     * @param string|null $color_to
405     *
406     * @return string
407     */
408    public function chartFamsWithSources(
409        string $color_from = null,
410        string $color_to = null
411    ): string {
412        return $this->individualRepository->chartFamsWithSources($color_from, $color_to);
413    }
414
415    /**
416     * @return string
417     */
418    public function totalSources(): string
419    {
420        return $this->individualRepository->totalSources();
421    }
422
423    /**
424     * @return string
425     */
426    public function totalSourcesPercentage(): string
427    {
428        return $this->individualRepository->totalSourcesPercentage();
429    }
430
431    /**
432     * @return string
433     */
434    public function totalNotes(): string
435    {
436        return $this->individualRepository->totalNotes();
437    }
438
439    /**
440     * @return string
441     */
442    public function totalNotesPercentage(): string
443    {
444        return $this->individualRepository->totalNotesPercentage();
445    }
446
447    /**
448     * @return string
449     */
450    public function totalRepositories(): string
451    {
452        return $this->individualRepository->totalRepositories();
453    }
454
455    /**
456     * @return string
457     */
458    public function totalRepositoriesPercentage(): string
459    {
460        return $this->individualRepository->totalRepositoriesPercentage();
461    }
462
463    /**
464     * @param array<string> ...$params
465     *
466     * @return string
467     */
468    public function totalSurnames(...$params): string
469    {
470        return $this->individualRepository->totalSurnames(...$params);
471    }
472
473    /**
474     * @param array<string> ...$params
475     *
476     * @return string
477     */
478    public function totalGivennames(...$params): string
479    {
480        return $this->individualRepository->totalGivennames(...$params);
481    }
482
483    /**
484     * @param array<string> $events
485     *
486     * @return string
487     */
488    public function totalEvents(array $events = []): string
489    {
490        return $this->eventRepository->totalEvents($events);
491    }
492
493    /**
494     * @return string
495     */
496    public function totalEventsBirth(): string
497    {
498        return $this->eventRepository->totalEventsBirth();
499    }
500
501    /**
502     * @return string
503     */
504    public function totalBirths(): string
505    {
506        return $this->eventRepository->totalBirths();
507    }
508
509    /**
510     * @return string
511     */
512    public function totalEventsDeath(): string
513    {
514        return $this->eventRepository->totalEventsDeath();
515    }
516
517    /**
518     * @return string
519     */
520    public function totalDeaths(): string
521    {
522        return $this->eventRepository->totalDeaths();
523    }
524
525    /**
526     * @return string
527     */
528    public function totalEventsMarriage(): string
529    {
530        return $this->eventRepository->totalEventsMarriage();
531    }
532
533    /**
534     * @return string
535     */
536    public function totalMarriages(): string
537    {
538        return $this->eventRepository->totalMarriages();
539    }
540
541    /**
542     * @return string
543     */
544    public function totalEventsDivorce(): string
545    {
546        return $this->eventRepository->totalEventsDivorce();
547    }
548
549    /**
550     * @return string
551     */
552    public function totalDivorces(): string
553    {
554        return $this->eventRepository->totalDivorces();
555    }
556
557    /**
558     * @return string
559     */
560    public function totalEventsOther(): string
561    {
562        return $this->eventRepository->totalEventsOther();
563    }
564
565    /**
566     * @return string
567     */
568    public function totalSexMales(): string
569    {
570        return $this->individualRepository->totalSexMales();
571    }
572
573    /**
574     * @return string
575     */
576    public function totalSexMalesPercentage(): string
577    {
578        return $this->individualRepository->totalSexMalesPercentage();
579    }
580
581    /**
582     * @return string
583     */
584    public function totalSexFemales(): string
585    {
586        return $this->individualRepository->totalSexFemales();
587    }
588
589    /**
590     * @return string
591     */
592    public function totalSexFemalesPercentage(): string
593    {
594        return $this->individualRepository->totalSexFemalesPercentage();
595    }
596
597    /**
598     * @return string
599     */
600    public function totalSexUnknown(): string
601    {
602        return $this->individualRepository->totalSexUnknown();
603    }
604
605    /**
606     * @return string
607     */
608    public function totalSexUnknownPercentage(): string
609    {
610        return $this->individualRepository->totalSexUnknownPercentage();
611    }
612
613    /**
614     * @param string|null $color_female
615     * @param string|null $color_male
616     * @param string|null $color_unknown
617     *
618     * @return string
619     */
620    public function chartSex(
621        string $color_female = null,
622        string $color_male = null,
623        string $color_unknown = null
624    ): string {
625        return $this->individualRepository->chartSex($color_female, $color_male, $color_unknown);
626    }
627
628    /**
629     * @return string
630     */
631    public function totalLiving(): string
632    {
633        return $this->individualRepository->totalLiving();
634    }
635
636    /**
637     * @return string
638     */
639    public function totalLivingPercentage(): string
640    {
641        return $this->individualRepository->totalLivingPercentage();
642    }
643
644    /**
645     * @return string
646     */
647    public function totalDeceased(): string
648    {
649        return $this->individualRepository->totalDeceased();
650    }
651
652    /**
653     * @return string
654     */
655    public function totalDeceasedPercentage(): string
656    {
657        return $this->individualRepository->totalDeceasedPercentage();
658    }
659
660    /**
661     * @param string|null $color_living
662     * @param string|null $color_dead
663     *
664     * @return string
665     */
666    public function chartMortality(string $color_living = null, string $color_dead = null): string
667    {
668        return $this->individualRepository->chartMortality($color_living, $color_dead);
669    }
670
671    /**
672     * @return string
673     */
674    public function totalMedia(): string
675    {
676        return $this->mediaRepository->totalMedia();
677    }
678
679    /**
680     * @return string
681     */
682    public function totalMediaAudio(): string
683    {
684        return $this->mediaRepository->totalMediaAudio();
685    }
686
687    /**
688     * @return string
689     */
690    public function totalMediaBook(): string
691    {
692        return $this->mediaRepository->totalMediaBook();
693    }
694
695    /**
696     * @return string
697     */
698    public function totalMediaCard(): string
699    {
700        return $this->mediaRepository->totalMediaCard();
701    }
702
703    /**
704     * @return string
705     */
706    public function totalMediaCertificate(): string
707    {
708        return $this->mediaRepository->totalMediaCertificate();
709    }
710
711    /**
712     * @return string
713     */
714    public function totalMediaCoatOfArms(): string
715    {
716        return $this->mediaRepository->totalMediaCoatOfArms();
717    }
718
719    /**
720     * @return string
721     */
722    public function totalMediaDocument(): string
723    {
724        return $this->mediaRepository->totalMediaDocument();
725    }
726
727    /**
728     * @return string
729     */
730    public function totalMediaElectronic(): string
731    {
732        return $this->mediaRepository->totalMediaElectronic();
733    }
734
735    /**
736     * @return string
737     */
738    public function totalMediaMagazine(): string
739    {
740        return $this->mediaRepository->totalMediaMagazine();
741    }
742
743    /**
744     * @return string
745     */
746    public function totalMediaManuscript(): string
747    {
748        return $this->mediaRepository->totalMediaManuscript();
749    }
750
751    /**
752     * @return string
753     */
754    public function totalMediaMap(): string
755    {
756        return $this->mediaRepository->totalMediaMap();
757    }
758
759    /**
760     * @return string
761     */
762    public function totalMediaFiche(): string
763    {
764        return $this->mediaRepository->totalMediaFiche();
765    }
766
767    /**
768     * @return string
769     */
770    public function totalMediaFilm(): string
771    {
772        return $this->mediaRepository->totalMediaFilm();
773    }
774
775    /**
776     * @return string
777     */
778    public function totalMediaNewspaper(): string
779    {
780        return $this->mediaRepository->totalMediaNewspaper();
781    }
782
783    /**
784     * @return string
785     */
786    public function totalMediaPainting(): string
787    {
788        return $this->mediaRepository->totalMediaPainting();
789    }
790
791    /**
792     * @return string
793     */
794    public function totalMediaPhoto(): string
795    {
796        return $this->mediaRepository->totalMediaPhoto();
797    }
798
799    /**
800     * @return string
801     */
802    public function totalMediaTombstone(): string
803    {
804        return $this->mediaRepository->totalMediaTombstone();
805    }
806
807    /**
808     * @return string
809     */
810    public function totalMediaVideo(): string
811    {
812        return $this->mediaRepository->totalMediaVideo();
813    }
814
815    /**
816     * @return string
817     */
818    public function totalMediaOther(): string
819    {
820        return $this->mediaRepository->totalMediaOther();
821    }
822
823    /**
824     * @return string
825     */
826    public function totalMediaUnknown(): string
827    {
828        return $this->mediaRepository->totalMediaUnknown();
829    }
830
831    /**
832     * @param string|null $color_from
833     * @param string|null $color_to
834     *
835     * @return string
836     */
837    public function chartMedia(string $color_from = null, string $color_to = null): string
838    {
839        return $this->mediaRepository->chartMedia($color_from, $color_to);
840    }
841
842    /**
843     * @param string $what
844     * @param string $fact
845     * @param int    $parent
846     * @param bool   $country
847     *
848     * @return array<object>
849     */
850    public function statsPlaces(string $what = 'ALL', string $fact = '', int $parent = 0, bool $country = false): array
851    {
852        return $this->placeRepository->statsPlaces($what, $fact, $parent, $country);
853    }
854
855    /**
856     * @return string
857     */
858    public function totalPlaces(): string
859    {
860        return $this->placeRepository->totalPlaces();
861    }
862
863    /**
864     * @param string $chart_shows
865     * @param string $chart_type
866     * @param string $surname
867     *
868     * @return string
869     */
870    public function chartDistribution(
871        string $chart_shows = 'world',
872        string $chart_type = '',
873        string $surname = ''
874    ): string {
875        return $this->placeRepository->chartDistribution($chart_shows, $chart_type, $surname);
876    }
877
878    /**
879     * @return string
880     */
881    public function commonCountriesList(): string
882    {
883        return $this->placeRepository->commonCountriesList();
884    }
885
886    /**
887     * @return string
888     */
889    public function commonBirthPlacesList(): string
890    {
891        return $this->placeRepository->commonBirthPlacesList();
892    }
893
894    /**
895     * @return string
896     */
897    public function commonDeathPlacesList(): string
898    {
899        return $this->placeRepository->commonDeathPlacesList();
900    }
901
902    /**
903     * @return string
904     */
905    public function commonMarriagePlacesList(): string
906    {
907        return $this->placeRepository->commonMarriagePlacesList();
908    }
909
910    /**
911     * @return string
912     */
913    public function firstBirth(): string
914    {
915        return $this->familyDatesRepository->firstBirth();
916    }
917
918    /**
919     * @return string
920     */
921    public function firstBirthYear(): string
922    {
923        return $this->familyDatesRepository->firstBirthYear();
924    }
925
926    /**
927     * @return string
928     */
929    public function firstBirthName(): string
930    {
931        return $this->familyDatesRepository->firstBirthName();
932    }
933
934    /**
935     * @return string
936     */
937    public function firstBirthPlace(): string
938    {
939        return $this->familyDatesRepository->firstBirthPlace();
940    }
941
942    /**
943     * @return string
944     */
945    public function lastBirth(): string
946    {
947        return $this->familyDatesRepository->lastBirth();
948    }
949
950    /**
951     * @return string
952     */
953    public function lastBirthYear(): string
954    {
955        return $this->familyDatesRepository->lastBirthYear();
956    }
957
958    /**
959     * @return string
960     */
961    public function lastBirthName(): string
962    {
963        return $this->familyDatesRepository->lastBirthName();
964    }
965
966    /**
967     * @return string
968     */
969    public function lastBirthPlace(): string
970    {
971        return $this->familyDatesRepository->lastBirthPlace();
972    }
973
974    /**
975     * @param int $year1
976     * @param int $year2
977     *
978     * @return Builder
979     */
980    public function statsBirthQuery(int $year1 = -1, int $year2 = -1): Builder
981    {
982        return $this->individualRepository->statsBirthQuery($year1, $year2);
983    }
984
985    /**
986     * @param int $year1
987     * @param int $year2
988     *
989     * @return Builder
990     */
991    public function statsBirthBySexQuery(int $year1 = -1, int $year2 = -1): Builder
992    {
993        return $this->individualRepository->statsBirthBySexQuery($year1, $year2);
994    }
995
996    /**
997     * @param string|null $color_from
998     * @param string|null $color_to
999     *
1000     * @return string
1001     */
1002    public function statsBirth(string $color_from = null, string $color_to = null): string
1003    {
1004        return $this->individualRepository->statsBirth($color_from, $color_to);
1005    }
1006
1007    /**
1008     * @return string
1009     */
1010    public function firstDeath(): string
1011    {
1012        return $this->familyDatesRepository->firstDeath();
1013    }
1014
1015    /**
1016     * @return string
1017     */
1018    public function firstDeathYear(): string
1019    {
1020        return $this->familyDatesRepository->firstDeathYear();
1021    }
1022
1023    /**
1024     * @return string
1025     */
1026    public function firstDeathName(): string
1027    {
1028        return $this->familyDatesRepository->firstDeathName();
1029    }
1030
1031    /**
1032     * @return string
1033     */
1034    public function firstDeathPlace(): string
1035    {
1036        return $this->familyDatesRepository->firstDeathPlace();
1037    }
1038
1039    /**
1040     * @return string
1041     */
1042    public function lastDeath(): string
1043    {
1044        return $this->familyDatesRepository->lastDeath();
1045    }
1046
1047    /**
1048     * @return string
1049     */
1050    public function lastDeathYear(): string
1051    {
1052        return $this->familyDatesRepository->lastDeathYear();
1053    }
1054
1055    /**
1056     * @return string
1057     */
1058    public function lastDeathName(): string
1059    {
1060        return $this->familyDatesRepository->lastDeathName();
1061    }
1062
1063    /**
1064     * @return string
1065     */
1066    public function lastDeathPlace(): string
1067    {
1068        return $this->familyDatesRepository->lastDeathPlace();
1069    }
1070
1071    /**
1072     * @param int $year1
1073     * @param int $year2
1074     *
1075     * @return Builder
1076     */
1077    public function statsDeathQuery(int $year1 = -1, int $year2 = -1): Builder
1078    {
1079        return $this->individualRepository->statsDeathQuery($year1, $year2);
1080    }
1081
1082    /**
1083     * @param int $year1
1084     * @param int $year2
1085     *
1086     * @return Builder
1087     */
1088    public function statsDeathBySexQuery(int $year1 = -1, int $year2 = -1): Builder
1089    {
1090        return $this->individualRepository->statsDeathBySexQuery($year1, $year2);
1091    }
1092
1093    /**
1094     * @param string|null $color_from
1095     * @param string|null $color_to
1096     *
1097     * @return string
1098     */
1099    public function statsDeath(string $color_from = null, string $color_to = null): string
1100    {
1101        return $this->individualRepository->statsDeath($color_from, $color_to);
1102    }
1103
1104    /**
1105     * General query on ages.
1106     *
1107     * @param string $related
1108     * @param string $sex
1109     * @param int    $year1
1110     * @param int    $year2
1111     *
1112     * @return array|string
1113     */
1114    public function statsAgeQuery(string $related = 'BIRT', string $sex = 'BOTH', int $year1 = -1, int $year2 = -1)
1115    {
1116        return $this->individualRepository->statsAgeQuery($related, $sex, $year1, $year2);
1117    }
1118
1119    /**
1120     * @return string
1121     */
1122    public function statsAge(): string
1123    {
1124        return $this->individualRepository->statsAge();
1125    }
1126
1127    /**
1128     * @return string
1129     */
1130    public function longestLife(): string
1131    {
1132        return $this->individualRepository->longestLife();
1133    }
1134
1135    /**
1136     * @return string
1137     */
1138    public function longestLifeAge(): string
1139    {
1140        return $this->individualRepository->longestLifeAge();
1141    }
1142
1143    /**
1144     * @return string
1145     */
1146    public function longestLifeName(): string
1147    {
1148        return $this->individualRepository->longestLifeName();
1149    }
1150
1151    /**
1152     * @return string
1153     */
1154    public function longestLifeFemale(): string
1155    {
1156        return $this->individualRepository->longestLifeFemale();
1157    }
1158
1159    /**
1160     * @return string
1161     */
1162    public function longestLifeFemaleAge(): string
1163    {
1164        return $this->individualRepository->longestLifeFemaleAge();
1165    }
1166
1167    /**
1168     * @return string
1169     */
1170    public function longestLifeFemaleName(): string
1171    {
1172        return $this->individualRepository->longestLifeFemaleName();
1173    }
1174
1175    /**
1176     * @return string
1177     */
1178    public function longestLifeMale(): string
1179    {
1180        return $this->individualRepository->longestLifeMale();
1181    }
1182
1183    /**
1184     * @return string
1185     */
1186    public function longestLifeMaleAge(): string
1187    {
1188        return $this->individualRepository->longestLifeMaleAge();
1189    }
1190
1191    /**
1192     * @return string
1193     */
1194    public function longestLifeMaleName(): string
1195    {
1196        return $this->individualRepository->longestLifeMaleName();
1197    }
1198
1199    /**
1200     * @param string $total
1201     *
1202     * @return string
1203     */
1204    public function topTenOldest(string $total = '10'): string
1205    {
1206        return $this->individualRepository->topTenOldest((int) $total);
1207    }
1208
1209    /**
1210     * @param string $total
1211     *
1212     * @return string
1213     */
1214    public function topTenOldestList(string $total = '10'): string
1215    {
1216        return $this->individualRepository->topTenOldestList((int) $total);
1217    }
1218
1219    /**
1220     * @param string $total
1221     *
1222     * @return string
1223     */
1224    public function topTenOldestFemale(string $total = '10'): string
1225    {
1226        return $this->individualRepository->topTenOldestFemale((int) $total);
1227    }
1228
1229    /**
1230     * @param string $total
1231     *
1232     * @return string
1233     */
1234    public function topTenOldestFemaleList(string $total = '10'): string
1235    {
1236        return $this->individualRepository->topTenOldestFemaleList((int) $total);
1237    }
1238
1239    /**
1240     * @param string $total
1241     *
1242     * @return string
1243     */
1244    public function topTenOldestMale(string $total = '10'): string
1245    {
1246        return $this->individualRepository->topTenOldestMale((int) $total);
1247    }
1248
1249    /**
1250     * @param string $total
1251     *
1252     * @return string
1253     */
1254    public function topTenOldestMaleList(string $total = '10'): string
1255    {
1256        return $this->individualRepository->topTenOldestMaleList((int) $total);
1257    }
1258
1259    /**
1260     * @param string $total
1261     *
1262     * @return string
1263     */
1264    public function topTenOldestAlive(string $total = '10'): string
1265    {
1266        return $this->individualRepository->topTenOldestAlive((int) $total);
1267    }
1268
1269    /**
1270     * @param string $total
1271     *
1272     * @return string
1273     */
1274    public function topTenOldestListAlive(string $total = '10'): string
1275    {
1276        return $this->individualRepository->topTenOldestListAlive((int) $total);
1277    }
1278
1279    /**
1280     * @param string $total
1281     *
1282     * @return string
1283     */
1284    public function topTenOldestFemaleAlive(string $total = '10'): string
1285    {
1286        return $this->individualRepository->topTenOldestFemaleAlive((int) $total);
1287    }
1288
1289    /**
1290     * @param string $total
1291     *
1292     * @return string
1293     */
1294    public function topTenOldestFemaleListAlive(string $total = '10'): string
1295    {
1296        return $this->individualRepository->topTenOldestFemaleListAlive((int) $total);
1297    }
1298
1299    /**
1300     * @param string $total
1301     *
1302     * @return string
1303     */
1304    public function topTenOldestMaleAlive(string $total = '10'): string
1305    {
1306        return $this->individualRepository->topTenOldestMaleAlive((int) $total);
1307    }
1308
1309    /**
1310     * @param string $total
1311     *
1312     * @return string
1313     */
1314    public function topTenOldestMaleListAlive(string $total = '10'): string
1315    {
1316        return $this->individualRepository->topTenOldestMaleListAlive((int) $total);
1317    }
1318
1319    /**
1320     * @param bool $show_years
1321     *
1322     * @return string
1323     */
1324    public function averageLifespan(bool $show_years = false): string
1325    {
1326        return $this->individualRepository->averageLifespan($show_years);
1327    }
1328
1329    /**
1330     * @param bool $show_years
1331     *
1332     * @return string
1333     */
1334    public function averageLifespanFemale(bool $show_years = false): string
1335    {
1336        return $this->individualRepository->averageLifespanFemale($show_years);
1337    }
1338
1339    /**
1340     * @param bool $show_years
1341     *
1342     * @return string
1343     */
1344    public function averageLifespanMale(bool $show_years = false): string
1345    {
1346        return $this->individualRepository->averageLifespanMale($show_years);
1347    }
1348
1349    /**
1350     * @return string
1351     */
1352    public function firstEvent(): string
1353    {
1354        return $this->eventRepository->firstEvent();
1355    }
1356
1357    /**
1358     * @return string
1359     */
1360    public function firstEventYear(): string
1361    {
1362        return $this->eventRepository->firstEventYear();
1363    }
1364
1365    /**
1366     * @return string
1367     */
1368    public function firstEventType(): string
1369    {
1370        return $this->eventRepository->firstEventType();
1371    }
1372
1373    /**
1374     * @return string
1375     */
1376    public function firstEventName(): string
1377    {
1378        return $this->eventRepository->firstEventName();
1379    }
1380
1381    /**
1382     * @return string
1383     */
1384    public function firstEventPlace(): string
1385    {
1386        return $this->eventRepository->firstEventPlace();
1387    }
1388
1389    /**
1390     * @return string
1391     */
1392    public function lastEvent(): string
1393    {
1394        return $this->eventRepository->lastEvent();
1395    }
1396
1397    /**
1398     * @return string
1399     */
1400    public function lastEventYear(): string
1401    {
1402        return $this->eventRepository->lastEventYear();
1403    }
1404
1405    /**
1406     * @return string
1407     */
1408    public function lastEventType(): string
1409    {
1410        return $this->eventRepository->lastEventType();
1411    }
1412
1413    /**
1414     * @return string
1415     */
1416    public function lastEventName(): string
1417    {
1418        return $this->eventRepository->lastEventName();
1419    }
1420
1421    /**
1422     * @return string
1423     */
1424    public function lastEventPlace(): string
1425    {
1426        return $this->eventRepository->lastEventType();
1427    }
1428
1429    /**
1430     * @return string
1431     */
1432    public function firstMarriage(): string
1433    {
1434        return $this->familyDatesRepository->firstMarriage();
1435    }
1436
1437    /**
1438     * @return string
1439     */
1440    public function firstMarriageYear(): string
1441    {
1442        return $this->familyDatesRepository->firstMarriageYear();
1443    }
1444
1445    /**
1446     * @return string
1447     */
1448    public function firstMarriageName(): string
1449    {
1450        return $this->familyDatesRepository->firstMarriageName();
1451    }
1452
1453    /**
1454     * @return string
1455     */
1456    public function firstMarriagePlace(): string
1457    {
1458        return $this->familyDatesRepository->firstMarriagePlace();
1459    }
1460
1461    /**
1462     * @return string
1463     */
1464    public function lastMarriage(): string
1465    {
1466        return $this->familyDatesRepository->lastMarriage();
1467    }
1468
1469    /**
1470     * @return string
1471     */
1472    public function lastMarriageYear(): string
1473    {
1474        return $this->familyDatesRepository->lastMarriageYear();
1475    }
1476
1477    /**
1478     * @return string
1479     */
1480    public function lastMarriageName(): string
1481    {
1482        return $this->familyDatesRepository->lastMarriageName();
1483    }
1484
1485    /**
1486     * @return string
1487     */
1488    public function lastMarriagePlace(): string
1489    {
1490        return $this->familyDatesRepository->lastMarriagePlace();
1491    }
1492
1493    /**
1494     * @param int $year1
1495     * @param int $year2
1496     *
1497     * @return Builder
1498     */
1499    public function statsMarriageQuery(int $year1 = -1, int $year2 = -1): Builder
1500    {
1501        return $this->familyRepository->statsMarriageQuery($year1, $year2);
1502    }
1503
1504    /**
1505     * @param int $year1
1506     * @param int $year2
1507     *
1508     * @return Builder
1509     */
1510    public function statsFirstMarriageQuery(int $year1 = -1, int $year2 = -1): Builder
1511    {
1512        return $this->familyRepository->statsFirstMarriageQuery($year1, $year2);
1513    }
1514
1515    /**
1516     * @param string|null $color_from
1517     * @param string|null $color_to
1518     *
1519     * @return string
1520     */
1521    public function statsMarr(string $color_from = null, string $color_to = null): string
1522    {
1523        return $this->familyRepository->statsMarr($color_from, $color_to);
1524    }
1525
1526    /**
1527     * @return string
1528     */
1529    public function firstDivorce(): string
1530    {
1531        return $this->familyDatesRepository->firstDivorce();
1532    }
1533
1534    /**
1535     * @return string
1536     */
1537    public function firstDivorceYear(): string
1538    {
1539        return $this->familyDatesRepository->firstDivorceYear();
1540    }
1541
1542    /**
1543     * @return string
1544     */
1545    public function firstDivorceName(): string
1546    {
1547        return $this->familyDatesRepository->firstDivorceName();
1548    }
1549
1550    /**
1551     * @return string
1552     */
1553    public function firstDivorcePlace(): string
1554    {
1555        return $this->familyDatesRepository->firstDivorcePlace();
1556    }
1557
1558    /**
1559     * @return string
1560     */
1561    public function lastDivorce(): string
1562    {
1563        return $this->familyDatesRepository->lastDivorce();
1564    }
1565
1566    /**
1567     * @return string
1568     */
1569    public function lastDivorceYear(): string
1570    {
1571        return $this->familyDatesRepository->lastDivorceYear();
1572    }
1573
1574    /**
1575     * @return string
1576     */
1577    public function lastDivorceName(): string
1578    {
1579        return $this->familyDatesRepository->lastDivorceName();
1580    }
1581
1582    /**
1583     * @return string
1584     */
1585    public function lastDivorcePlace(): string
1586    {
1587        return $this->familyDatesRepository->lastDivorcePlace();
1588    }
1589
1590    /**
1591     * @param string|null $color_from
1592     * @param string|null $color_to
1593     *
1594     * @return string
1595     */
1596    public function statsDiv(string $color_from = null, string $color_to = null): string
1597    {
1598        return $this->familyRepository->statsDiv($color_from, $color_to);
1599    }
1600
1601    /**
1602     * @return string
1603     */
1604    public function youngestMarriageFemale(): string
1605    {
1606        return $this->familyRepository->youngestMarriageFemale();
1607    }
1608
1609    /**
1610     * @return string
1611     */
1612    public function youngestMarriageFemaleName(): string
1613    {
1614        return $this->familyRepository->youngestMarriageFemaleName();
1615    }
1616
1617    /**
1618     * @param string $show_years
1619     *
1620     * @return string
1621     */
1622    public function youngestMarriageFemaleAge(string $show_years = ''): string
1623    {
1624        return $this->familyRepository->youngestMarriageFemaleAge($show_years);
1625    }
1626
1627    /**
1628     * @return string
1629     */
1630    public function oldestMarriageFemale(): string
1631    {
1632        return $this->familyRepository->oldestMarriageFemale();
1633    }
1634
1635    /**
1636     * @return string
1637     */
1638    public function oldestMarriageFemaleName(): string
1639    {
1640        return $this->familyRepository->oldestMarriageFemaleName();
1641    }
1642
1643    /**
1644     * @param string $show_years
1645     *
1646     * @return string
1647     */
1648    public function oldestMarriageFemaleAge(string $show_years = ''): string
1649    {
1650        return $this->familyRepository->oldestMarriageFemaleAge($show_years);
1651    }
1652
1653    /**
1654     * @return string
1655     */
1656    public function youngestMarriageMale(): string
1657    {
1658        return $this->familyRepository->youngestMarriageMale();
1659    }
1660
1661    /**
1662     * @return string
1663     */
1664    public function youngestMarriageMaleName(): string
1665    {
1666        return $this->familyRepository->youngestMarriageMaleName();
1667    }
1668
1669    /**
1670     * @param string $show_years
1671     *
1672     * @return string
1673     */
1674    public function youngestMarriageMaleAge(string $show_years = ''): string
1675    {
1676        return $this->familyRepository->youngestMarriageMaleAge($show_years);
1677    }
1678
1679    /**
1680     * @return string
1681     */
1682    public function oldestMarriageMale(): string
1683    {
1684        return $this->familyRepository->oldestMarriageMale();
1685    }
1686
1687    /**
1688     * @return string
1689     */
1690    public function oldestMarriageMaleName(): string
1691    {
1692        return $this->familyRepository->oldestMarriageMaleName();
1693    }
1694
1695    /**
1696     * @param string $show_years
1697     *
1698     * @return string
1699     */
1700    public function oldestMarriageMaleAge(string $show_years = ''): string
1701    {
1702        return $this->familyRepository->oldestMarriageMaleAge($show_years);
1703    }
1704
1705    /**
1706     * @param string $sex
1707     * @param int    $year1
1708     * @param int    $year2
1709     *
1710     * @return array
1711     */
1712    public function statsMarrAgeQuery(string $sex, int $year1 = -1, int $year2 = -1): array
1713    {
1714        return $this->familyRepository->statsMarrAgeQuery($sex, $year1, $year2);
1715    }
1716
1717    /**
1718     * @return string
1719     */
1720    public function statsMarrAge(): string
1721    {
1722        return $this->familyRepository->statsMarrAge();
1723    }
1724
1725    /**
1726     * @param string $total
1727     *
1728     * @return string
1729     */
1730    public function ageBetweenSpousesMF(string $total = '10'): string
1731    {
1732        return $this->familyRepository->ageBetweenSpousesMF((int) $total);
1733    }
1734
1735    /**
1736     * @param string $total
1737     *
1738     * @return string
1739     */
1740    public function ageBetweenSpousesMFList(string $total = '10'): string
1741    {
1742        return $this->familyRepository->ageBetweenSpousesMFList((int) $total);
1743    }
1744
1745    /**
1746     * @param string $total
1747     *
1748     * @return string
1749     */
1750    public function ageBetweenSpousesFM(string $total = '10'): string
1751    {
1752        return $this->familyRepository->ageBetweenSpousesFM((int) $total);
1753    }
1754
1755    /**
1756     * @param string $total
1757     *
1758     * @return string
1759     */
1760    public function ageBetweenSpousesFMList(string $total = '10'): string
1761    {
1762        return $this->familyRepository->ageBetweenSpousesFMList((int) $total);
1763    }
1764
1765    /**
1766     * @return string
1767     */
1768    public function topAgeOfMarriageFamily(): string
1769    {
1770        return $this->familyRepository->topAgeOfMarriageFamily();
1771    }
1772
1773    /**
1774     * @return string
1775     */
1776    public function topAgeOfMarriage(): string
1777    {
1778        return $this->familyRepository->topAgeOfMarriage();
1779    }
1780
1781    /**
1782     * @param string $total
1783     *
1784     * @return string
1785     */
1786    public function topAgeOfMarriageFamilies(string $total = '10'): string
1787    {
1788        return $this->familyRepository->topAgeOfMarriageFamilies((int) $total);
1789    }
1790
1791    /**
1792     * @param string $total
1793     *
1794     * @return string
1795     */
1796    public function topAgeOfMarriageFamiliesList(string $total = '10'): string
1797    {
1798        return $this->familyRepository->topAgeOfMarriageFamiliesList((int) $total);
1799    }
1800
1801    /**
1802     * @return string
1803     */
1804    public function minAgeOfMarriageFamily(): string
1805    {
1806        return $this->familyRepository->minAgeOfMarriageFamily();
1807    }
1808
1809    /**
1810     * @return string
1811     */
1812    public function minAgeOfMarriage(): string
1813    {
1814        return $this->familyRepository->minAgeOfMarriage();
1815    }
1816
1817    /**
1818     * @param string $total
1819     *
1820     * @return string
1821     */
1822    public function minAgeOfMarriageFamilies(string $total = '10'): string
1823    {
1824        return $this->familyRepository->minAgeOfMarriageFamilies((int) $total);
1825    }
1826
1827    /**
1828     * @param string $total
1829     *
1830     * @return string
1831     */
1832    public function minAgeOfMarriageFamiliesList(string $total = '10'): string
1833    {
1834        return $this->familyRepository->minAgeOfMarriageFamiliesList((int) $total);
1835    }
1836
1837    /**
1838     * @return string
1839     */
1840    public function youngestMother(): string
1841    {
1842        return $this->familyRepository->youngestMother();
1843    }
1844
1845    /**
1846     * @return string
1847     */
1848    public function youngestMotherName(): string
1849    {
1850        return $this->familyRepository->youngestMotherName();
1851    }
1852
1853    /**
1854     * @param string $show_years
1855     *
1856     * @return string
1857     */
1858    public function youngestMotherAge(string $show_years = ''): string
1859    {
1860        return $this->familyRepository->youngestMotherAge($show_years);
1861    }
1862
1863    /**
1864     * @return string
1865     */
1866    public function oldestMother(): string
1867    {
1868        return $this->familyRepository->oldestMother();
1869    }
1870
1871    /**
1872     * @return string
1873     */
1874    public function oldestMotherName(): string
1875    {
1876        return $this->familyRepository->oldestMotherName();
1877    }
1878
1879    /**
1880     * @param string $show_years
1881     *
1882     * @return string
1883     */
1884    public function oldestMotherAge(string $show_years = ''): string
1885    {
1886        return $this->familyRepository->oldestMotherAge($show_years);
1887    }
1888
1889    /**
1890     * @return string
1891     */
1892    public function youngestFather(): string
1893    {
1894        return $this->familyRepository->youngestFather();
1895    }
1896
1897    /**
1898     * @return string
1899     */
1900    public function youngestFatherName(): string
1901    {
1902        return $this->familyRepository->youngestFatherName();
1903    }
1904
1905    /**
1906     * @param string $show_years
1907     *
1908     * @return string
1909     */
1910    public function youngestFatherAge(string $show_years = ''): string
1911    {
1912        return $this->familyRepository->youngestFatherAge($show_years);
1913    }
1914
1915    /**
1916     * @return string
1917     */
1918    public function oldestFather(): string
1919    {
1920        return $this->familyRepository->oldestFather();
1921    }
1922
1923    /**
1924     * @return string
1925     */
1926    public function oldestFatherName(): string
1927    {
1928        return $this->familyRepository->oldestFatherName();
1929    }
1930
1931    /**
1932     * @param string $show_years
1933     *
1934     * @return string
1935     */
1936    public function oldestFatherAge(string $show_years = ''): string
1937    {
1938        return $this->familyRepository->oldestFatherAge($show_years);
1939    }
1940
1941    /**
1942     * @return string
1943     */
1944    public function totalMarriedMales(): string
1945    {
1946        return $this->familyRepository->totalMarriedMales();
1947    }
1948
1949    /**
1950     * @return string
1951     */
1952    public function totalMarriedFemales(): string
1953    {
1954        return $this->familyRepository->totalMarriedFemales();
1955    }
1956
1957    /**
1958     * @param int $year1
1959     * @param int $year2
1960     *
1961     * @return Builder
1962     */
1963    public function monthFirstChildQuery(int $year1 = -1, int $year2 = -1): Builder
1964    {
1965        return $this->familyRepository->monthFirstChildQuery($year1, $year2);
1966    }
1967
1968    /**
1969     * @param int $year1
1970     * @param int $year2
1971     *
1972     * @return Builder
1973     */
1974    public function monthFirstChildBySexQuery(int $year1 = -1, int $year2 = -1): Builder
1975    {
1976        return $this->familyRepository->monthFirstChildBySexQuery($year1, $year2);
1977    }
1978
1979    /**
1980     * @return string
1981     */
1982    public function largestFamily(): string
1983    {
1984        return $this->familyRepository->largestFamily();
1985    }
1986
1987    /**
1988     * @return string
1989     */
1990    public function largestFamilySize(): string
1991    {
1992        return $this->familyRepository->largestFamilySize();
1993    }
1994
1995    /**
1996     * @return string
1997     */
1998    public function largestFamilyName(): string
1999    {
2000        return $this->familyRepository->largestFamilyName();
2001    }
2002
2003    /**
2004     * @param string $total
2005     *
2006     * @return string
2007     */
2008    public function topTenLargestFamily(string $total = '10'): string
2009    {
2010        return $this->familyRepository->topTenLargestFamily((int) $total);
2011    }
2012
2013    /**
2014     * @param string $total
2015     *
2016     * @return string
2017     */
2018    public function topTenLargestFamilyList(string $total = '10'): string
2019    {
2020        return $this->familyRepository->topTenLargestFamilyList((int) $total);
2021    }
2022
2023    /**
2024     * @param string|null $color_from
2025     * @param string|null $color_to
2026     * @param string      $total
2027     *
2028     * @return string
2029     */
2030    public function chartLargestFamilies(
2031        string $color_from = null,
2032        string $color_to = null,
2033        string $total = '10'
2034    ): string {
2035        return $this->familyRepository->chartLargestFamilies($color_from, $color_to, (int) $total);
2036    }
2037
2038    /**
2039     * @return string
2040     */
2041    public function totalChildren(): string
2042    {
2043        return $this->familyRepository->totalChildren();
2044    }
2045
2046    /**
2047     * @return string
2048     */
2049    public function averageChildren(): string
2050    {
2051        return $this->familyRepository->averageChildren();
2052    }
2053
2054    /**
2055     * @param int $year1
2056     * @param int $year2
2057     *
2058     * @return array
2059     */
2060    public function statsChildrenQuery(int $year1 = -1, int $year2 = -1): array
2061    {
2062        return $this->familyRepository->statsChildrenQuery($year1, $year2);
2063    }
2064
2065    /**
2066     * @return string
2067     */
2068    public function statsChildren(): string
2069    {
2070        return $this->familyRepository->statsChildren();
2071    }
2072
2073    /**
2074     * @param string $total
2075     *
2076     * @return string
2077     */
2078    public function topAgeBetweenSiblingsName(string $total = '10'): string
2079    {
2080        return $this->familyRepository->topAgeBetweenSiblingsName((int) $total);
2081    }
2082
2083    /**
2084     * @param string $total
2085     *
2086     * @return string
2087     */
2088    public function topAgeBetweenSiblings(string $total = '10'): string
2089    {
2090        return $this->familyRepository->topAgeBetweenSiblings((int) $total);
2091    }
2092
2093    /**
2094     * @param string $total
2095     *
2096     * @return string
2097     */
2098    public function topAgeBetweenSiblingsFullName(string $total = '10'): string
2099    {
2100        return $this->familyRepository->topAgeBetweenSiblingsFullName((int) $total);
2101    }
2102
2103    /**
2104     * @param string $total
2105     * @param string $one
2106     *
2107     * @return string
2108     */
2109    public function topAgeBetweenSiblingsList(string $total = '10', string $one = ''): string
2110    {
2111        return $this->familyRepository->topAgeBetweenSiblingsList((int) $total, $one);
2112    }
2113
2114    /**
2115     * @return string
2116     */
2117    public function noChildrenFamilies(): string
2118    {
2119        return $this->familyRepository->noChildrenFamilies();
2120    }
2121
2122    /**
2123     * @param string $type
2124     *
2125     * @return string
2126     */
2127    public function noChildrenFamiliesList(string $type = 'list'): string
2128    {
2129        return $this->familyRepository->noChildrenFamiliesList($type);
2130    }
2131
2132    /**
2133     * @param string $year1
2134     * @param string $year2
2135     *
2136     * @return string
2137     */
2138    public function chartNoChildrenFamilies(
2139        string $year1 = '-1',
2140        string $year2 = '-1'
2141    ): string {
2142        return $this->familyRepository->chartNoChildrenFamilies((int) $year1, (int) $year2);
2143    }
2144
2145    /**
2146     * @param string $total
2147     *
2148     * @return string
2149     */
2150    public function topTenLargestGrandFamily(string $total = '10'): string
2151    {
2152        return $this->familyRepository->topTenLargestGrandFamily((int) $total);
2153    }
2154
2155    /**
2156     * @param string $total
2157     *
2158     * @return string
2159     */
2160    public function topTenLargestGrandFamilyList(string $total = '10'): string
2161    {
2162        return $this->familyRepository->topTenLargestGrandFamilyList((int) $total);
2163    }
2164
2165    /**
2166     * @return string
2167     */
2168    public function getCommonSurname(): string
2169    {
2170        return $this->individualRepository->getCommonSurname();
2171    }
2172
2173    /**
2174     * @param string $threshold
2175     * @param string $number_of_surnames
2176     * @param string $sorting
2177     *
2178     * @return string
2179     */
2180    public function commonSurnames(
2181        string $threshold = '1',
2182        string $number_of_surnames = '10',
2183        string $sorting = 'alpha'
2184    ): string {
2185        return $this->individualRepository->commonSurnames((int) $threshold, (int) $number_of_surnames, $sorting);
2186    }
2187
2188    /**
2189     * @param string $threshold
2190     * @param string $number_of_surnames
2191     * @param string $sorting
2192     *
2193     * @return string
2194     */
2195    public function commonSurnamesTotals(
2196        string $threshold = '1',
2197        string $number_of_surnames = '10',
2198        string $sorting = 'count'
2199    ): string {
2200        return $this->individualRepository->commonSurnamesTotals((int) $threshold, (int) $number_of_surnames, $sorting);
2201    }
2202
2203    /**
2204     * @param string $threshold
2205     * @param string $number_of_surnames
2206     * @param string $sorting
2207     *
2208     * @return string
2209     */
2210    public function commonSurnamesList(
2211        string $threshold = '1',
2212        string $number_of_surnames = '10',
2213        string $sorting = 'alpha'
2214    ): string {
2215        return $this->individualRepository->commonSurnamesList((int) $threshold, (int) $number_of_surnames, $sorting);
2216    }
2217
2218    /**
2219     * @param string $threshold
2220     * @param string $number_of_surnames
2221     * @param string $sorting
2222     *
2223     * @return string
2224     */
2225    public function commonSurnamesListTotals(
2226        string $threshold = '1',
2227        string $number_of_surnames = '10',
2228        string $sorting = 'count'
2229    ): string {
2230        return $this->individualRepository
2231            ->commonSurnamesListTotals((int) $threshold, (int) $number_of_surnames, $sorting);
2232    }
2233
2234    /**
2235     * @param string|null $color_from
2236     * @param string|null $color_to
2237     * @param string      $number_of_surnames
2238     *
2239     * @return string
2240     */
2241    public function chartCommonSurnames(
2242        string $color_from = null,
2243        string $color_to = null,
2244        string $number_of_surnames = '10'
2245    ): string {
2246        return $this->individualRepository
2247            ->chartCommonSurnames($color_from, $color_to, (int) $number_of_surnames);
2248    }
2249
2250    /**
2251     * @param string $threshold
2252     * @param string $maxtoshow
2253     *
2254     * @return string
2255     */
2256    public function commonGiven(string $threshold = '1', string $maxtoshow = '10'): string
2257    {
2258        return $this->individualRepository->commonGiven((int) $threshold, (int) $maxtoshow);
2259    }
2260
2261    /**
2262     * @param string $threshold
2263     * @param string $maxtoshow
2264     *
2265     * @return string
2266     */
2267    public function commonGivenTotals(string $threshold = '1', string $maxtoshow = '10'): string
2268    {
2269        return $this->individualRepository->commonGivenTotals((int) $threshold, (int) $maxtoshow);
2270    }
2271
2272    /**
2273     * @param string $threshold
2274     * @param string $maxtoshow
2275     *
2276     * @return string
2277     */
2278    public function commonGivenList(string $threshold = '1', string $maxtoshow = '10'): string
2279    {
2280        return $this->individualRepository->commonGivenList((int) $threshold, (int) $maxtoshow);
2281    }
2282
2283    /**
2284     * @param string $threshold
2285     * @param string $maxtoshow
2286     *
2287     * @return string
2288     */
2289    public function commonGivenListTotals(string $threshold = '1', string $maxtoshow = '10'): string
2290    {
2291        return $this->individualRepository->commonGivenListTotals((int) $threshold, (int) $maxtoshow);
2292    }
2293
2294    /**
2295     * @param string $threshold
2296     * @param string $maxtoshow
2297     *
2298     * @return string
2299     */
2300    public function commonGivenTable(string $threshold = '1', string $maxtoshow = '10'): string
2301    {
2302        return $this->individualRepository->commonGivenTable((int) $threshold, (int) $maxtoshow);
2303    }
2304
2305    /**
2306     * @param string $threshold
2307     * @param string $maxtoshow
2308     *
2309     * @return string
2310     */
2311    public function commonGivenFemale(string $threshold = '1', string $maxtoshow = '10'): string
2312    {
2313        return $this->individualRepository->commonGivenFemale((int) $threshold, (int) $maxtoshow);
2314    }
2315
2316    /**
2317     * @param string $threshold
2318     * @param string $maxtoshow
2319     *
2320     * @return string
2321     */
2322    public function commonGivenFemaleTotals(string $threshold = '1', string $maxtoshow = '10'): string
2323    {
2324        return $this->individualRepository->commonGivenFemaleTotals((int) $threshold, (int) $maxtoshow);
2325    }
2326
2327    /**
2328     * @param string $threshold
2329     * @param string $maxtoshow
2330     *
2331     * @return string
2332     */
2333    public function commonGivenFemaleList(string $threshold = '1', string $maxtoshow = '10'): string
2334    {
2335        return $this->individualRepository->commonGivenFemaleList((int) $threshold, (int) $maxtoshow);
2336    }
2337
2338    /**
2339     * @param string $threshold
2340     * @param string $maxtoshow
2341     *
2342     * @return string
2343     */
2344    public function commonGivenFemaleListTotals(string $threshold = '1', string $maxtoshow = '10'): string
2345    {
2346        return $this->individualRepository->commonGivenFemaleListTotals((int) $threshold, (int) $maxtoshow);
2347    }
2348
2349    /**
2350     * @param string $threshold
2351     * @param string $maxtoshow
2352     *
2353     * @return string
2354     */
2355    public function commonGivenFemaleTable(string $threshold = '1', string $maxtoshow = '10'): string
2356    {
2357        return $this->individualRepository->commonGivenFemaleTable((int) $threshold, (int) $maxtoshow);
2358    }
2359
2360    /**
2361     * @param string $threshold
2362     * @param string $maxtoshow
2363     *
2364     * @return string
2365     */
2366    public function commonGivenMale(string $threshold = '1', string $maxtoshow = '10'): string
2367    {
2368        return $this->individualRepository->commonGivenMale((int) $threshold, (int) $maxtoshow);
2369    }
2370
2371    /**
2372     * @param string $threshold
2373     * @param string $maxtoshow
2374     *
2375     * @return string
2376     */
2377    public function commonGivenMaleTotals(string $threshold = '1', string $maxtoshow = '10'): string
2378    {
2379        return $this->individualRepository->commonGivenMaleTotals((int) $threshold, (int) $maxtoshow);
2380    }
2381
2382    /**
2383     * @param string $threshold
2384     * @param string $maxtoshow
2385     *
2386     * @return string
2387     */
2388    public function commonGivenMaleList(string $threshold = '1', string $maxtoshow = '10'): string
2389    {
2390        return $this->individualRepository->commonGivenMaleList((int) $threshold, (int) $maxtoshow);
2391    }
2392
2393    /**
2394     * @param string $threshold
2395     * @param string $maxtoshow
2396     *
2397     * @return string
2398     */
2399    public function commonGivenMaleListTotals(string $threshold = '1', string $maxtoshow = '10'): string
2400    {
2401        return $this->individualRepository->commonGivenMaleListTotals((int) $threshold, (int) $maxtoshow);
2402    }
2403
2404    /**
2405     * @param string $threshold
2406     * @param string $maxtoshow
2407     *
2408     * @return string
2409     */
2410    public function commonGivenMaleTable(string $threshold = '1', string $maxtoshow = '10'): string
2411    {
2412        return $this->individualRepository->commonGivenMaleTable((int) $threshold, (int) $maxtoshow);
2413    }
2414
2415    /**
2416     * @param string $threshold
2417     * @param string $maxtoshow
2418     *
2419     * @return string
2420     */
2421    public function commonGivenUnknown(string $threshold = '1', string $maxtoshow = '10'): string
2422    {
2423        return $this->individualRepository->commonGivenUnknown((int) $threshold, (int) $maxtoshow);
2424    }
2425
2426    /**
2427     * @param string $threshold
2428     * @param string $maxtoshow
2429     *
2430     * @return string
2431     */
2432    public function commonGivenUnknownTotals(string $threshold = '1', string $maxtoshow = '10'): string
2433    {
2434        return $this->individualRepository->commonGivenUnknownTotals((int) $threshold, (int) $maxtoshow);
2435    }
2436
2437    /**
2438     * @param string $threshold
2439     * @param string $maxtoshow
2440     *
2441     * @return string
2442     */
2443    public function commonGivenUnknownList(string $threshold = '1', string $maxtoshow = '10'): string
2444    {
2445        return $this->individualRepository->commonGivenUnknownList((int) $threshold, (int) $maxtoshow);
2446    }
2447
2448    /**
2449     * @param string $threshold
2450     * @param string $maxtoshow
2451     *
2452     * @return string
2453     */
2454    public function commonGivenUnknownListTotals(string $threshold = '1', string $maxtoshow = '10'): string
2455    {
2456        return $this->individualRepository->commonGivenUnknownListTotals((int) $threshold, (int) $maxtoshow);
2457    }
2458
2459    /**
2460     * @param string $threshold
2461     * @param string $maxtoshow
2462     *
2463     * @return string
2464     */
2465    public function commonGivenUnknownTable(string $threshold = '1', string $maxtoshow = '10'): string
2466    {
2467        return $this->individualRepository->commonGivenUnknownTable((int) $threshold, (int) $maxtoshow);
2468    }
2469
2470    /**
2471     * @param string|null $color_from
2472     * @param string|null $color_to
2473     * @param string      $maxtoshow
2474     *
2475     * @return string
2476     */
2477    public function chartCommonGiven(
2478        string $color_from = null,
2479        string $color_to = null,
2480        string $maxtoshow = '7'
2481    ): string {
2482        return $this->individualRepository->chartCommonGiven($color_from, $color_to, (int) $maxtoshow);
2483    }
2484
2485    /**
2486     * @return string
2487     */
2488    public function usersLoggedIn(): string
2489    {
2490        return $this->userRepository->usersLoggedIn();
2491    }
2492
2493    /**
2494     * @return string
2495     */
2496    public function usersLoggedInList(): string
2497    {
2498        return $this->userRepository->usersLoggedInList();
2499    }
2500
2501    /**
2502     * @return int
2503     */
2504    public function usersLoggedInTotal(): int
2505    {
2506        return $this->userRepository->usersLoggedInTotal();
2507    }
2508
2509    /**
2510     * @return int
2511     */
2512    public function usersLoggedInTotalAnon(): int
2513    {
2514        return $this->userRepository->usersLoggedInTotalAnon();
2515    }
2516
2517    /**
2518     * @return int
2519     */
2520    public function usersLoggedInTotalVisible(): int
2521    {
2522        return $this->userRepository->usersLoggedInTotalVisible();
2523    }
2524
2525    /**
2526     * @return string
2527     */
2528    public function userId(): string
2529    {
2530        return $this->userRepository->userId();
2531    }
2532
2533    /**
2534     * @param string $visitor_text
2535     *
2536     * @return string
2537     */
2538    public function userName(string $visitor_text = ''): string
2539    {
2540        return $this->userRepository->userName($visitor_text);
2541    }
2542
2543    /**
2544     * @return string
2545     */
2546    public function userFullName(): string
2547    {
2548        return $this->userRepository->userFullName();
2549    }
2550
2551    /**
2552     * @return string
2553     */
2554    public function totalUsers(): string
2555    {
2556        return $this->userRepository->totalUsers();
2557    }
2558
2559    /**
2560     * @return string
2561     */
2562    public function totalAdmins(): string
2563    {
2564        return $this->userRepository->totalAdmins();
2565    }
2566
2567    /**
2568     * @return string
2569     */
2570    public function totalNonAdmins(): string
2571    {
2572        return $this->userRepository->totalNonAdmins();
2573    }
2574
2575    /**
2576     * @return string
2577     */
2578    public function latestUserId(): string
2579    {
2580        return $this->latestUserRepository->latestUserId();
2581    }
2582
2583    /**
2584     * @return string
2585     */
2586    public function latestUserName(): string
2587    {
2588        return $this->latestUserRepository->latestUserName();
2589    }
2590
2591    /**
2592     * @return string
2593     */
2594    public function latestUserFullName(): string
2595    {
2596        return $this->latestUserRepository->latestUserFullName();
2597    }
2598
2599    /**
2600     * @param string|null $format
2601     *
2602     * @return string
2603     */
2604    public function latestUserRegDate(string $format = null): string
2605    {
2606        return $this->latestUserRepository->latestUserRegDate($format);
2607    }
2608
2609    /**
2610     * @param string|null $format
2611     *
2612     * @return string
2613     */
2614    public function latestUserRegTime(string $format = null): string
2615    {
2616        return $this->latestUserRepository->latestUserRegTime($format);
2617    }
2618
2619    /**
2620     * @param string|null $yes
2621     * @param string|null $no
2622     *
2623     * @return string
2624     */
2625    public function latestUserLoggedin(string $yes = null, string $no = null): string
2626    {
2627        return $this->latestUserRepository->latestUserLoggedin($yes, $no);
2628    }
2629
2630    /**
2631     * @return string
2632     */
2633    public function contactWebmaster(): string
2634    {
2635        return $this->contactRepository->contactWebmaster();
2636    }
2637
2638    /**
2639     * @return string
2640     */
2641    public function contactGedcom(): string
2642    {
2643        return $this->contactRepository->contactGedcom();
2644    }
2645
2646    /**
2647     * @return string
2648     */
2649    public function serverDate(): string
2650    {
2651        return $this->serverRepository->serverDate();
2652    }
2653
2654    /**
2655     * @return string
2656     */
2657    public function serverTime(): string
2658    {
2659        return $this->serverRepository->serverTime();
2660    }
2661
2662    /**
2663     * @return string
2664     */
2665    public function serverTime24(): string
2666    {
2667        return $this->serverRepository->serverTime24();
2668    }
2669
2670    /**
2671     * What is the timezone of the server.
2672     *
2673     * @return string
2674     */
2675    public function serverTimezone(): string
2676    {
2677        return $this->serverRepository->serverTimezone();
2678    }
2679
2680    /**
2681     * @return string
2682     */
2683    public function browserDate(): string
2684    {
2685        return $this->browserRepository->browserDate();
2686    }
2687
2688    /**
2689     * @return string
2690     */
2691    public function browserTime(): string
2692    {
2693        return $this->browserRepository->browserTime();
2694    }
2695
2696    /**
2697     * @return string
2698     */
2699    public function browserTimezone(): string
2700    {
2701        return $this->browserRepository->browserTimezone();
2702    }
2703
2704    /**
2705     * @param string $page_parameter
2706     *
2707     * @return string
2708     */
2709    public function hitCount(string $page_parameter = ''): string
2710    {
2711        return $this->hitCountRepository->hitCount($page_parameter);
2712    }
2713
2714    /**
2715     * @param string $page_parameter
2716     *
2717     * @return string
2718     */
2719    public function hitCountUser(string $page_parameter = ''): string
2720    {
2721        return $this->hitCountRepository->hitCountUser($page_parameter);
2722    }
2723
2724    /**
2725     * @param string $page_parameter
2726     *
2727     * @return string
2728     */
2729    public function hitCountIndi(string $page_parameter = ''): string
2730    {
2731        return $this->hitCountRepository->hitCountIndi($page_parameter);
2732    }
2733
2734    /**
2735     * @param string $page_parameter
2736     *
2737     * @return string
2738     */
2739    public function hitCountFam(string $page_parameter = ''): string
2740    {
2741        return $this->hitCountRepository->hitCountFam($page_parameter);
2742    }
2743
2744    /**
2745     * @param string $page_parameter
2746     *
2747     * @return string
2748     */
2749    public function hitCountSour(string $page_parameter = ''): string
2750    {
2751        return $this->hitCountRepository->hitCountSour($page_parameter);
2752    }
2753
2754    /**
2755     * @param string $page_parameter
2756     *
2757     * @return string
2758     */
2759    public function hitCountRepo(string $page_parameter = ''): string
2760    {
2761        return $this->hitCountRepository->hitCountRepo($page_parameter);
2762    }
2763
2764    /**
2765     * @param string $page_parameter
2766     *
2767     * @return string
2768     */
2769    public function hitCountNote(string $page_parameter = ''): string
2770    {
2771        return $this->hitCountRepository->hitCountNote($page_parameter);
2772    }
2773
2774    /**
2775     * @param string $page_parameter
2776     *
2777     * @return string
2778     */
2779    public function hitCountObje(string $page_parameter = ''): string
2780    {
2781        return $this->hitCountRepository->hitCountObje($page_parameter);
2782    }
2783
2784    /**
2785     * @return string
2786     */
2787    public function gedcomFavorites(): string
2788    {
2789        return $this->favoritesRepository->gedcomFavorites();
2790    }
2791
2792    /**
2793     * @return string
2794     */
2795    public function userFavorites(): string
2796    {
2797        return $this->favoritesRepository->userFavorites();
2798    }
2799
2800    /**
2801     * @return string
2802     */
2803    public function totalGedcomFavorites(): string
2804    {
2805        return $this->favoritesRepository->totalGedcomFavorites();
2806    }
2807
2808    /**
2809     * @return string
2810     */
2811    public function totalUserFavorites(): string
2812    {
2813        return $this->favoritesRepository->totalUserFavorites();
2814    }
2815
2816    /**
2817     * @return string
2818     */
2819    public function totalUserMessages(): string
2820    {
2821        return $this->messageRepository->totalUserMessages();
2822    }
2823
2824    /**
2825     * @return string
2826     */
2827    public function totalUserJournal(): string
2828    {
2829        return $this->newsRepository->totalUserJournal();
2830    }
2831
2832    /**
2833     * @return string
2834     */
2835    public function totalGedcomNews(): string
2836    {
2837        return $this->newsRepository->totalGedcomNews();
2838    }
2839
2840    /**
2841     * Create any of the other blocks.
2842     * Use as #callBlock:block_name#
2843     *
2844     * @param string $block
2845     * @param string ...$params
2846     *
2847     * @return string|null
2848     */
2849    public function callBlock(string $block = '', ...$params): ?string
2850    {
2851        /** @var ModuleBlockInterface|null $module */
2852        $module = $this->module_service
2853            ->findByComponent(ModuleBlockInterface::class, $this->tree, Auth::user())
2854            ->first(static function (ModuleInterface $module) use ($block): bool {
2855                return $module->name() === $block && $module->name() !== 'html';
2856            });
2857
2858        if ($module === null) {
2859            return '';
2860        }
2861
2862        // Build the config array
2863        $cfg = [];
2864        foreach ($params as $config) {
2865            $bits = explode('=', $config);
2866
2867            if (count($bits) < 2) {
2868                continue;
2869            }
2870
2871            $v       = array_shift($bits);
2872            $cfg[$v] = implode('=', $bits);
2873        }
2874
2875        return $module->getBlock($this->tree, 0, ModuleBlockInterface::CONTEXT_EMBED, $cfg);
2876    }
2877
2878    /**
2879     * What is the current version of webtrees.
2880     *
2881     * @return string
2882     */
2883    public function webtreesVersion(): string
2884    {
2885        return Webtrees::VERSION;
2886    }
2887
2888    /**
2889     * Get tags and their parsed results.
2890     *
2891     * @param string $text
2892     *
2893     * @return array<string>
2894     */
2895    private function getTags(string $text): array
2896    {
2897        $tags    = [];
2898        $matches = [];
2899
2900        preg_match_all('/#([^#\n]+)(?=#)/', $text, $matches, PREG_SET_ORDER);
2901
2902        foreach ($matches as $match) {
2903            $params = explode(':', $match[1]);
2904            $method = array_shift($params);
2905
2906            if (method_exists($this, $method)) {
2907                $tags[$match[0] . '#'] = call_user_func([$this, $method], ...$params);
2908            }
2909        }
2910
2911        return $tags;
2912    }
2913}
2914