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