xref: /webtrees/app/Services/IndividualFactsService.php (revision 449b311ecf65f677a2595e1e29f712d11ef22f34)
12c066a59SGreg Roach<?php
22c066a59SGreg Roach
32c066a59SGreg Roach/**
42c066a59SGreg Roach * webtrees: online genealogy
5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team
62c066a59SGreg Roach * This program is free software: you can redistribute it and/or modify
72c066a59SGreg Roach * it under the terms of the GNU General Public License as published by
82c066a59SGreg Roach * the Free Software Foundation, either version 3 of the License, or
92c066a59SGreg Roach * (at your option) any later version.
102c066a59SGreg Roach * This program is distributed in the hope that it will be useful,
112c066a59SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
122c066a59SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
132c066a59SGreg Roach * GNU General Public License for more details.
142c066a59SGreg Roach * You should have received a copy of the GNU General Public License
152c066a59SGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
162c066a59SGreg Roach */
172c066a59SGreg Roach
182c066a59SGreg Roachdeclare(strict_types=1);
192c066a59SGreg Roach
202c066a59SGreg Roachnamespace Fisharebest\Webtrees\Services;
212c066a59SGreg Roach
222c066a59SGreg Roachuse Fisharebest\Webtrees\Date;
232c066a59SGreg Roachuse Fisharebest\Webtrees\Fact;
242c066a59SGreg Roachuse Fisharebest\Webtrees\Family;
252c066a59SGreg Roachuse Fisharebest\Webtrees\I18N;
262c066a59SGreg Roachuse Fisharebest\Webtrees\Individual;
272c066a59SGreg Roachuse Fisharebest\Webtrees\Module\ModuleHistoricEventsInterface;
282c066a59SGreg Roachuse Illuminate\Support\Collection;
292c066a59SGreg Roach
302c066a59SGreg Roachuse function explode;
312c066a59SGreg Roachuse function preg_match;
322c066a59SGreg Roachuse function preg_replace;
332c066a59SGreg Roachuse function str_replace;
342c066a59SGreg Roach
352c066a59SGreg Roach/**
362c066a59SGreg Roach * Provide lists of facts for IndividualFactsTabModule.
372c066a59SGreg Roach */
382c066a59SGreg Roachclass IndividualFactsService
392c066a59SGreg Roach{
402c066a59SGreg Roach    private LinkedRecordService $linked_record_service;
412c066a59SGreg Roach
422c066a59SGreg Roach    private ModuleService $module_service;
432c066a59SGreg Roach
442c066a59SGreg Roach    /**
452c066a59SGreg Roach     * @param LinkedRecordService $linked_record_service
462c066a59SGreg Roach     * @param ModuleService       $module_service
472c066a59SGreg Roach     */
482c066a59SGreg Roach    public function __construct(
492c066a59SGreg Roach        LinkedRecordService $linked_record_service,
502c066a59SGreg Roach        ModuleService $module_service
512c066a59SGreg Roach    ) {
522c066a59SGreg Roach        $this->linked_record_service = $linked_record_service;
532c066a59SGreg Roach        $this->module_service        = $module_service;
542c066a59SGreg Roach    }
552c066a59SGreg Roach
562c066a59SGreg Roach    /**
572c066a59SGreg Roach     * The individuals own facts, such as birth and death.
582c066a59SGreg Roach     *
592c066a59SGreg Roach     * @param Individual             $individual
602c066a59SGreg Roach     * @param Collection<int,string> $exclude_facts
612c066a59SGreg Roach     *
62ccbecad5SGreg Roach     * @return Collection<int,Fact>
632c066a59SGreg Roach     */
642c066a59SGreg Roach    public function individualFacts(Individual $individual, Collection $exclude_facts): Collection
652c066a59SGreg Roach    {
662c066a59SGreg Roach        return $individual->facts()
672c066a59SGreg Roach            ->filter(fn (Fact $fact): bool => !$exclude_facts->contains($fact->tag()));
682c066a59SGreg Roach    }
692c066a59SGreg Roach
702c066a59SGreg Roach    /**
712c066a59SGreg Roach     * The individuals own family facts, such as marriage and divorce.
722c066a59SGreg Roach     *
732c066a59SGreg Roach     * @param Individual             $individual
742c066a59SGreg Roach     * @param Collection<int,string> $exclude_facts
752c066a59SGreg Roach     *
76ccbecad5SGreg Roach     * @return Collection<int,Fact>
772c066a59SGreg Roach     */
782c066a59SGreg Roach    public function familyFacts(Individual $individual, Collection $exclude_facts): Collection
792c066a59SGreg Roach    {
802c066a59SGreg Roach        return $individual->spouseFamilies()
812c066a59SGreg Roach            ->map(fn (Family $family): Collection => $family->facts())
822c066a59SGreg Roach            ->flatten()
832c066a59SGreg Roach            ->filter(fn (Fact $fact): bool => !$exclude_facts->contains($fact->tag()));
842c066a59SGreg Roach    }
852c066a59SGreg Roach
862c066a59SGreg Roach    /**
872c066a59SGreg Roach     * Get the events of associates.
882c066a59SGreg Roach     *
892c066a59SGreg Roach     * @param Individual $individual
902c066a59SGreg Roach     *
912c066a59SGreg Roach     * @return Collection<int,Fact>
922c066a59SGreg Roach     */
932c066a59SGreg Roach    public function associateFacts(Individual $individual): Collection
942c066a59SGreg Roach    {
952c066a59SGreg Roach        $facts = [];
962c066a59SGreg Roach
972c066a59SGreg Roach        $asso1 = $this->linked_record_service->linkedIndividuals($individual, 'ASSO');
982c066a59SGreg Roach        $asso2 = $this->linked_record_service->linkedIndividuals($individual, '_ASSO');
992c066a59SGreg Roach        $asso3 = $this->linked_record_service->linkedFamilies($individual, 'ASSO');
1002c066a59SGreg Roach        $asso4 = $this->linked_record_service->linkedFamilies($individual, '_ASSO');
1012c066a59SGreg Roach
1022c066a59SGreg Roach        $associates = $asso1->merge($asso2)->merge($asso3)->merge($asso4);
1032c066a59SGreg Roach
1042c066a59SGreg Roach        foreach ($associates as $associate) {
1052c066a59SGreg Roach            foreach ($associate->facts() as $fact) {
1062c066a59SGreg Roach                if (preg_match('/\n\d _?ASSO @' . $individual->xref() . '@/', $fact->gedcom())) {
1072c066a59SGreg Roach                    // Extract the important details from the fact
1082c066a59SGreg Roach                    $factrec = explode("\n", $fact->gedcom(), 2)[0];
1092c066a59SGreg Roach                    if (preg_match('/\n2 DATE .*/', $fact->gedcom(), $match)) {
1102c066a59SGreg Roach                        $factrec .= $match[0];
1112c066a59SGreg Roach                    }
1122c066a59SGreg Roach                    if (preg_match('/\n2 PLAC .*/', $fact->gedcom(), $match)) {
1132c066a59SGreg Roach                        $factrec .= $match[0];
1142c066a59SGreg Roach                    }
1152c066a59SGreg Roach                    if ($associate instanceof Family) {
1162c066a59SGreg Roach                        foreach ($associate->spouses() as $spouse) {
1172c066a59SGreg Roach                            $factrec .= "\n2 _ASSO @" . $spouse->xref() . '@';
1182c066a59SGreg Roach                        }
1192c066a59SGreg Roach                    } else {
1202c066a59SGreg Roach                        $factrec .= "\n2 _ASSO @" . $associate->xref() . '@';
1212c066a59SGreg Roach                    }
1222c066a59SGreg Roach                    $facts[] = new Fact($factrec, $associate, 'asso');
1232c066a59SGreg Roach                }
1242c066a59SGreg Roach            }
1252c066a59SGreg Roach        }
1262c066a59SGreg Roach
1272c066a59SGreg Roach        return new Collection($facts);
1282c066a59SGreg Roach    }
1292c066a59SGreg Roach
1302c066a59SGreg Roach    /**
1312c066a59SGreg Roach     * Get the events of close relatives.
1322c066a59SGreg Roach     *
1332c066a59SGreg Roach     * @param Individual $individual
1342c066a59SGreg Roach     *
1352c066a59SGreg Roach     * @return Collection<int,Fact>
1362c066a59SGreg Roach     */
1372c066a59SGreg Roach    public function relativeFacts(Individual $individual): Collection
1382c066a59SGreg Roach    {
1392c066a59SGreg Roach        // Only include events of close relatives that are between birth and death
1402c066a59SGreg Roach        $min_date = $individual->getEstimatedBirthDate();
1412c066a59SGreg Roach        $max_date = $individual->getEstimatedDeathDate();
1422c066a59SGreg Roach
1432c066a59SGreg Roach        $parent_facts = $this->parentFacts($individual, 1, $min_date, $max_date);
1442c066a59SGreg Roach
1452c066a59SGreg Roach        $spouse_facts = $individual->spouseFamilies()
1462c066a59SGreg Roach            ->filter(fn (Family $family): bool => $family->spouse($individual) instanceof Individual)
1472c066a59SGreg Roach            ->map(fn (Family $family): Collection => $this->spouseFacts($individual, $family->spouse($individual), $min_date, $max_date))
1482c066a59SGreg Roach            ->flatten();
1492c066a59SGreg Roach
1502c066a59SGreg Roach        $child_facts = $individual->spouseFamilies()
1512c066a59SGreg Roach            ->map(fn (Family $family): Collection => $this->childFacts($individual, $family, '_CHIL', '', $min_date, $max_date))
1522c066a59SGreg Roach            ->flatten();
1532c066a59SGreg Roach
1542c066a59SGreg Roach        return $parent_facts
1552c066a59SGreg Roach            ->merge($child_facts)
156357f854eSGreg Roach            ->merge($spouse_facts)
157357f854eSGreg Roach            ->unique();
1582c066a59SGreg Roach    }
1592c066a59SGreg Roach
1602c066a59SGreg Roach    /**
1612c066a59SGreg Roach     * Get any historical events.
1622c066a59SGreg Roach     *
1632c066a59SGreg Roach     * @param Individual $individual
1642c066a59SGreg Roach     *
1652c066a59SGreg Roach     * @return Collection<int,Fact>
1662c066a59SGreg Roach     */
1672c066a59SGreg Roach    public function historicFacts(Individual $individual): Collection
1682c066a59SGreg Roach    {
1692c066a59SGreg Roach        return $this->module_service->findByInterface(ModuleHistoricEventsInterface::class)
170*f25fc0f9SGreg Roach            ->map(static fn (ModuleHistoricEventsInterface $module): Collection => $module->historicEventsForIndividual($individual))
1712c066a59SGreg Roach            ->flatten();
1722c066a59SGreg Roach    }
1732c066a59SGreg Roach
1742c066a59SGreg Roach    /**
1752c066a59SGreg Roach     * Get the events of children and grandchildren.
1762c066a59SGreg Roach     *
1772c066a59SGreg Roach     * @param Individual $person
1782c066a59SGreg Roach     * @param Family     $family
1792c066a59SGreg Roach     * @param string     $option
1802c066a59SGreg Roach     * @param string     $relation
1812c066a59SGreg Roach     * @param Date       $min_date
1822c066a59SGreg Roach     * @param Date       $max_date
1832c066a59SGreg Roach     *
1842c066a59SGreg Roach     * @return Collection<int,Fact>
1852c066a59SGreg Roach     */
1862c066a59SGreg Roach    private function childFacts(Individual $person, Family $family, string $option, string $relation, Date $min_date, Date $max_date): Collection
1872c066a59SGreg Roach    {
1882c066a59SGreg Roach        $SHOW_RELATIVES_EVENTS = $person->tree()->getPreference('SHOW_RELATIVES_EVENTS');
1892c066a59SGreg Roach
1902c066a59SGreg Roach        $birth_of_a_child = [
1912c066a59SGreg Roach            'INDI:BIRT' => [
1922c066a59SGreg Roach                'M' => I18N::translate('Birth of a son'),
1932c066a59SGreg Roach                'F' => I18N::translate('Birth of a daughter'),
1942c066a59SGreg Roach                'U' => I18N::translate('Birth of a child'),
1952c066a59SGreg Roach            ],
1962c066a59SGreg Roach            'INDI:CHR'  => [
1972c066a59SGreg Roach                'M' => I18N::translate('Christening of a son'),
1982c066a59SGreg Roach                'F' => I18N::translate('Christening of a daughter'),
1992c066a59SGreg Roach                'U' => I18N::translate('Christening of a child'),
2002c066a59SGreg Roach            ],
2012c066a59SGreg Roach            'INDI:BAPM' => [
2022c066a59SGreg Roach                'M' => I18N::translate('Baptism of a son'),
2032c066a59SGreg Roach                'F' => I18N::translate('Baptism of a daughter'),
2042c066a59SGreg Roach                'U' => I18N::translate('Baptism of a child'),
2052c066a59SGreg Roach            ],
2062c066a59SGreg Roach            'INDI:ADOP' => [
2072c066a59SGreg Roach                'M' => I18N::translate('Adoption of a son'),
2082c066a59SGreg Roach                'F' => I18N::translate('Adoption of a daughter'),
2092c066a59SGreg Roach                'U' => I18N::translate('Adoption of a child'),
2102c066a59SGreg Roach            ],
2112c066a59SGreg Roach        ];
2122c066a59SGreg Roach
2132c066a59SGreg Roach        $birth_of_a_sibling = [
2142c066a59SGreg Roach            'INDI:BIRT' => [
2152c066a59SGreg Roach                'M' => I18N::translate('Birth of a brother'),
2162c066a59SGreg Roach                'F' => I18N::translate('Birth of a sister'),
2172c066a59SGreg Roach                'U' => I18N::translate('Birth of a sibling'),
2182c066a59SGreg Roach            ],
2192c066a59SGreg Roach            'INDI:CHR'  => [
2202c066a59SGreg Roach                'M' => I18N::translate('Christening of a brother'),
2212c066a59SGreg Roach                'F' => I18N::translate('Christening of a sister'),
2222c066a59SGreg Roach                'U' => I18N::translate('Christening of a sibling'),
2232c066a59SGreg Roach            ],
2242c066a59SGreg Roach            'INDI:BAPM' => [
2252c066a59SGreg Roach                'M' => I18N::translate('Baptism of a brother'),
2262c066a59SGreg Roach                'F' => I18N::translate('Baptism of a sister'),
2272c066a59SGreg Roach                'U' => I18N::translate('Baptism of a sibling'),
2282c066a59SGreg Roach            ],
2292c066a59SGreg Roach            'INDI:ADOP' => [
2302c066a59SGreg Roach                'M' => I18N::translate('Adoption of a brother'),
2312c066a59SGreg Roach                'F' => I18N::translate('Adoption of a sister'),
2322c066a59SGreg Roach                'U' => I18N::translate('Adoption of a sibling'),
2332c066a59SGreg Roach            ],
2342c066a59SGreg Roach        ];
2352c066a59SGreg Roach
2362c066a59SGreg Roach        $birth_of_a_half_sibling = [
2372c066a59SGreg Roach            'INDI:BIRT' => [
2382c066a59SGreg Roach                'M' => I18N::translate('Birth of a half-brother'),
2392c066a59SGreg Roach                'F' => I18N::translate('Birth of a half-sister'),
2402c066a59SGreg Roach                'U' => I18N::translate('Birth of a half-sibling'),
2412c066a59SGreg Roach            ],
2422c066a59SGreg Roach            'INDI:CHR'  => [
2432c066a59SGreg Roach                'M' => I18N::translate('Christening of a half-brother'),
2442c066a59SGreg Roach                'F' => I18N::translate('Christening of a half-sister'),
2452c066a59SGreg Roach                'U' => I18N::translate('Christening of a half-sibling'),
2462c066a59SGreg Roach            ],
2472c066a59SGreg Roach            'INDI:BAPM' => [
2482c066a59SGreg Roach                'M' => I18N::translate('Baptism of a half-brother'),
2492c066a59SGreg Roach                'F' => I18N::translate('Baptism of a half-sister'),
2502c066a59SGreg Roach                'U' => I18N::translate('Baptism of a half-sibling'),
2512c066a59SGreg Roach            ],
2522c066a59SGreg Roach            'INDI:ADOP' => [
2532c066a59SGreg Roach                'M' => I18N::translate('Adoption of a half-brother'),
2542c066a59SGreg Roach                'F' => I18N::translate('Adoption of a half-sister'),
2552c066a59SGreg Roach                'U' => I18N::translate('Adoption of a half-sibling'),
2562c066a59SGreg Roach            ],
2572c066a59SGreg Roach        ];
2582c066a59SGreg Roach
2592c066a59SGreg Roach        $birth_of_a_grandchild = [
2602c066a59SGreg Roach            'INDI:BIRT' => [
2612c066a59SGreg Roach                'M' => I18N::translate('Birth of a grandson'),
2622c066a59SGreg Roach                'F' => I18N::translate('Birth of a granddaughter'),
2632c066a59SGreg Roach                'U' => I18N::translate('Birth of a grandchild'),
2642c066a59SGreg Roach            ],
2652c066a59SGreg Roach            'INDI:CHR'  => [
2662c066a59SGreg Roach                'M' => I18N::translate('Christening of a grandson'),
2672c066a59SGreg Roach                'F' => I18N::translate('Christening of a granddaughter'),
2682c066a59SGreg Roach                'U' => I18N::translate('Christening of a grandchild'),
2692c066a59SGreg Roach            ],
2702c066a59SGreg Roach            'INDI:BAPM' => [
2712c066a59SGreg Roach                'M' => I18N::translate('Baptism of a grandson'),
2722c066a59SGreg Roach                'F' => I18N::translate('Baptism of a granddaughter'),
2732c066a59SGreg Roach                'U' => I18N::translate('Baptism of a grandchild'),
2742c066a59SGreg Roach            ],
2752c066a59SGreg Roach            'INDI:ADOP' => [
2762c066a59SGreg Roach                'M' => I18N::translate('Adoption of a grandson'),
2772c066a59SGreg Roach                'F' => I18N::translate('Adoption of a granddaughter'),
2782c066a59SGreg Roach                'U' => I18N::translate('Adoption of a grandchild'),
2792c066a59SGreg Roach            ],
2802c066a59SGreg Roach        ];
2812c066a59SGreg Roach
2822c066a59SGreg Roach        $birth_of_a_grandchild1 = [
2832c066a59SGreg Roach            'INDI:BIRT' => [
2842c066a59SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Birth of a grandson'),
2852c066a59SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Birth of a granddaughter'),
2862c066a59SGreg Roach                'U' => I18N::translate('Birth of a grandchild'),
2872c066a59SGreg Roach            ],
2882c066a59SGreg Roach            'INDI:CHR'  => [
2892c066a59SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Christening of a grandson'),
2902c066a59SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Christening of a granddaughter'),
2912c066a59SGreg Roach                'U' => I18N::translate('Christening of a grandchild'),
2922c066a59SGreg Roach            ],
2932c066a59SGreg Roach            'INDI:BAPM' => [
2942c066a59SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Baptism of a grandson'),
2952c066a59SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Baptism of a granddaughter'),
2962c066a59SGreg Roach                'U' => I18N::translate('Baptism of a grandchild'),
2972c066a59SGreg Roach            ],
2982c066a59SGreg Roach            'INDI:ADOP' => [
2992c066a59SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Adoption of a grandson'),
3002c066a59SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Adoption of a granddaughter'),
3012c066a59SGreg Roach                'U' => I18N::translate('Adoption of a grandchild'),
3022c066a59SGreg Roach            ],
3032c066a59SGreg Roach        ];
3042c066a59SGreg Roach
3052c066a59SGreg Roach        $birth_of_a_grandchild2 = [
3062c066a59SGreg Roach            'INDI:BIRT' => [
3072c066a59SGreg Roach                'M' => I18N::translateContext('son’s son', 'Birth of a grandson'),
3082c066a59SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Birth of a granddaughter'),
3092c066a59SGreg Roach                'U' => I18N::translate('Birth of a grandchild'),
3102c066a59SGreg Roach            ],
3112c066a59SGreg Roach            'INDI:CHR'  => [
3122c066a59SGreg Roach                'M' => I18N::translateContext('son’s son', 'Christening of a grandson'),
3132c066a59SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Christening of a granddaughter'),
3142c066a59SGreg Roach                'U' => I18N::translate('Christening of a grandchild'),
3152c066a59SGreg Roach            ],
3162c066a59SGreg Roach            'INDI:BAPM' => [
3172c066a59SGreg Roach                'M' => I18N::translateContext('son’s son', 'Baptism of a grandson'),
3182c066a59SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Baptism of a granddaughter'),
3192c066a59SGreg Roach                'U' => I18N::translate('Baptism of a grandchild'),
3202c066a59SGreg Roach            ],
3212c066a59SGreg Roach            'INDI:ADOP' => [
3222c066a59SGreg Roach                'M' => I18N::translateContext('son’s son', 'Adoption of a grandson'),
3232c066a59SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Adoption of a granddaughter'),
3242c066a59SGreg Roach                'U' => I18N::translate('Adoption of a grandchild'),
3252c066a59SGreg Roach            ],
3262c066a59SGreg Roach        ];
3272c066a59SGreg Roach
3282c066a59SGreg Roach        $death_of_a_child = [
3292c066a59SGreg Roach            'INDI:DEAT' => [
3302c066a59SGreg Roach                'M' => I18N::translate('Death of a son'),
3312c066a59SGreg Roach                'F' => I18N::translate('Death of a daughter'),
3322c066a59SGreg Roach                'U' => I18N::translate('Death of a child'),
3332c066a59SGreg Roach            ],
3342c066a59SGreg Roach            'INDI:BURI' => [
3352c066a59SGreg Roach                'M' => I18N::translate('Burial of a son'),
3362c066a59SGreg Roach                'F' => I18N::translate('Burial of a daughter'),
3372c066a59SGreg Roach                'U' => I18N::translate('Burial of a child'),
3382c066a59SGreg Roach            ],
3392c066a59SGreg Roach            'INDI:CREM' => [
3402c066a59SGreg Roach                'M' => I18N::translate('Cremation of a son'),
3412c066a59SGreg Roach                'F' => I18N::translate('Cremation of a daughter'),
3422c066a59SGreg Roach                'U' => I18N::translate('Cremation of a child'),
3432c066a59SGreg Roach            ],
3442c066a59SGreg Roach        ];
3452c066a59SGreg Roach
3462c066a59SGreg Roach        $death_of_a_sibling = [
3472c066a59SGreg Roach            'INDI:DEAT' => [
3482c066a59SGreg Roach                'M' => I18N::translate('Death of a brother'),
3492c066a59SGreg Roach                'F' => I18N::translate('Death of a sister'),
3502c066a59SGreg Roach                'U' => I18N::translate('Death of a sibling'),
3512c066a59SGreg Roach            ],
3522c066a59SGreg Roach            'INDI:BURI' => [
3532c066a59SGreg Roach                'M' => I18N::translate('Burial of a brother'),
3542c066a59SGreg Roach                'F' => I18N::translate('Burial of a sister'),
3552c066a59SGreg Roach                'U' => I18N::translate('Burial of a sibling'),
3562c066a59SGreg Roach            ],
3572c066a59SGreg Roach            'INDI:CREM' => [
3582c066a59SGreg Roach                'M' => I18N::translate('Cremation of a brother'),
3592c066a59SGreg Roach                'F' => I18N::translate('Cremation of a sister'),
3602c066a59SGreg Roach                'U' => I18N::translate('Cremation of a sibling'),
3612c066a59SGreg Roach            ],
3622c066a59SGreg Roach        ];
3632c066a59SGreg Roach
3642c066a59SGreg Roach        $death_of_a_half_sibling = [
3652c066a59SGreg Roach            'INDI:DEAT' => [
3662c066a59SGreg Roach                'M' => I18N::translate('Death of a half-brother'),
3672c066a59SGreg Roach                'F' => I18N::translate('Death of a half-sister'),
3682c066a59SGreg Roach                'U' => I18N::translate('Death of a half-sibling'),
3692c066a59SGreg Roach            ],
3702c066a59SGreg Roach            'INDI:BURI' => [
3712c066a59SGreg Roach                'M' => I18N::translate('Burial of a half-brother'),
3722c066a59SGreg Roach                'F' => I18N::translate('Burial of a half-sister'),
3732c066a59SGreg Roach                'U' => I18N::translate('Burial of a half-sibling'),
3742c066a59SGreg Roach            ],
3752c066a59SGreg Roach            'INDI:CREM' => [
3762c066a59SGreg Roach                'M' => I18N::translate('Cremation of a half-brother'),
3772c066a59SGreg Roach                'F' => I18N::translate('Cremation of a half-sister'),
3782c066a59SGreg Roach                'U' => I18N::translate('Cremation of a half-sibling'),
3792c066a59SGreg Roach            ],
3802c066a59SGreg Roach        ];
3812c066a59SGreg Roach
3822c066a59SGreg Roach        $death_of_a_grandchild = [
3832c066a59SGreg Roach            'INDI:DEAT' => [
3842c066a59SGreg Roach                'M' => I18N::translate('Death of a grandson'),
3852c066a59SGreg Roach                'F' => I18N::translate('Death of a granddaughter'),
3862c066a59SGreg Roach                'U' => I18N::translate('Death of a grandchild'),
3872c066a59SGreg Roach            ],
3882c066a59SGreg Roach            'INDI:BURI' => [
3892c066a59SGreg Roach                'M' => I18N::translate('Burial of a grandson'),
3902c066a59SGreg Roach                'F' => I18N::translate('Burial of a granddaughter'),
3912c066a59SGreg Roach                'U' => I18N::translate('Burial of a grandchild'),
3922c066a59SGreg Roach            ],
3932c066a59SGreg Roach            'INDI:CREM' => [
3942c066a59SGreg Roach                'M' => I18N::translate('Cremation of a grandson'),
3952c066a59SGreg Roach                'F' => I18N::translate('Cremation of a granddaughter'),
3962c066a59SGreg Roach                'U' => I18N::translate('Baptism of a grandchild'),
3972c066a59SGreg Roach            ],
3982c066a59SGreg Roach        ];
3992c066a59SGreg Roach
4002c066a59SGreg Roach        $death_of_a_grandchild1 = [
4012c066a59SGreg Roach            'INDI:DEAT' => [
4022c066a59SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Death of a grandson'),
4032c066a59SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Death of a granddaughter'),
4042c066a59SGreg Roach                'U' => I18N::translate('Death of a grandchild'),
4052c066a59SGreg Roach            ],
4062c066a59SGreg Roach            'INDI:BURI' => [
4072c066a59SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Burial of a grandson'),
4082c066a59SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Burial of a granddaughter'),
4092c066a59SGreg Roach                'U' => I18N::translate('Burial of a grandchild'),
4102c066a59SGreg Roach            ],
4112c066a59SGreg Roach            'INDI:CREM' => [
4122c066a59SGreg Roach                'M' => I18N::translateContext('daughter’s son', 'Cremation of a grandson'),
4132c066a59SGreg Roach                'F' => I18N::translateContext('daughter’s daughter', 'Cremation of a granddaughter'),
4142c066a59SGreg Roach                'U' => I18N::translate('Baptism of a grandchild'),
4152c066a59SGreg Roach            ],
4162c066a59SGreg Roach        ];
4172c066a59SGreg Roach
4182c066a59SGreg Roach        $death_of_a_grandchild2 = [
4192c066a59SGreg Roach            'INDI:DEAT' => [
4202c066a59SGreg Roach                'M' => I18N::translateContext('son’s son', 'Death of a grandson'),
4212c066a59SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Death of a granddaughter'),
4222c066a59SGreg Roach                'U' => I18N::translate('Death of a grandchild'),
4232c066a59SGreg Roach            ],
4242c066a59SGreg Roach            'INDI:BURI' => [
4252c066a59SGreg Roach                'M' => I18N::translateContext('son’s son', 'Burial of a grandson'),
4262c066a59SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Burial of a granddaughter'),
4272c066a59SGreg Roach                'U' => I18N::translate('Burial of a grandchild'),
4282c066a59SGreg Roach            ],
4292c066a59SGreg Roach            'INDI:CREM' => [
4302c066a59SGreg Roach                'M' => I18N::translateContext('son’s son', 'Cremation of a grandson'),
4312c066a59SGreg Roach                'F' => I18N::translateContext('son’s daughter', 'Cremation of a granddaughter'),
4322c066a59SGreg Roach                'U' => I18N::translate('Cremation of a grandchild'),
4332c066a59SGreg Roach            ],
4342c066a59SGreg Roach        ];
4352c066a59SGreg Roach
4362c066a59SGreg Roach        $marriage_of_a_child = [
4372c066a59SGreg Roach            'M' => I18N::translate('Marriage of a son'),
4382c066a59SGreg Roach            'F' => I18N::translate('Marriage of a daughter'),
4392c066a59SGreg Roach            'U' => I18N::translate('Marriage of a child'),
4402c066a59SGreg Roach        ];
4412c066a59SGreg Roach
4422c066a59SGreg Roach        $marriage_of_a_grandchild = [
4432c066a59SGreg Roach            'M' => I18N::translate('Marriage of a grandson'),
4442c066a59SGreg Roach            'F' => I18N::translate('Marriage of a granddaughter'),
4452c066a59SGreg Roach            'U' => I18N::translate('Marriage of a grandchild'),
4462c066a59SGreg Roach        ];
4472c066a59SGreg Roach
4482c066a59SGreg Roach        $marriage_of_a_grandchild1 = [
4492c066a59SGreg Roach            'M' => I18N::translateContext('daughter’s son', 'Marriage of a grandson'),
4502c066a59SGreg Roach            'F' => I18N::translateContext('daughter’s daughter', 'Marriage of a granddaughter'),
4512c066a59SGreg Roach            'U' => I18N::translate('Marriage of a grandchild'),
4522c066a59SGreg Roach        ];
4532c066a59SGreg Roach
4542c066a59SGreg Roach        $marriage_of_a_grandchild2 = [
4552c066a59SGreg Roach            'M' => I18N::translateContext('son’s son', 'Marriage of a grandson'),
4562c066a59SGreg Roach            'F' => I18N::translateContext('son’s daughter', 'Marriage of a granddaughter'),
4572c066a59SGreg Roach            'U' => I18N::translate('Marriage of a grandchild'),
4582c066a59SGreg Roach        ];
4592c066a59SGreg Roach
4602c066a59SGreg Roach        $marriage_of_a_sibling = [
4612c066a59SGreg Roach            'M' => I18N::translate('Marriage of a brother'),
4622c066a59SGreg Roach            'F' => I18N::translate('Marriage of a sister'),
4632c066a59SGreg Roach            'U' => I18N::translate('Marriage of a sibling'),
4642c066a59SGreg Roach        ];
4652c066a59SGreg Roach
4662c066a59SGreg Roach        $marriage_of_a_half_sibling = [
4672c066a59SGreg Roach            'M' => I18N::translate('Marriage of a half-brother'),
4682c066a59SGreg Roach            'F' => I18N::translate('Marriage of a half-sister'),
4692c066a59SGreg Roach            'U' => I18N::translate('Marriage of a half-sibling'),
4702c066a59SGreg Roach        ];
4712c066a59SGreg Roach
4722c066a59SGreg Roach        $facts = new Collection();
4732c066a59SGreg Roach
4742c066a59SGreg Roach        // Deal with recursion.
4752c066a59SGreg Roach        if ($option === '_CHIL') {
4762c066a59SGreg Roach            // Add grandchildren
4772c066a59SGreg Roach            foreach ($family->children() as $child) {
4782c066a59SGreg Roach                foreach ($child->spouseFamilies() as $cfamily) {
4792c066a59SGreg Roach                    switch ($child->sex()) {
4802c066a59SGreg Roach                        case 'M':
4812c066a59SGreg Roach                            foreach ($this->childFacts($person, $cfamily, '_GCHI', 'son', $min_date, $max_date) as $fact) {
4822c066a59SGreg Roach                                $facts[] = $fact;
4832c066a59SGreg Roach                            }
4842c066a59SGreg Roach                            break;
4852c066a59SGreg Roach                        case 'F':
4862c066a59SGreg Roach                            foreach ($this->childFacts($person, $cfamily, '_GCHI', 'dau', $min_date, $max_date) as $fact) {
4872c066a59SGreg Roach                                $facts[] = $fact;
4882c066a59SGreg Roach                            }
4892c066a59SGreg Roach                            break;
4902c066a59SGreg Roach                        default:
4912c066a59SGreg Roach                            foreach ($this->childFacts($person, $cfamily, '_GCHI', 'chi', $min_date, $max_date) as $fact) {
4922c066a59SGreg Roach                                $facts[] = $fact;
4932c066a59SGreg Roach                            }
4942c066a59SGreg Roach                            break;
4952c066a59SGreg Roach                    }
4962c066a59SGreg Roach                }
4972c066a59SGreg Roach            }
4982c066a59SGreg Roach        }
4992c066a59SGreg Roach
5002c066a59SGreg Roach        // For each child in the family
5012c066a59SGreg Roach        foreach ($family->children() as $child) {
5022c066a59SGreg Roach            if ($child->xref() === $person->xref()) {
5032c066a59SGreg Roach                // We are not our own sibling!
5042c066a59SGreg Roach                continue;
5052c066a59SGreg Roach            }
5062c066a59SGreg Roach            // add child’s birth
5072c066a59SGreg Roach            if (str_contains($SHOW_RELATIVES_EVENTS, '_BIRT' . str_replace('_HSIB', '_SIBL', $option))) {
5082c066a59SGreg Roach                foreach ($child->facts(['BIRT', 'CHR', 'BAPM', 'ADOP']) as $fact) {
5092c066a59SGreg Roach                    // Always show _BIRT_CHIL, even if the dates are not known
5102c066a59SGreg Roach                    if ($option === '_CHIL' || $this->includeFact($fact, $min_date, $max_date)) {
5112c066a59SGreg Roach                        switch ($option) {
5122c066a59SGreg Roach                            case '_GCHI':
5132c066a59SGreg Roach                                switch ($relation) {
5142c066a59SGreg Roach                                    case 'dau':
5152c066a59SGreg Roach                                        $facts[] = $this->convertEvent($fact, $birth_of_a_grandchild1[$fact->tag()], $fact->record()->sex());
5162c066a59SGreg Roach                                        break;
5172c066a59SGreg Roach                                    case 'son':
5182c066a59SGreg Roach                                        $facts[] = $this->convertEvent($fact, $birth_of_a_grandchild2[$fact->tag()], $fact->record()->sex());
5192c066a59SGreg Roach                                        break;
5202c066a59SGreg Roach                                    case 'chil':
5212c066a59SGreg Roach                                        $facts[] = $this->convertEvent($fact, $birth_of_a_grandchild[$fact->tag()], $fact->record()->sex());
5222c066a59SGreg Roach                                        break;
5232c066a59SGreg Roach                                }
5242c066a59SGreg Roach                                break;
5252c066a59SGreg Roach                            case '_SIBL':
5262c066a59SGreg Roach                                $facts[] = $this->convertEvent($fact, $birth_of_a_sibling[$fact->tag()], $fact->record()->sex());
5272c066a59SGreg Roach                                break;
5282c066a59SGreg Roach                            case '_HSIB':
5292c066a59SGreg Roach                                $facts[] = $this->convertEvent($fact, $birth_of_a_half_sibling[$fact->tag()], $fact->record()->sex());
5302c066a59SGreg Roach                                break;
5312c066a59SGreg Roach                            case '_CHIL':
5322c066a59SGreg Roach                                $facts[] = $this->convertEvent($fact, $birth_of_a_child[$fact->tag()], $fact->record()->sex());
5332c066a59SGreg Roach                                break;
5342c066a59SGreg Roach                        }
5352c066a59SGreg Roach                    }
5362c066a59SGreg Roach                }
5372c066a59SGreg Roach            }
5382c066a59SGreg Roach            // add child’s death
5392c066a59SGreg Roach            if (str_contains($SHOW_RELATIVES_EVENTS, '_DEAT' . str_replace('_HSIB', '_SIBL', $option))) {
5402c066a59SGreg Roach                foreach ($child->facts(['DEAT', 'BURI', 'CREM']) as $fact) {
5412c066a59SGreg Roach                    if ($this->includeFact($fact, $min_date, $max_date)) {
5422c066a59SGreg Roach                        switch ($option) {
5432c066a59SGreg Roach                            case '_GCHI':
5442c066a59SGreg Roach                                switch ($relation) {
5452c066a59SGreg Roach                                    case 'dau':
5462c066a59SGreg Roach                                        $facts[] = $this->convertEvent($fact, $death_of_a_grandchild1[$fact->tag()], $fact->record()->sex());
5472c066a59SGreg Roach                                        break;
5482c066a59SGreg Roach                                    case 'son':
5492c066a59SGreg Roach                                        $facts[] = $this->convertEvent($fact, $death_of_a_grandchild2[$fact->tag()], $fact->record()->sex());
5502c066a59SGreg Roach                                        break;
5512c066a59SGreg Roach                                    case 'chi':
5522c066a59SGreg Roach                                        $facts[] = $this->convertEvent($fact, $death_of_a_grandchild[$fact->tag()], $fact->record()->sex());
5532c066a59SGreg Roach                                        break;
5542c066a59SGreg Roach                                }
5552c066a59SGreg Roach                                break;
5562c066a59SGreg Roach                            case '_SIBL':
5572c066a59SGreg Roach                                $facts[] = $this->convertEvent($fact, $death_of_a_sibling[$fact->tag()], $fact->record()->sex());
5582c066a59SGreg Roach                                break;
5592c066a59SGreg Roach                            case '_HSIB':
5602c066a59SGreg Roach                                $facts[] = $this->convertEvent($fact, $death_of_a_half_sibling[$fact->tag()], $fact->record()->sex());
5612c066a59SGreg Roach                                break;
5622c066a59SGreg Roach                            case '_CHIL':
5632c066a59SGreg Roach                                $facts[] = $this->convertEvent($fact, $death_of_a_child[$fact->tag()], $fact->record()->sex());
5642c066a59SGreg Roach                                break;
5652c066a59SGreg Roach                        }
5662c066a59SGreg Roach                    }
5672c066a59SGreg Roach                }
5682c066a59SGreg Roach            }
5692c066a59SGreg Roach
5702c066a59SGreg Roach            // add child’s marriage
5712c066a59SGreg Roach            if (str_contains($SHOW_RELATIVES_EVENTS, '_MARR' . str_replace('_HSIB', '_SIBL', $option))) {
5722c066a59SGreg Roach                foreach ($child->spouseFamilies() as $sfamily) {
5732c066a59SGreg Roach                    foreach ($sfamily->facts(['MARR']) as $fact) {
5742c066a59SGreg Roach                        if ($this->includeFact($fact, $min_date, $max_date)) {
5752c066a59SGreg Roach                            switch ($option) {
5762c066a59SGreg Roach                                case '_GCHI':
5772c066a59SGreg Roach                                    switch ($relation) {
5782c066a59SGreg Roach                                        case 'dau':
5792c066a59SGreg Roach                                            $facts[] = $this->convertEvent($fact, $marriage_of_a_grandchild1, $child->sex());
5802c066a59SGreg Roach                                            break;
5812c066a59SGreg Roach                                        case 'son':
5822c066a59SGreg Roach                                            $facts[] = $this->convertEvent($fact, $marriage_of_a_grandchild2, $child->sex());
5832c066a59SGreg Roach                                            break;
5842c066a59SGreg Roach                                        case 'chi':
5852c066a59SGreg Roach                                            $facts[] = $this->convertEvent($fact, $marriage_of_a_grandchild, $child->sex());
5862c066a59SGreg Roach                                            break;
5872c066a59SGreg Roach                                    }
5882c066a59SGreg Roach                                    break;
5892c066a59SGreg Roach                                case '_SIBL':
5902c066a59SGreg Roach                                    $facts[] = $this->convertEvent($fact, $marriage_of_a_sibling, $child->sex());
5912c066a59SGreg Roach                                    break;
5922c066a59SGreg Roach                                case '_HSIB':
5932c066a59SGreg Roach                                    $facts[] = $this->convertEvent($fact, $marriage_of_a_half_sibling, $child->sex());
5942c066a59SGreg Roach                                    break;
5952c066a59SGreg Roach                                case '_CHIL':
5962c066a59SGreg Roach                                    $facts[] = $this->convertEvent($fact, $marriage_of_a_child, $child->sex());
5972c066a59SGreg Roach                                    break;
5982c066a59SGreg Roach                            }
5992c066a59SGreg Roach                        }
6002c066a59SGreg Roach                    }
6012c066a59SGreg Roach                }
6022c066a59SGreg Roach            }
6032c066a59SGreg Roach        }
6042c066a59SGreg Roach
6052c066a59SGreg Roach        return $facts;
6062c066a59SGreg Roach    }
6072c066a59SGreg Roach
6082c066a59SGreg Roach    /**
6092c066a59SGreg Roach     * Get the events of parents and grandparents.
6102c066a59SGreg Roach     *
6112c066a59SGreg Roach     * @param Individual $person
6122c066a59SGreg Roach     * @param int        $sosa
6132c066a59SGreg Roach     * @param Date       $min_date
6142c066a59SGreg Roach     * @param Date       $max_date
6152c066a59SGreg Roach     *
6162c066a59SGreg Roach     * @return Collection<int,Fact>
6172c066a59SGreg Roach     */
6182c066a59SGreg Roach    private function parentFacts(Individual $person, int $sosa, Date $min_date, Date $max_date): Collection
6192c066a59SGreg Roach    {
6202c066a59SGreg Roach        $SHOW_RELATIVES_EVENTS = $person->tree()->getPreference('SHOW_RELATIVES_EVENTS');
6212c066a59SGreg Roach
6222c066a59SGreg Roach        $death_of_a_parent = [
6232c066a59SGreg Roach            'INDI:DEAT' => [
6242c066a59SGreg Roach                'M' => I18N::translate('Death of a father'),
6252c066a59SGreg Roach                'F' => I18N::translate('Death of a mother'),
6262c066a59SGreg Roach                'U' => I18N::translate('Death of a parent'),
6272c066a59SGreg Roach            ],
6282c066a59SGreg Roach            'INDI:BURI' => [
6292c066a59SGreg Roach                'M' => I18N::translate('Burial of a father'),
6302c066a59SGreg Roach                'F' => I18N::translate('Burial of a mother'),
6312c066a59SGreg Roach                'U' => I18N::translate('Burial of a parent'),
6322c066a59SGreg Roach            ],
6332c066a59SGreg Roach            'INDI:CREM' => [
6342c066a59SGreg Roach                'M' => I18N::translate('Cremation of a father'),
6352c066a59SGreg Roach                'F' => I18N::translate('Cremation of a mother'),
6362c066a59SGreg Roach                'U' => I18N::translate('Cremation of a parent'),
6372c066a59SGreg Roach            ],
6382c066a59SGreg Roach        ];
6392c066a59SGreg Roach
6402c066a59SGreg Roach        $death_of_a_grandparent = [
6412c066a59SGreg Roach            'INDI:DEAT' => [
6422c066a59SGreg Roach                'M' => I18N::translate('Death of a grandfather'),
6432c066a59SGreg Roach                'F' => I18N::translate('Death of a grandmother'),
6442c066a59SGreg Roach                'U' => I18N::translate('Death of a grandparent'),
6452c066a59SGreg Roach            ],
6462c066a59SGreg Roach            'INDI:BURI' => [
6472c066a59SGreg Roach                'M' => I18N::translate('Burial of a grandfather'),
6482c066a59SGreg Roach                'F' => I18N::translate('Burial of a grandmother'),
6492c066a59SGreg Roach                'U' => I18N::translate('Burial of a grandparent'),
6502c066a59SGreg Roach            ],
6512c066a59SGreg Roach            'INDI:CREM' => [
6522c066a59SGreg Roach                'M' => I18N::translate('Cremation of a grandfather'),
6532c066a59SGreg Roach                'F' => I18N::translate('Cremation of a grandmother'),
6542c066a59SGreg Roach                'U' => I18N::translate('Cremation of a grandparent'),
6552c066a59SGreg Roach            ],
6562c066a59SGreg Roach        ];
6572c066a59SGreg Roach
6582c066a59SGreg Roach        $death_of_a_maternal_grandparent = [
6592c066a59SGreg Roach            'INDI:DEAT' => [
6602c066a59SGreg Roach                'M' => I18N::translate('Death of a maternal grandfather'),
6612c066a59SGreg Roach                'F' => I18N::translate('Death of a maternal grandmother'),
6622c066a59SGreg Roach                'U' => I18N::translate('Death of a grandparent'),
6632c066a59SGreg Roach            ],
6642c066a59SGreg Roach            'INDI:BURI' => [
6652c066a59SGreg Roach                'M' => I18N::translate('Burial of a maternal grandfather'),
6662c066a59SGreg Roach                'F' => I18N::translate('Burial of a maternal grandmother'),
6672c066a59SGreg Roach                'U' => I18N::translate('Burial of a grandparent'),
6682c066a59SGreg Roach            ],
6692c066a59SGreg Roach            'INDI:CREM' => [
6702c066a59SGreg Roach                'M' => I18N::translate('Cremation of a maternal grandfather'),
6712c066a59SGreg Roach                'F' => I18N::translate('Cremation of a maternal grandmother'),
6722c066a59SGreg Roach                'U' => I18N::translate('Cremation of a grandparent'),
6732c066a59SGreg Roach            ],
6742c066a59SGreg Roach        ];
6752c066a59SGreg Roach
6762c066a59SGreg Roach        $death_of_a_paternal_grandparent = [
6772c066a59SGreg Roach            'INDI:DEAT' => [
6782c066a59SGreg Roach                'M' => I18N::translate('Death of a paternal grandfather'),
6792c066a59SGreg Roach                'F' => I18N::translate('Death of a paternal grandmother'),
6802c066a59SGreg Roach                'U' => I18N::translate('Death of a grandparent'),
6812c066a59SGreg Roach            ],
6822c066a59SGreg Roach            'INDI:BURI' => [
6832c066a59SGreg Roach                'M' => I18N::translate('Burial of a paternal grandfather'),
6842c066a59SGreg Roach                'F' => I18N::translate('Burial of a paternal grandmother'),
6852c066a59SGreg Roach                'U' => I18N::translate('Burial of a grandparent'),
6862c066a59SGreg Roach            ],
6872c066a59SGreg Roach            'INDI:CREM' => [
6882c066a59SGreg Roach                'M' => I18N::translate('Cremation of a paternal grandfather'),
6892c066a59SGreg Roach                'F' => I18N::translate('Cremation of a paternal grandmother'),
6902c066a59SGreg Roach                'U' => I18N::translate('Cremation of a grandparent'),
6912c066a59SGreg Roach            ],
6922c066a59SGreg Roach        ];
6932c066a59SGreg Roach
6942c066a59SGreg Roach        $marriage_of_a_parent = [
6952c066a59SGreg Roach            'M' => I18N::translate('Marriage of a father'),
6962c066a59SGreg Roach            'F' => I18N::translate('Marriage of a mother'),
6972c066a59SGreg Roach            'U' => I18N::translate('Marriage of a parent'),
6982c066a59SGreg Roach        ];
6992c066a59SGreg Roach
7002c066a59SGreg Roach        $facts = new Collection();
7012c066a59SGreg Roach
7022c066a59SGreg Roach        if ($sosa === 1) {
7032c066a59SGreg Roach            foreach ($person->childFamilies() as $family) {
7042c066a59SGreg Roach                // Add siblings
7052c066a59SGreg Roach                foreach ($this->childFacts($person, $family, '_SIBL', '', $min_date, $max_date) as $fact) {
7062c066a59SGreg Roach                    $facts[] = $fact;
7072c066a59SGreg Roach                }
7082c066a59SGreg Roach                foreach ($family->spouses() as $spouse) {
7092c066a59SGreg Roach                    foreach ($spouse->spouseFamilies() as $sfamily) {
7102c066a59SGreg Roach                        if ($family !== $sfamily) {
7112c066a59SGreg Roach                            // Add half-siblings
7122c066a59SGreg Roach                            foreach ($this->childFacts($person, $sfamily, '_HSIB', '', $min_date, $max_date) as $fact) {
7132c066a59SGreg Roach                                $facts[] = $fact;
7142c066a59SGreg Roach                            }
7152c066a59SGreg Roach                        }
7162c066a59SGreg Roach                    }
7172c066a59SGreg Roach                    // Add grandparents
7182c066a59SGreg Roach                    foreach ($this->parentFacts($spouse, $spouse->sex() === 'F' ? 3 : 2, $min_date, $max_date) as $fact) {
7192c066a59SGreg Roach                        $facts[] = $fact;
7202c066a59SGreg Roach                    }
7212c066a59SGreg Roach                }
7222c066a59SGreg Roach            }
7232c066a59SGreg Roach
7242c066a59SGreg Roach            if (str_contains($SHOW_RELATIVES_EVENTS, '_MARR_PARE')) {
7252c066a59SGreg Roach                // add father/mother marriages
7262c066a59SGreg Roach                foreach ($person->childFamilies() as $sfamily) {
7272c066a59SGreg Roach                    foreach ($sfamily->facts(['MARR']) as $fact) {
7282c066a59SGreg Roach                        if ($this->includeFact($fact, $min_date, $max_date)) {
7292c066a59SGreg Roach                            // marriage of parents (to each other)
7302c066a59SGreg Roach                            $facts[] = $this->convertEvent($fact, ['U' => I18N::translate('Marriage of parents')], 'U');
7312c066a59SGreg Roach                        }
7322c066a59SGreg Roach                    }
7332c066a59SGreg Roach                }
7342c066a59SGreg Roach                foreach ($person->childStepFamilies() as $sfamily) {
7352c066a59SGreg Roach                    foreach ($sfamily->facts(['MARR']) as $fact) {
7362c066a59SGreg Roach                        if ($this->includeFact($fact, $min_date, $max_date)) {
7372c066a59SGreg Roach                            // marriage of a parent (to another spouse)
7382c066a59SGreg Roach                            $facts[] = $this->convertEvent($fact, $marriage_of_a_parent, 'U');
7392c066a59SGreg Roach                        }
7402c066a59SGreg Roach                    }
7412c066a59SGreg Roach                }
7422c066a59SGreg Roach            }
7432c066a59SGreg Roach        }
7442c066a59SGreg Roach
7452c066a59SGreg Roach        foreach ($person->childFamilies() as $family) {
7462c066a59SGreg Roach            foreach ($family->spouses() as $parent) {
7472c066a59SGreg Roach                if (str_contains($SHOW_RELATIVES_EVENTS, '_DEAT' . ($sosa === 1 ? '_PARE' : '_GPAR'))) {
7482c066a59SGreg Roach                    foreach ($parent->facts(['DEAT', 'BURI', 'CREM']) as $fact) {
7492c066a59SGreg Roach                        // Show death of parent when it happened prior to birth
7502c066a59SGreg Roach                        if ($sosa === 1 && Date::compare($fact->date(), $min_date) < 0 || $this->includeFact($fact, $min_date, $max_date)) {
7512c066a59SGreg Roach                            switch ($sosa) {
7522c066a59SGreg Roach                                case 1:
7532c066a59SGreg Roach                                    $facts[] = $this->convertEvent($fact, $death_of_a_parent[$fact->tag()], $fact->record()->sex());
7542c066a59SGreg Roach                                    break;
7552c066a59SGreg Roach                                case 2:
7562c066a59SGreg Roach                                case 3:
7572c066a59SGreg Roach                                    switch ($person->sex()) {
7582c066a59SGreg Roach                                        case 'M':
7592c066a59SGreg Roach                                            $facts[] = $this->convertEvent($fact, $death_of_a_paternal_grandparent[$fact->tag()], $fact->record()->sex());
7602c066a59SGreg Roach                                            break;
7612c066a59SGreg Roach                                        case 'F':
7622c066a59SGreg Roach                                            $facts[] = $this->convertEvent($fact, $death_of_a_maternal_grandparent[$fact->tag()], $fact->record()->sex());
7632c066a59SGreg Roach                                            break;
7642c066a59SGreg Roach                                        default:
7652c066a59SGreg Roach                                            $facts[] = $this->convertEvent($fact, $death_of_a_grandparent[$fact->tag()], $fact->record()->sex());
7662c066a59SGreg Roach                                            break;
7672c066a59SGreg Roach                                    }
7682c066a59SGreg Roach                            }
7692c066a59SGreg Roach                        }
7702c066a59SGreg Roach                    }
7712c066a59SGreg Roach                }
7722c066a59SGreg Roach            }
7732c066a59SGreg Roach        }
7742c066a59SGreg Roach
7752c066a59SGreg Roach        return $facts;
7762c066a59SGreg Roach    }
7772c066a59SGreg Roach
7782c066a59SGreg Roach    /**
7792c066a59SGreg Roach     * Spouse facts that are shown on an individual’s page.
7802c066a59SGreg Roach     *
7812c066a59SGreg Roach     * @param Individual $individual Show events that occurred during the lifetime of this individual
7822c066a59SGreg Roach     * @param Individual $spouse     Show events of this individual
7832c066a59SGreg Roach     * @param Date       $min_date
7842c066a59SGreg Roach     * @param Date       $max_date
7852c066a59SGreg Roach     *
7862c066a59SGreg Roach     * @return Collection<int,Fact>
7872c066a59SGreg Roach     */
7882c066a59SGreg Roach    private function spouseFacts(Individual $individual, Individual $spouse, Date $min_date, Date $max_date): Collection
7892c066a59SGreg Roach    {
7902c066a59SGreg Roach        $SHOW_RELATIVES_EVENTS = $individual->tree()->getPreference('SHOW_RELATIVES_EVENTS');
7912c066a59SGreg Roach
7922c066a59SGreg Roach        $death_of_a_spouse = [
7932c066a59SGreg Roach            'INDI:DEAT' => [
7942c066a59SGreg Roach                'M' => I18N::translate('Death of a husband'),
7952c066a59SGreg Roach                'F' => I18N::translate('Death of a wife'),
7962c066a59SGreg Roach                'U' => I18N::translate('Death of a spouse'),
7972c066a59SGreg Roach            ],
7982c066a59SGreg Roach            'INDI:BURI' => [
7992c066a59SGreg Roach                'M' => I18N::translate('Burial of a husband'),
8002c066a59SGreg Roach                'F' => I18N::translate('Burial of a wife'),
8012c066a59SGreg Roach                'U' => I18N::translate('Burial of a spouse'),
8022c066a59SGreg Roach            ],
8032c066a59SGreg Roach            'INDI:CREM' => [
8042c066a59SGreg Roach                'M' => I18N::translate('Cremation of a husband'),
8052c066a59SGreg Roach                'F' => I18N::translate('Cremation of a wife'),
8062c066a59SGreg Roach                'U' => I18N::translate('Cremation of a spouse'),
8072c066a59SGreg Roach            ],
8082c066a59SGreg Roach        ];
8092c066a59SGreg Roach
8102c066a59SGreg Roach        $facts = new Collection();
8112c066a59SGreg Roach
8122c066a59SGreg Roach        if (str_contains($SHOW_RELATIVES_EVENTS, '_DEAT_SPOU')) {
8132c066a59SGreg Roach            foreach ($spouse->facts(['DEAT', 'BURI', 'CREM']) as $fact) {
8142c066a59SGreg Roach                if ($this->includeFact($fact, $min_date, $max_date)) {
8152c066a59SGreg Roach                    $facts[] = $this->convertEvent($fact, $death_of_a_spouse[$fact->tag()], $fact->record()->sex());
8162c066a59SGreg Roach                }
8172c066a59SGreg Roach            }
8182c066a59SGreg Roach        }
8192c066a59SGreg Roach
8202c066a59SGreg Roach        return $facts;
8212c066a59SGreg Roach    }
8222c066a59SGreg Roach
8232c066a59SGreg Roach    /**
8242c066a59SGreg Roach     * Does a relative event occur within a date range (i.e. the individual's lifetime)?
8252c066a59SGreg Roach     *
8262c066a59SGreg Roach     * @param Fact $fact
8272c066a59SGreg Roach     * @param Date $min_date
8282c066a59SGreg Roach     * @param Date $max_date
8292c066a59SGreg Roach     *
8302c066a59SGreg Roach     * @return bool
8312c066a59SGreg Roach     */
8322c066a59SGreg Roach    private function includeFact(Fact $fact, Date $min_date, Date $max_date): bool
8332c066a59SGreg Roach    {
8342c066a59SGreg Roach        $fact_date = $fact->date();
8352c066a59SGreg Roach
8362c066a59SGreg Roach        return $fact_date->isOK() && Date::compare($min_date, $fact_date) <= 0 && Date::compare($fact_date, $max_date) <= 0;
8372c066a59SGreg Roach    }
8382c066a59SGreg Roach
8392c066a59SGreg Roach    /**
8402c066a59SGreg Roach     * Convert an event into a special "event of a close relative".
8412c066a59SGreg Roach     *
8422c066a59SGreg Roach     * @param Fact          $fact
8432c066a59SGreg Roach     * @param array<string> $types
8442c066a59SGreg Roach     * @param string        $sex
8452c066a59SGreg Roach     *
8462c066a59SGreg Roach     * @return Fact
8472c066a59SGreg Roach     */
8482c066a59SGreg Roach    private function convertEvent(Fact $fact, array $types, string $sex): Fact
8492c066a59SGreg Roach    {
8502c066a59SGreg Roach        $type = $types[$sex] ?? $types['U'];
8512c066a59SGreg Roach
8522c066a59SGreg Roach        $gedcom = $fact->gedcom();
8532c066a59SGreg Roach        $gedcom = preg_replace('/\n2 TYPE .*/', '', $gedcom);
8542c066a59SGreg Roach        $gedcom = preg_replace('/^1 .*/', "1 EVEN CLOSE_RELATIVE\n2 TYPE " . $type, $gedcom);
8552c066a59SGreg Roach
8562c066a59SGreg Roach        $converted = new Fact($gedcom, $fact->record(), $fact->id());
8572c066a59SGreg Roach
8582c066a59SGreg Roach        if ($fact->isPendingAddition()) {
8592c066a59SGreg Roach            $converted->setPendingAddition();
8602c066a59SGreg Roach        }
8612c066a59SGreg Roach
8622c066a59SGreg Roach        if ($fact->isPendingDeletion()) {
8632c066a59SGreg Roach            $converted->setPendingDeletion();
8642c066a59SGreg Roach        }
8652c066a59SGreg Roach
8662c066a59SGreg Roach        return $converted;
8672c066a59SGreg Roach    }
8682c066a59SGreg Roach}
869