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