xref: /webtrees/app/Module/ModuleDataFixTrait.php (revision 6f4ec3cadc983f0a7294108c634bef48846b4311)
1ce42304aSGreg Roach<?php
2ce42304aSGreg Roach
3ce42304aSGreg Roach/**
4ce42304aSGreg Roach * webtrees: online genealogy
5d11be702SGreg Roach * Copyright (C) 2023 webtrees development team
6ce42304aSGreg Roach * This program is free software: you can redistribute it and/or modify
7ce42304aSGreg Roach * it under the terms of the GNU General Public License as published by
8ce42304aSGreg Roach * the Free Software Foundation, either version 3 of the License, or
9ce42304aSGreg Roach * (at your option) any later version.
10ce42304aSGreg Roach * This program is distributed in the hope that it will be useful,
11ce42304aSGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
12ce42304aSGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13ce42304aSGreg Roach * GNU General Public License for more details.
14ce42304aSGreg Roach * You should have received a copy of the GNU General Public License
1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>.
16ce42304aSGreg Roach */
17ce42304aSGreg Roach
18ce42304aSGreg Roachdeclare(strict_types=1);
19ce42304aSGreg Roach
20ce42304aSGreg Roachnamespace Fisharebest\Webtrees\Module;
21ce42304aSGreg Roach
22*6f4ec3caSGreg Roachuse Fisharebest\Webtrees\DB;
23ce42304aSGreg Roachuse Fisharebest\Webtrees\Family;
24ce42304aSGreg Roachuse Fisharebest\Webtrees\GedcomRecord;
25ce42304aSGreg Roachuse Fisharebest\Webtrees\Individual;
267684867eSGreg Roachuse Fisharebest\Webtrees\Location;
27ce42304aSGreg Roachuse Fisharebest\Webtrees\Media;
28ce42304aSGreg Roachuse Fisharebest\Webtrees\Note;
29ce42304aSGreg Roachuse Fisharebest\Webtrees\Repository;
30ce42304aSGreg Roachuse Fisharebest\Webtrees\Source;
31ce42304aSGreg Roachuse Fisharebest\Webtrees\Submitter;
32ce42304aSGreg Roachuse Fisharebest\Webtrees\Tree;
33ce42304aSGreg Roachuse Illuminate\Database\Query\Builder;
34ce42304aSGreg Roachuse Illuminate\Support\Collection;
35ce42304aSGreg Roach
36ce42304aSGreg Roach/**
37ce42304aSGreg Roach * Trait ModuleDataFixTrait - default implementation of ModuleDataFixTrait
38ce42304aSGreg Roach */
39ce42304aSGreg Roachtrait ModuleDataFixTrait
40ce42304aSGreg Roach{
41ce42304aSGreg Roach    /**
42ce42304aSGreg Roach     * Options form.
43ce42304aSGreg Roach     *
44ce42304aSGreg Roach     * @param Tree $tree
45ce42304aSGreg Roach     *
46ce42304aSGreg Roach     * @return string
47ce42304aSGreg Roach     */
4849528f2bSGreg Roach    public function fixOptions(Tree $tree): string
49ce42304aSGreg Roach    {
50ce42304aSGreg Roach        return '';
51ce42304aSGreg Roach    }
52ce42304aSGreg Roach
53ce42304aSGreg Roach    /**
54ce42304aSGreg Roach     * A list of all records that need examining.  This may include records
55ce42304aSGreg Roach     * that do not need updating, if we can't detect this quickly using SQL.
56ce42304aSGreg Roach     *
57ce42304aSGreg Roach     * @param Tree                 $tree
58ce42304aSGreg Roach     * @param array<string,string> $params
59ce42304aSGreg Roach     *
6036779af1SGreg Roach     * @return Collection<int,object>
61ce42304aSGreg Roach     */
62ce42304aSGreg Roach    public function recordsToFix(Tree $tree, array $params): Collection
63ce42304aSGreg Roach    {
64ce42304aSGreg Roach        $families     = $this->familiesToFix($tree, $params);
65ce42304aSGreg Roach        $individuals  = $this->individualsToFix($tree, $params);
667684867eSGreg Roach        $locations    = $this->locationsToFix($tree, $params);
67ce42304aSGreg Roach        $media        = $this->mediaToFix($tree, $params);
68ce42304aSGreg Roach        $notes        = $this->notesToFix($tree, $params);
69ce42304aSGreg Roach        $repositories = $this->repositoriesToFix($tree, $params);
70ce42304aSGreg Roach        $sources      = $this->sourcesToFix($tree, $params);
71ce42304aSGreg Roach        $submitters   = $this->submittersToFix($tree, $params);
72ce42304aSGreg Roach
73ce42304aSGreg Roach        $records = new Collection();
74ce42304aSGreg Roach
75ce42304aSGreg Roach        if ($families !== null) {
76ce42304aSGreg Roach            $records = $records->concat($this->mergePendingRecords($families, $tree, Family::RECORD_TYPE));
77ce42304aSGreg Roach        }
78ce42304aSGreg Roach
79ce42304aSGreg Roach        if ($individuals !== null) {
80ce42304aSGreg Roach            $records = $records->concat($this->mergePendingRecords($individuals, $tree, Individual::RECORD_TYPE));
81ce42304aSGreg Roach        }
82ce42304aSGreg Roach
837684867eSGreg Roach        if ($locations !== null) {
847684867eSGreg Roach            $records = $records->concat($this->mergePendingRecords($locations, $tree, Location::RECORD_TYPE));
857684867eSGreg Roach        }
867684867eSGreg Roach
87ce42304aSGreg Roach        if ($media !== null) {
88ce42304aSGreg Roach            $records = $records->concat($this->mergePendingRecords($media, $tree, Media::RECORD_TYPE));
89ce42304aSGreg Roach        }
90ce42304aSGreg Roach
91ce42304aSGreg Roach        if ($notes !== null) {
92ce42304aSGreg Roach            $records = $records->concat($this->mergePendingRecords($notes, $tree, Note::RECORD_TYPE));
93ce42304aSGreg Roach        }
94ce42304aSGreg Roach
95ce42304aSGreg Roach        if ($repositories !== null) {
96ce42304aSGreg Roach            $records = $records->concat($this->mergePendingRecords($repositories, $tree, Repository::RECORD_TYPE));
97ce42304aSGreg Roach        }
98ce42304aSGreg Roach
99ce42304aSGreg Roach        if ($sources !== null) {
100ce42304aSGreg Roach            $records = $records->concat($this->mergePendingRecords($sources, $tree, Source::RECORD_TYPE));
101ce42304aSGreg Roach        }
102ce42304aSGreg Roach
103ce42304aSGreg Roach        if ($submitters !== null) {
104ce42304aSGreg Roach            $records = $records->concat($this->mergePendingRecords($submitters, $tree, Submitter::RECORD_TYPE));
105ce42304aSGreg Roach        }
106ce42304aSGreg Roach
107ce42304aSGreg Roach        return $records
108ce42304aSGreg Roach            ->unique()
109f70bcff5SGreg Roach            ->sort(static function (object $x, object $y) {
110ce42304aSGreg Roach                return $x->xref <=> $y->xref;
111ce42304aSGreg Roach            });
112ce42304aSGreg Roach    }
113ce42304aSGreg Roach
114ce42304aSGreg Roach    /**
115ce42304aSGreg Roach     * Does a record need updating?
116ce42304aSGreg Roach     *
117ce42304aSGreg Roach     * @param GedcomRecord         $record
118ce42304aSGreg Roach     * @param array<string,string> $params
119ce42304aSGreg Roach     *
120ce42304aSGreg Roach     * @return bool
121ce42304aSGreg Roach     */
12249528f2bSGreg Roach    public function doesRecordNeedUpdate(GedcomRecord $record, array $params): bool
123ce42304aSGreg Roach    {
124ce42304aSGreg Roach        return false;
125ce42304aSGreg Roach    }
126ce42304aSGreg Roach
127ce42304aSGreg Roach    /**
128ce42304aSGreg Roach     * Show the changes we would make
129ce42304aSGreg Roach     *
130ce42304aSGreg Roach     * @param GedcomRecord         $record
131ce42304aSGreg Roach     * @param array<string,string> $params
132ce42304aSGreg Roach     *
133ce42304aSGreg Roach     * @return string
134ce42304aSGreg Roach     */
13549528f2bSGreg Roach    public function previewUpdate(GedcomRecord $record, array $params): string
136ce42304aSGreg Roach    {
137ce42304aSGreg Roach        return $record->fullName();
138ce42304aSGreg Roach    }
139ce42304aSGreg Roach
140ce42304aSGreg Roach    /**
141ce42304aSGreg Roach     * Fix a record
142ce42304aSGreg Roach     *
143ce42304aSGreg Roach     * @param GedcomRecord         $record
144ce42304aSGreg Roach     * @param array<string,string> $params
145ce42304aSGreg Roach     *
146ce42304aSGreg Roach     * @return void
147ce42304aSGreg Roach     */
14849528f2bSGreg Roach    public function updateRecord(GedcomRecord $record, array $params): void
149ce42304aSGreg Roach    {
150ce42304aSGreg Roach    }
151ce42304aSGreg Roach
152ce42304aSGreg Roach    /**
153ce42304aSGreg Roach     * XREFs of family records that might need fixing.
154ce42304aSGreg Roach     *
155ce42304aSGreg Roach     * @param Tree                 $tree
156ce42304aSGreg Roach     * @param array<string,string> $params
157ce42304aSGreg Roach     *
15836779af1SGreg Roach     * @return Collection<int,string>|null
159ce42304aSGreg Roach     */
16049528f2bSGreg Roach    protected function familiesToFix(Tree $tree, array $params): ?Collection
161ce42304aSGreg Roach    {
162ce42304aSGreg Roach        return null;
163ce42304aSGreg Roach    }
164ce42304aSGreg Roach
165ce42304aSGreg Roach    /**
1667684867eSGreg Roach     * @param Tree                 $tree
1677684867eSGreg Roach     * @param array<string,string> $params
1687684867eSGreg Roach     *
1697684867eSGreg Roach     * @return Builder
1707684867eSGreg Roach     */
1717684867eSGreg Roach    protected function familiesToFixQuery(Tree $tree, array $params): Builder
1727684867eSGreg Roach    {
1737684867eSGreg Roach        $query = DB::table('families')
1745062b1caSJonathan Jaubart            ->where('f_file', '=', $tree->id());
1757684867eSGreg Roach
1767684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
1775062b1caSJonathan Jaubart            $query->whereBetween('f_id', [$params['start'], $params['end']]);
1787684867eSGreg Roach        }
1797684867eSGreg Roach
1807684867eSGreg Roach        return $query;
1817684867eSGreg Roach    }
1827684867eSGreg Roach
1837684867eSGreg Roach    /**
184ce42304aSGreg Roach     * XREFs of individual records that might need fixing.
185ce42304aSGreg Roach     *
186ce42304aSGreg Roach     * @param Tree                 $tree
187ce42304aSGreg Roach     * @param array<string,string> $params
188ce42304aSGreg Roach     *
18936779af1SGreg Roach     * @return Collection<int,string>|null
190ce42304aSGreg Roach     */
19149528f2bSGreg Roach    protected function individualsToFix(Tree $tree, array $params): ?Collection
192ce42304aSGreg Roach    {
193ce42304aSGreg Roach        return null;
194ce42304aSGreg Roach    }
195ce42304aSGreg Roach
196ce42304aSGreg Roach    /**
1977684867eSGreg Roach     * @param Tree                 $tree
1987684867eSGreg Roach     * @param array<string,string> $params
1997684867eSGreg Roach     *
2007684867eSGreg Roach     * @return Builder
2017684867eSGreg Roach     */
2027684867eSGreg Roach    protected function individualsToFixQuery(Tree $tree, array $params): Builder
2037684867eSGreg Roach    {
2047684867eSGreg Roach        $query = DB::table('individuals')
2057684867eSGreg Roach            ->where('i_file', '=', $tree->id());
2067684867eSGreg Roach
2077684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
2087684867eSGreg Roach            $query->whereBetween('i_id', [$params['start'], $params['end']]);
2097684867eSGreg Roach        }
2107684867eSGreg Roach
2117684867eSGreg Roach        return $query;
2127684867eSGreg Roach    }
2137684867eSGreg Roach
2147684867eSGreg Roach    /**
2157684867eSGreg Roach     * XREFs of location records that might need fixing.
2167684867eSGreg Roach     *
2177684867eSGreg Roach     * @param Tree                 $tree
2187684867eSGreg Roach     * @param array<string,string> $params
2197684867eSGreg Roach     *
22036779af1SGreg Roach     * @return Collection<int,string>|null
2217684867eSGreg Roach     */
22249528f2bSGreg Roach    protected function locationsToFix(Tree $tree, array $params): ?Collection
2237684867eSGreg Roach    {
2247684867eSGreg Roach        return null;
2257684867eSGreg Roach    }
2267684867eSGreg Roach
2277684867eSGreg Roach    /**
2287684867eSGreg Roach     * @param Tree                 $tree
2297684867eSGreg Roach     * @param array<string,string> $params
2307684867eSGreg Roach     *
2317684867eSGreg Roach     * @return Builder
2327684867eSGreg Roach     */
2337684867eSGreg Roach    protected function locationsToFixQuery(Tree $tree, array $params): Builder
2347684867eSGreg Roach    {
2357684867eSGreg Roach        $query = DB::table('other')
2367684867eSGreg Roach            ->where('o_type', '=', Location::RECORD_TYPE)
2377684867eSGreg Roach            ->where('o_file', '=', $tree->id());
2387684867eSGreg Roach
2397684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
2407684867eSGreg Roach            $query->whereBetween('o_id', [$params['start'], $params['end']]);
2417684867eSGreg Roach        }
2427684867eSGreg Roach
2437684867eSGreg Roach        return $query;
2447684867eSGreg Roach    }
2457684867eSGreg Roach
2467684867eSGreg Roach    /**
247ce42304aSGreg Roach     * XREFs of media records that might need fixing.
248ce42304aSGreg Roach     *
249ce42304aSGreg Roach     * @param Tree                 $tree
250ce42304aSGreg Roach     * @param array<string,string> $params
251ce42304aSGreg Roach     *
25236779af1SGreg Roach     * @return Collection<int,string>|null
253ce42304aSGreg Roach     */
25449528f2bSGreg Roach    protected function mediaToFix(Tree $tree, array $params): ?Collection
255ce42304aSGreg Roach    {
256ce42304aSGreg Roach        return null;
257ce42304aSGreg Roach    }
258ce42304aSGreg Roach
259ce42304aSGreg Roach    /**
2607684867eSGreg Roach     * @param Tree                 $tree
2617684867eSGreg Roach     * @param array<string,string> $params
2627684867eSGreg Roach     *
2637684867eSGreg Roach     * @return Builder
2647684867eSGreg Roach     */
2657684867eSGreg Roach    protected function mediaToFixQuery(Tree $tree, array $params): Builder
2667684867eSGreg Roach    {
2677684867eSGreg Roach        $query = DB::table('media')
2687684867eSGreg Roach            ->where('m_file', '=', $tree->id());
2697684867eSGreg Roach
2707684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
2717684867eSGreg Roach            $query->whereBetween('m_id', [$params['start'], $params['end']]);
2727684867eSGreg Roach        }
2737684867eSGreg Roach
2747684867eSGreg Roach        return $query;
2757684867eSGreg Roach    }
2767684867eSGreg Roach
2777684867eSGreg Roach    /**
278ce42304aSGreg Roach     * XREFs of note records that might need fixing.
279ce42304aSGreg Roach     *
280ce42304aSGreg Roach     * @param Tree                 $tree
281ce42304aSGreg Roach     * @param array<string,string> $params
282ce42304aSGreg Roach     *
28336779af1SGreg Roach     * @return Collection<int,string>|null
284ce42304aSGreg Roach     */
28549528f2bSGreg Roach    protected function notesToFix(Tree $tree, array $params): ?Collection
286ce42304aSGreg Roach    {
287ce42304aSGreg Roach        return null;
288ce42304aSGreg Roach    }
289ce42304aSGreg Roach
290ce42304aSGreg Roach    /**
2917684867eSGreg Roach     * @param Tree                 $tree
2927684867eSGreg Roach     * @param array<string,string> $params
2937684867eSGreg Roach     *
2947684867eSGreg Roach     * @return Builder
2957684867eSGreg Roach     */
2967684867eSGreg Roach    protected function notesToFixQuery(Tree $tree, array $params): Builder
2977684867eSGreg Roach    {
2987684867eSGreg Roach        $query = DB::table('other')
2997684867eSGreg Roach            ->where('o_type', '=', Note::RECORD_TYPE)
3007684867eSGreg Roach            ->where('o_file', '=', $tree->id());
3017684867eSGreg Roach
3027684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
3037684867eSGreg Roach            $query->whereBetween('o_id', [$params['start'], $params['end']]);
3047684867eSGreg Roach        }
3057684867eSGreg Roach
3067684867eSGreg Roach        return $query;
3077684867eSGreg Roach    }
3087684867eSGreg Roach
3097684867eSGreg Roach    /**
310ce42304aSGreg Roach     * XREFs of repository records that might need fixing.
311ce42304aSGreg Roach     *
312ce42304aSGreg Roach     * @param Tree                 $tree
313ce42304aSGreg Roach     * @param array<string,string> $params
314ce42304aSGreg Roach     *
31536779af1SGreg Roach     * @return Collection<int,string>|null
316ce42304aSGreg Roach     */
31749528f2bSGreg Roach    protected function repositoriesToFix(Tree $tree, array $params): ?Collection
318ce42304aSGreg Roach    {
319ce42304aSGreg Roach        return null;
320ce42304aSGreg Roach    }
321ce42304aSGreg Roach
322ce42304aSGreg Roach    /**
3237684867eSGreg Roach     * @param Tree                 $tree
3247684867eSGreg Roach     * @param array<string,string> $params
3257684867eSGreg Roach     *
3267684867eSGreg Roach     * @return Builder
3277684867eSGreg Roach     */
3287684867eSGreg Roach    protected function repositoriesToFixQuery(Tree $tree, array $params): Builder
3297684867eSGreg Roach    {
3307684867eSGreg Roach        $query = DB::table('other')
3317684867eSGreg Roach            ->where('o_type', '=', Repository::RECORD_TYPE)
3327684867eSGreg Roach            ->where('o_file', '=', $tree->id());
3337684867eSGreg Roach
3347684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
3357684867eSGreg Roach            $query->whereBetween('o_id', [$params['start'], $params['end']]);
3367684867eSGreg Roach        }
3377684867eSGreg Roach
3387684867eSGreg Roach        return $query;
3397684867eSGreg Roach    }
3407684867eSGreg Roach
3417684867eSGreg Roach    /**
342ce42304aSGreg Roach     * XREFs of source records that might need fixing.
343ce42304aSGreg Roach     *
344ce42304aSGreg Roach     * @param Tree                 $tree
345ce42304aSGreg Roach     * @param array<string,string> $params
346ce42304aSGreg Roach     *
34736779af1SGreg Roach     * @return Collection<int,string>|null
348ce42304aSGreg Roach     */
34949528f2bSGreg Roach    protected function sourcesToFix(Tree $tree, array $params): ?Collection
350ce42304aSGreg Roach    {
351ce42304aSGreg Roach        return null;
352ce42304aSGreg Roach    }
353ce42304aSGreg Roach
354ce42304aSGreg Roach    /**
3557684867eSGreg Roach     * @param Tree                 $tree
3567684867eSGreg Roach     * @param array<string,string> $params
3577684867eSGreg Roach     *
3587684867eSGreg Roach     * @return Builder
3597684867eSGreg Roach     */
3607684867eSGreg Roach    protected function sourcesToFixQuery(Tree $tree, array $params): Builder
3617684867eSGreg Roach    {
3627684867eSGreg Roach        $query = DB::table('sources')
3637684867eSGreg Roach            ->where('s_file', '=', $tree->id());
3647684867eSGreg Roach
3657684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
3667684867eSGreg Roach            $query->whereBetween('s_id', [$params['start'], $params['end']]);
3677684867eSGreg Roach        }
3687684867eSGreg Roach
3697684867eSGreg Roach        return $query;
3707684867eSGreg Roach    }
3717684867eSGreg Roach
3727684867eSGreg Roach    /**
373ce42304aSGreg Roach     * XREFs of submitter records that might need fixing.
374ce42304aSGreg Roach     *
375ce42304aSGreg Roach     * @param Tree                 $tree
376ce42304aSGreg Roach     * @param array<string,string> $params
377ce42304aSGreg Roach     *
37836779af1SGreg Roach     * @return Collection<int,string>|null
379ce42304aSGreg Roach     */
38049528f2bSGreg Roach    protected function submittersToFix(Tree $tree, array $params): ?Collection
381ce42304aSGreg Roach    {
382ce42304aSGreg Roach        return null;
383ce42304aSGreg Roach    }
384ce42304aSGreg Roach
385ce42304aSGreg Roach    /**
3867684867eSGreg Roach     * @param Tree                 $tree
3877684867eSGreg Roach     * @param array<string,string> $params
3887684867eSGreg Roach     *
3897684867eSGreg Roach     * @return Builder
3907684867eSGreg Roach     */
3917684867eSGreg Roach    protected function submittersToFixQuery(Tree $tree, array $params): Builder
3927684867eSGreg Roach    {
3937684867eSGreg Roach        $query = DB::table('other')
3947684867eSGreg Roach            ->where('o_type', '=', Submitter::RECORD_TYPE)
3957684867eSGreg Roach            ->where('o_file', '=', $tree->id());
3967684867eSGreg Roach
3977684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
3987684867eSGreg Roach            $query->whereBetween('o_id', [$params['start'], $params['end']]);
3997684867eSGreg Roach        }
4007684867eSGreg Roach
4017684867eSGreg Roach        return $query;
4027684867eSGreg Roach    }
4037684867eSGreg Roach
4047684867eSGreg Roach    /**
405ce42304aSGreg Roach     * Merge pending changes of a given type.  We need to check all pending records.
406ce42304aSGreg Roach     *
40736779af1SGreg Roach     * @param Collection<int,string> $records
408ce42304aSGreg Roach     * @param Tree                   $tree
409ce42304aSGreg Roach     * @param string                 $type
410ce42304aSGreg Roach     *
411e93aa0bdSGreg Roach     * @return Collection<int,object>
412ce42304aSGreg Roach     */
413ce42304aSGreg Roach    private function mergePendingRecords(Collection $records, Tree $tree, string $type): Collection
414ce42304aSGreg Roach    {
415ce42304aSGreg Roach        $pending = DB::table('change')
416ce42304aSGreg Roach            ->where('gedcom_id', '=', $tree->id())
417ce42304aSGreg Roach            ->where('status', '=', 'pending')
418ce42304aSGreg Roach            ->where(static function (Builder $query) use ($type): void {
419ce42304aSGreg Roach                $query
420e93aa0bdSGreg Roach                    ->where('old_gedcom', 'LIKE', '%@ ' . $type . '\n%')
421e93aa0bdSGreg Roach                    ->orWhere('new_gedcom', 'LIKE', '%@ ' . $type . '\n%');
422ce42304aSGreg Roach            })
423ce42304aSGreg Roach            ->pluck('xref');
424ce42304aSGreg Roach
425ce42304aSGreg Roach        return $records
426ce42304aSGreg Roach            ->concat($pending)
427f70bcff5SGreg Roach            ->map(static function (string $xref) use ($type): object {
428ce42304aSGreg Roach                return (object) ['xref' => $xref, 'type' => $type];
429ce42304aSGreg Roach            });
430ce42304aSGreg Roach    }
431ce42304aSGreg Roach}
432