xref: /webtrees/app/Services/LinkedRecordService.php (revision 9d38fa282da4881cce7818eaa091645829a8bee4)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2023 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Services;
21
22use Fisharebest\Webtrees\Family;
23use Fisharebest\Webtrees\GedcomRecord;
24use Fisharebest\Webtrees\Individual;
25use Fisharebest\Webtrees\Location;
26use Fisharebest\Webtrees\Media;
27use Fisharebest\Webtrees\Note;
28use Fisharebest\Webtrees\Registry;
29use Fisharebest\Webtrees\Repository;
30use Fisharebest\Webtrees\Source;
31use Fisharebest\Webtrees\Submitter;
32use Illuminate\Database\Capsule\Manager as DB;
33use Illuminate\Database\Query\Builder;
34use Illuminate\Database\Query\Expression;
35use Illuminate\Database\Query\JoinClause;
36use Illuminate\Support\Collection;
37
38use function addcslashes;
39
40/**
41 * Find records linked to other records
42 */
43class LinkedRecordService
44{
45    /**
46     * Find all records linked to a record.
47     *
48     * @param GedcomRecord $record
49     *
50     * @return Collection<int,Family>
51     */
52    public function allLinkedRecords(GedcomRecord $record): Collection
53    {
54        $like = addcslashes($record->xref(), '\\%_');
55
56        $union = DB::table('change')
57            ->where('gedcom_id', '=', $record->tree()->id())
58            ->where('new_gedcom', 'LIKE', '%@' . $like . '@%')
59            ->where('new_gedcom', 'NOT LIKE', '0 @' . $like . '@%')
60            ->whereIn('change_id', function (Builder $query) use ($record): void {
61                $query
62                    ->select([new Expression('MAX(change_id)')])
63                    ->from('change')
64                    ->where('gedcom_id', '=', $record->tree()->id())
65                    ->where('status', '=', 'pending')
66                    ->groupBy(['xref']);
67            })
68            ->select(['xref']);
69
70        $xrefs = DB::table('link')
71            ->where('l_file', '=', $record->tree()->id())
72            ->where('l_to', '=', $record->xref())
73            ->select(['l_from'])
74            ->union($union)
75            ->pluck('l_from');
76
77        return $xrefs->map(static fn (string $xref) => Registry::gedcomRecordFactory()->make($xref, $record->tree()));
78    }
79
80    /**
81     * Find families linked to a record.
82     *
83     * @param GedcomRecord $record
84     * @param string|null  $link_type
85     *
86     * @return Collection<int,Family>
87     */
88    public function linkedFamilies(GedcomRecord $record, string $link_type = null): Collection
89    {
90        $query = DB::table('families')
91            ->join('link', static function (JoinClause $join): void {
92                $join
93                    ->on('l_file', '=', 'f_file')
94                    ->on('l_from', '=', 'f_id');
95            })
96            ->where('f_file', '=', $record->tree()->id())
97            ->where('l_to', '=', $record->xref());
98
99        if ($link_type !== null) {
100            $query->where('l_type', '=', $link_type);
101        }
102
103        return $query
104            ->distinct()
105            ->select(['families.*'])
106            ->get()
107            ->map(Registry::familyFactory()->mapper($record->tree()))
108            ->filter(GedcomRecord::accessFilter());
109    }
110
111    /**
112     * Find individuals linked to a record.
113     *
114     * @param GedcomRecord $record
115     * @param string|null  $link_type
116     *
117     * @return Collection<int,Individual>
118     */
119    public function linkedIndividuals(GedcomRecord $record, string $link_type = null): Collection
120    {
121        $query = DB::table('individuals')
122            ->join('link', static function (JoinClause $join): void {
123                $join
124                    ->on('l_file', '=', 'i_file')
125                    ->on('l_from', '=', 'i_id');
126            })
127            ->where('i_file', '=', $record->tree()->id())
128            ->where('l_to', '=', $record->xref());
129
130        if ($link_type !== null) {
131            $query->where('l_type', '=', $link_type);
132        }
133
134        return $query
135            ->distinct()
136            ->select(['individuals.*'])
137            ->get()
138            ->map(Registry::individualFactory()->mapper($record->tree()))
139            ->filter(GedcomRecord::accessFilter());
140    }
141
142    /**
143     * Find locations linked to a record.
144     *
145     * @param GedcomRecord $record
146     *
147     * @return Collection<int,Location>
148     */
149    public function linkedLocations(GedcomRecord $record): Collection
150    {
151        return DB::table('other')
152            ->join('link', static function (JoinClause $join): void {
153                $join
154                    ->on('l_file', '=', 'o_file')
155                    ->on('l_from', '=', 'o_id');
156            })
157            ->where('o_file', '=', $record->tree()->id())
158            ->where('o_type', '=', Location::RECORD_TYPE)
159            ->where('l_to', '=', $record->xref())
160            ->distinct()
161            ->select(['other.*'])
162            ->get()
163            ->map(Registry::locationFactory()->mapper($record->tree()))
164            ->filter(GedcomRecord::accessFilter());
165    }
166
167    /**
168     * Find media objects linked to a record.
169     *
170     * @param GedcomRecord $record
171     *
172     * @return Collection<int,Media>
173     */
174    public function linkedMedia(GedcomRecord $record): Collection
175    {
176        return DB::table('media')
177            ->join('link', static function (JoinClause $join): void {
178                $join
179                    ->on('l_file', '=', 'm_file')
180                    ->on('l_from', '=', 'm_id');
181            })
182            ->where('m_file', '=', $record->tree()->id())
183            ->where('l_to', '=', $record->xref())
184            ->distinct()
185            ->select(['media.*'])
186            ->get()
187            ->map(Registry::mediaFactory()->mapper($record->tree()))
188            ->filter(GedcomRecord::accessFilter());
189    }
190
191    /**
192     * Find notes linked to a record.
193     *
194     * @param GedcomRecord $record
195     *
196     * @return Collection<int,Note>
197     */
198    public function linkedNotes(GedcomRecord $record): Collection
199    {
200        return DB::table('other')
201            ->join('link', static function (JoinClause $join): void {
202                $join
203                    ->on('l_file', '=', 'o_file')
204                    ->on('l_from', '=', 'o_id');
205            })
206            ->where('o_file', '=', $record->tree()->id())
207            ->where('o_type', '=', Note::RECORD_TYPE)
208            ->where('l_to', '=', $record->xref())
209            ->distinct()
210            ->select(['other.*'])
211            ->get()
212            ->map(Registry::noteFactory()->mapper($record->tree()))
213            ->filter(GedcomRecord::accessFilter());
214    }
215
216    /**
217     * Find repositories linked to a record.
218     *
219     * @param GedcomRecord $record
220     *
221     * @return Collection<int,Repository>
222     */
223    public function linkedRepositories(GedcomRecord $record): Collection
224    {
225        return DB::table('other')
226            ->join('link', static function (JoinClause $join): void {
227                $join
228                    ->on('l_file', '=', 'o_file')
229                    ->on('l_from', '=', 'o_id');
230            })
231            ->where('o_file', '=', $record->tree()->id())
232            ->where('o_type', '=', Repository::RECORD_TYPE)
233            ->where('l_to', '=', $record->xref())
234            ->distinct()
235            ->select(['other.*'])
236            ->get()
237            ->map(Registry::repositoryFactory()->mapper($record->tree()))
238            ->filter(GedcomRecord::accessFilter());
239    }
240
241    /**
242     * Find sources linked to a record.
243     *
244     * @param GedcomRecord $record
245     *
246     * @return Collection<int,Source>
247     */
248    public function linkedSources(GedcomRecord $record): Collection
249    {
250        return DB::table('sources')
251            ->join('link', static function (JoinClause $join): void {
252                $join
253                    ->on('l_file', '=', 's_file')
254                    ->on('l_from', '=', 's_id');
255            })
256            ->where('s_file', '=', $record->tree()->id())
257            ->where('l_to', '=', $record->xref())
258            ->distinct()
259            ->select(['sources.*'])
260            ->get()
261            ->map(Registry::sourceFactory()->mapper($record->tree()))
262            ->filter(GedcomRecord::accessFilter());
263    }
264
265    /**
266     * Find submitters linked to a record.
267     *
268     * @param GedcomRecord $record
269     *
270     * @return Collection<int,Repository>
271     */
272    public function linkedSubmitters(GedcomRecord $record): Collection
273    {
274        return DB::table('other')
275            ->join('link', static function (JoinClause $join): void {
276                $join
277                    ->on('l_file', '=', 'o_file')
278                    ->on('l_from', '=', 'o_id');
279            })
280            ->where('o_file', '=', $record->tree()->id())
281            ->where('o_type', '=', Submitter::RECORD_TYPE)
282            ->where('l_to', '=', $record->xref())
283            ->distinct()
284            ->select(['other.*'])
285            ->distinct()
286            ->get()
287            ->map(Registry::repositoryFactory()->mapper($record->tree()))
288            ->filter(GedcomRecord::accessFilter());
289    }
290}
291