xref: /webtrees/app/Module/ModuleDataFixTrait.php (revision 449b311ecf65f677a2595e1e29f712d11ef22f34)
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
226f4ec3caSGreg 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()
109f25fc0f9SGreg Roach            ->sort(static fn (object $x, object $y) => $x->xref <=> $y->xref);
110ce42304aSGreg Roach    }
111ce42304aSGreg Roach
112ce42304aSGreg Roach    /**
113ce42304aSGreg Roach     * Does a record need updating?
114ce42304aSGreg Roach     *
115ce42304aSGreg Roach     * @param GedcomRecord         $record
116ce42304aSGreg Roach     * @param array<string,string> $params
117ce42304aSGreg Roach     *
118ce42304aSGreg Roach     * @return bool
119ce42304aSGreg Roach     */
12049528f2bSGreg Roach    public function doesRecordNeedUpdate(GedcomRecord $record, array $params): bool
121ce42304aSGreg Roach    {
122ce42304aSGreg Roach        return false;
123ce42304aSGreg Roach    }
124ce42304aSGreg Roach
125ce42304aSGreg Roach    /**
126ce42304aSGreg Roach     * Show the changes we would make
127ce42304aSGreg Roach     *
128ce42304aSGreg Roach     * @param GedcomRecord         $record
129ce42304aSGreg Roach     * @param array<string,string> $params
130ce42304aSGreg Roach     *
131ce42304aSGreg Roach     * @return string
132ce42304aSGreg Roach     */
13349528f2bSGreg Roach    public function previewUpdate(GedcomRecord $record, array $params): string
134ce42304aSGreg Roach    {
135ce42304aSGreg Roach        return $record->fullName();
136ce42304aSGreg Roach    }
137ce42304aSGreg Roach
138ce42304aSGreg Roach    /**
139ce42304aSGreg Roach     * Fix a record
140ce42304aSGreg Roach     *
141ce42304aSGreg Roach     * @param GedcomRecord         $record
142ce42304aSGreg Roach     * @param array<string,string> $params
143ce42304aSGreg Roach     *
144ce42304aSGreg Roach     * @return void
145ce42304aSGreg Roach     */
14649528f2bSGreg Roach    public function updateRecord(GedcomRecord $record, array $params): void
147ce42304aSGreg Roach    {
148ce42304aSGreg Roach    }
149ce42304aSGreg Roach
150ce42304aSGreg Roach    /**
151ce42304aSGreg Roach     * XREFs of family records that might need fixing.
152ce42304aSGreg Roach     *
153ce42304aSGreg Roach     * @param Tree                 $tree
154ce42304aSGreg Roach     * @param array<string,string> $params
155ce42304aSGreg Roach     *
15636779af1SGreg Roach     * @return Collection<int,string>|null
157ce42304aSGreg Roach     */
158*1ff45046SGreg Roach    protected function familiesToFix(Tree $tree, array $params): Collection|null
159ce42304aSGreg Roach    {
160ce42304aSGreg Roach        return null;
161ce42304aSGreg Roach    }
162ce42304aSGreg Roach
163ce42304aSGreg Roach    /**
1647684867eSGreg Roach     * @param Tree                 $tree
1657684867eSGreg Roach     * @param array<string,string> $params
1667684867eSGreg Roach     *
1677684867eSGreg Roach     * @return Builder
1687684867eSGreg Roach     */
1697684867eSGreg Roach    protected function familiesToFixQuery(Tree $tree, array $params): Builder
1707684867eSGreg Roach    {
1717684867eSGreg Roach        $query = DB::table('families')
1725062b1caSJonathan Jaubart            ->where('f_file', '=', $tree->id());
1737684867eSGreg Roach
1747684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
1755062b1caSJonathan Jaubart            $query->whereBetween('f_id', [$params['start'], $params['end']]);
1767684867eSGreg Roach        }
1777684867eSGreg Roach
1787684867eSGreg Roach        return $query;
1797684867eSGreg Roach    }
1807684867eSGreg Roach
1817684867eSGreg Roach    /**
182ce42304aSGreg Roach     * XREFs of individual records that might need fixing.
183ce42304aSGreg Roach     *
184ce42304aSGreg Roach     * @param Tree                 $tree
185ce42304aSGreg Roach     * @param array<string,string> $params
186ce42304aSGreg Roach     *
18736779af1SGreg Roach     * @return Collection<int,string>|null
188ce42304aSGreg Roach     */
189*1ff45046SGreg Roach    protected function individualsToFix(Tree $tree, array $params): Collection|null
190ce42304aSGreg Roach    {
191ce42304aSGreg Roach        return null;
192ce42304aSGreg Roach    }
193ce42304aSGreg Roach
194ce42304aSGreg Roach    /**
1957684867eSGreg Roach     * @param Tree                 $tree
1967684867eSGreg Roach     * @param array<string,string> $params
1977684867eSGreg Roach     *
1987684867eSGreg Roach     * @return Builder
1997684867eSGreg Roach     */
2007684867eSGreg Roach    protected function individualsToFixQuery(Tree $tree, array $params): Builder
2017684867eSGreg Roach    {
2027684867eSGreg Roach        $query = DB::table('individuals')
2037684867eSGreg Roach            ->where('i_file', '=', $tree->id());
2047684867eSGreg Roach
2057684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
2067684867eSGreg Roach            $query->whereBetween('i_id', [$params['start'], $params['end']]);
2077684867eSGreg Roach        }
2087684867eSGreg Roach
2097684867eSGreg Roach        return $query;
2107684867eSGreg Roach    }
2117684867eSGreg Roach
2127684867eSGreg Roach    /**
2137684867eSGreg Roach     * XREFs of location records that might need fixing.
2147684867eSGreg Roach     *
2157684867eSGreg Roach     * @param Tree                 $tree
2167684867eSGreg Roach     * @param array<string,string> $params
2177684867eSGreg Roach     *
21836779af1SGreg Roach     * @return Collection<int,string>|null
2197684867eSGreg Roach     */
220*1ff45046SGreg Roach    protected function locationsToFix(Tree $tree, array $params): Collection|null
2217684867eSGreg Roach    {
2227684867eSGreg Roach        return null;
2237684867eSGreg Roach    }
2247684867eSGreg Roach
2257684867eSGreg Roach    /**
2267684867eSGreg Roach     * @param Tree                 $tree
2277684867eSGreg Roach     * @param array<string,string> $params
2287684867eSGreg Roach     *
2297684867eSGreg Roach     * @return Builder
2307684867eSGreg Roach     */
2317684867eSGreg Roach    protected function locationsToFixQuery(Tree $tree, array $params): Builder
2327684867eSGreg Roach    {
2337684867eSGreg Roach        $query = DB::table('other')
2347684867eSGreg Roach            ->where('o_type', '=', Location::RECORD_TYPE)
2357684867eSGreg Roach            ->where('o_file', '=', $tree->id());
2367684867eSGreg Roach
2377684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
2387684867eSGreg Roach            $query->whereBetween('o_id', [$params['start'], $params['end']]);
2397684867eSGreg Roach        }
2407684867eSGreg Roach
2417684867eSGreg Roach        return $query;
2427684867eSGreg Roach    }
2437684867eSGreg Roach
2447684867eSGreg Roach    /**
245ce42304aSGreg Roach     * XREFs of media records that might need fixing.
246ce42304aSGreg Roach     *
247ce42304aSGreg Roach     * @param Tree                 $tree
248ce42304aSGreg Roach     * @param array<string,string> $params
249ce42304aSGreg Roach     *
25036779af1SGreg Roach     * @return Collection<int,string>|null
251ce42304aSGreg Roach     */
252*1ff45046SGreg Roach    protected function mediaToFix(Tree $tree, array $params): Collection|null
253ce42304aSGreg Roach    {
254ce42304aSGreg Roach        return null;
255ce42304aSGreg Roach    }
256ce42304aSGreg Roach
257ce42304aSGreg Roach    /**
2587684867eSGreg Roach     * @param Tree                 $tree
2597684867eSGreg Roach     * @param array<string,string> $params
2607684867eSGreg Roach     *
2617684867eSGreg Roach     * @return Builder
2627684867eSGreg Roach     */
2637684867eSGreg Roach    protected function mediaToFixQuery(Tree $tree, array $params): Builder
2647684867eSGreg Roach    {
2657684867eSGreg Roach        $query = DB::table('media')
2667684867eSGreg Roach            ->where('m_file', '=', $tree->id());
2677684867eSGreg Roach
2687684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
2697684867eSGreg Roach            $query->whereBetween('m_id', [$params['start'], $params['end']]);
2707684867eSGreg Roach        }
2717684867eSGreg Roach
2727684867eSGreg Roach        return $query;
2737684867eSGreg Roach    }
2747684867eSGreg Roach
2757684867eSGreg Roach    /**
276ce42304aSGreg Roach     * XREFs of note records that might need fixing.
277ce42304aSGreg Roach     *
278ce42304aSGreg Roach     * @param Tree                 $tree
279ce42304aSGreg Roach     * @param array<string,string> $params
280ce42304aSGreg Roach     *
28136779af1SGreg Roach     * @return Collection<int,string>|null
282ce42304aSGreg Roach     */
283*1ff45046SGreg Roach    protected function notesToFix(Tree $tree, array $params): Collection|null
284ce42304aSGreg Roach    {
285ce42304aSGreg Roach        return null;
286ce42304aSGreg Roach    }
287ce42304aSGreg Roach
288ce42304aSGreg Roach    /**
2897684867eSGreg Roach     * @param Tree                 $tree
2907684867eSGreg Roach     * @param array<string,string> $params
2917684867eSGreg Roach     *
2927684867eSGreg Roach     * @return Builder
2937684867eSGreg Roach     */
2947684867eSGreg Roach    protected function notesToFixQuery(Tree $tree, array $params): Builder
2957684867eSGreg Roach    {
2967684867eSGreg Roach        $query = DB::table('other')
2977684867eSGreg Roach            ->where('o_type', '=', Note::RECORD_TYPE)
2987684867eSGreg Roach            ->where('o_file', '=', $tree->id());
2997684867eSGreg Roach
3007684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
3017684867eSGreg Roach            $query->whereBetween('o_id', [$params['start'], $params['end']]);
3027684867eSGreg Roach        }
3037684867eSGreg Roach
3047684867eSGreg Roach        return $query;
3057684867eSGreg Roach    }
3067684867eSGreg Roach
3077684867eSGreg Roach    /**
308ce42304aSGreg Roach     * XREFs of repository records that might need fixing.
309ce42304aSGreg Roach     *
310ce42304aSGreg Roach     * @param Tree                 $tree
311ce42304aSGreg Roach     * @param array<string,string> $params
312ce42304aSGreg Roach     *
31336779af1SGreg Roach     * @return Collection<int,string>|null
314ce42304aSGreg Roach     */
315*1ff45046SGreg Roach    protected function repositoriesToFix(Tree $tree, array $params): Collection|null
316ce42304aSGreg Roach    {
317ce42304aSGreg Roach        return null;
318ce42304aSGreg Roach    }
319ce42304aSGreg Roach
320ce42304aSGreg Roach    /**
3217684867eSGreg Roach     * @param Tree                 $tree
3227684867eSGreg Roach     * @param array<string,string> $params
3237684867eSGreg Roach     *
3247684867eSGreg Roach     * @return Builder
3257684867eSGreg Roach     */
3267684867eSGreg Roach    protected function repositoriesToFixQuery(Tree $tree, array $params): Builder
3277684867eSGreg Roach    {
3287684867eSGreg Roach        $query = DB::table('other')
3297684867eSGreg Roach            ->where('o_type', '=', Repository::RECORD_TYPE)
3307684867eSGreg Roach            ->where('o_file', '=', $tree->id());
3317684867eSGreg Roach
3327684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
3337684867eSGreg Roach            $query->whereBetween('o_id', [$params['start'], $params['end']]);
3347684867eSGreg Roach        }
3357684867eSGreg Roach
3367684867eSGreg Roach        return $query;
3377684867eSGreg Roach    }
3387684867eSGreg Roach
3397684867eSGreg Roach    /**
340ce42304aSGreg Roach     * XREFs of source records that might need fixing.
341ce42304aSGreg Roach     *
342ce42304aSGreg Roach     * @param Tree                 $tree
343ce42304aSGreg Roach     * @param array<string,string> $params
344ce42304aSGreg Roach     *
34536779af1SGreg Roach     * @return Collection<int,string>|null
346ce42304aSGreg Roach     */
347*1ff45046SGreg Roach    protected function sourcesToFix(Tree $tree, array $params): Collection|null
348ce42304aSGreg Roach    {
349ce42304aSGreg Roach        return null;
350ce42304aSGreg Roach    }
351ce42304aSGreg Roach
352ce42304aSGreg Roach    /**
3537684867eSGreg Roach     * @param Tree                 $tree
3547684867eSGreg Roach     * @param array<string,string> $params
3557684867eSGreg Roach     *
3567684867eSGreg Roach     * @return Builder
3577684867eSGreg Roach     */
3587684867eSGreg Roach    protected function sourcesToFixQuery(Tree $tree, array $params): Builder
3597684867eSGreg Roach    {
3607684867eSGreg Roach        $query = DB::table('sources')
3617684867eSGreg Roach            ->where('s_file', '=', $tree->id());
3627684867eSGreg Roach
3637684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
3647684867eSGreg Roach            $query->whereBetween('s_id', [$params['start'], $params['end']]);
3657684867eSGreg Roach        }
3667684867eSGreg Roach
3677684867eSGreg Roach        return $query;
3687684867eSGreg Roach    }
3697684867eSGreg Roach
3707684867eSGreg Roach    /**
371ce42304aSGreg Roach     * XREFs of submitter records that might need fixing.
372ce42304aSGreg Roach     *
373ce42304aSGreg Roach     * @param Tree                 $tree
374ce42304aSGreg Roach     * @param array<string,string> $params
375ce42304aSGreg Roach     *
37636779af1SGreg Roach     * @return Collection<int,string>|null
377ce42304aSGreg Roach     */
378*1ff45046SGreg Roach    protected function submittersToFix(Tree $tree, array $params): Collection|null
379ce42304aSGreg Roach    {
380ce42304aSGreg Roach        return null;
381ce42304aSGreg Roach    }
382ce42304aSGreg Roach
383ce42304aSGreg Roach    /**
3847684867eSGreg Roach     * @param Tree                 $tree
3857684867eSGreg Roach     * @param array<string,string> $params
3867684867eSGreg Roach     *
3877684867eSGreg Roach     * @return Builder
3887684867eSGreg Roach     */
3897684867eSGreg Roach    protected function submittersToFixQuery(Tree $tree, array $params): Builder
3907684867eSGreg Roach    {
3917684867eSGreg Roach        $query = DB::table('other')
3927684867eSGreg Roach            ->where('o_type', '=', Submitter::RECORD_TYPE)
3937684867eSGreg Roach            ->where('o_file', '=', $tree->id());
3947684867eSGreg Roach
3957684867eSGreg Roach        if (isset($params['start'], $params['end'])) {
3967684867eSGreg Roach            $query->whereBetween('o_id', [$params['start'], $params['end']]);
3977684867eSGreg Roach        }
3987684867eSGreg Roach
3997684867eSGreg Roach        return $query;
4007684867eSGreg Roach    }
4017684867eSGreg Roach
4027684867eSGreg Roach    /**
403ce42304aSGreg Roach     * Merge pending changes of a given type.  We need to check all pending records.
404ce42304aSGreg Roach     *
40536779af1SGreg Roach     * @param Collection<int,string> $records
406ce42304aSGreg Roach     * @param Tree                   $tree
407ce42304aSGreg Roach     * @param string                 $type
408ce42304aSGreg Roach     *
409e93aa0bdSGreg Roach     * @return Collection<int,object>
410ce42304aSGreg Roach     */
411ce42304aSGreg Roach    private function mergePendingRecords(Collection $records, Tree $tree, string $type): Collection
412ce42304aSGreg Roach    {
413ce42304aSGreg Roach        $pending = DB::table('change')
414ce42304aSGreg Roach            ->where('gedcom_id', '=', $tree->id())
415ce42304aSGreg Roach            ->where('status', '=', 'pending')
416ce42304aSGreg Roach            ->where(static function (Builder $query) use ($type): void {
417ce42304aSGreg Roach                $query
418e93aa0bdSGreg Roach                    ->where('old_gedcom', 'LIKE', '%@ ' . $type . '\n%')
419e93aa0bdSGreg Roach                    ->orWhere('new_gedcom', 'LIKE', '%@ ' . $type . '\n%');
420ce42304aSGreg Roach            })
421ce42304aSGreg Roach            ->pluck('xref');
422ce42304aSGreg Roach
423ce42304aSGreg Roach        return $records
424ce42304aSGreg Roach            ->concat($pending)
425f25fc0f9SGreg Roach            ->map(static fn (string $xref): object => (object) ['xref' => $xref, 'type' => $type]);
426ce42304aSGreg Roach    }
427ce42304aSGreg Roach}
428