xref: /webtrees/app/Services/MapDataService.php (revision 64c83a78b4caa4bdc810446739df37776e706d8d)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Services;
21
22use Fisharebest\Webtrees\Gedcom;
23use Fisharebest\Webtrees\PlaceLocation;
24use Illuminate\Database\Capsule\Manager as DB;
25use stdClass;
26
27use function array_unshift;
28
29/**
30 * Process geographic data.
31 */
32class MapDataService
33{
34    /**
35     * @param int $id
36     *
37     * @return PlaceLocation
38     */
39    public function findById(int $id): PlaceLocation
40    {
41        $hierarchy = [];
42
43        while ($id !== 0) {
44            $row = DB::table('placelocation')
45                ->where('pl_id', '=', $id)
46                ->select(['pl_place', 'pl_parent_id'])
47                ->first();
48
49            if ($row === null) {
50                $id = 0;
51            } else {
52                $hierarchy[] = $row->pl_place;
53                $id          = (int) $row->pl_parent_id;
54            }
55        }
56
57        return new PlaceLocation(implode(Gedcom::PLACE_SEPARATOR, $hierarchy));
58    }
59
60    /**
61     * Which trees use a particular location?
62     *
63     * @param PlaceLocation $location
64     *
65     * @return array<string,array<stdClass>>
66     */
67    public function activePlaces(PlaceLocation $location): array
68    {
69        $parents  = $this->placeIdsForLocation($location);
70        $children = [];
71
72        $rows = DB::table('places')
73            ->join('gedcom', 'gedcom.gedcom_id', '=', 'p_file')
74            ->join('gedcom_setting', 'gedcom_setting.gedcom_id', '=', 'gedcom.gedcom_id')
75            ->where('setting_name', '=', 'title')
76            ->whereIn('p_parent_id', $parents)
77            ->select(['p_place', 'gedcom_name AS tree_name', 'setting_value AS tree_title', 'p_id'])
78            ->get();
79
80        foreach ($rows as $row) {
81            $children[$row->p_place][] = $row;
82        }
83
84        return $children;
85    }
86
87    /**
88     * Make sure that all places in the genealogy data also exist in the location data.
89     *
90     * @param PlaceLocation $location
91     *
92     * @return void
93     */
94    public function importMissingChildren(PlaceLocation $location): void
95    {
96        $parents = $this->placeIdsForLocation($location);
97
98        $rows = DB::table('places')
99            ->join('gedcom', 'gedcom.gedcom_id', '=', 'p_file')
100            ->whereIn('p_parent_id', $parents)
101            ->whereNotIn('p_place', static function ($query) use ($location): void {
102                $query->select(['pl_place'])->from('placelocation')->where('pl_parent_id', '=', $location->id());
103            })
104            ->groupBy(['p_place'])
105            ->select(['p_place'])
106            ->get();
107
108        if ($rows->isNotEmpty()) {
109            $tmp   = clone $location;
110            $level = 1;
111
112            while ($tmp->id() !== 0) {
113                $level++;
114                $tmp = $tmp->parent();
115            }
116
117            foreach ($rows as $row) {
118                $place_id = 1 + (int) DB::table('placelocation')->max('pl_id');
119
120                DB::table('placelocation')->insert([
121                    'pl_id'        => $place_id,
122                    'pl_parent_id' => $location->id(),
123                    'pl_level'     => $level,
124                    'pl_place'     => $row->p_place,
125                    'pl_lati'      => '',
126                    'pl_long'      => '',
127                    'pl_zoom'      => 2,
128                    'pl_icon'      => '',
129                ]);
130            }
131        }
132    }
133
134    /**
135     * Find all active places that match a location
136     *
137     * @param PlaceLocation $location
138     *
139     * @return array<string>
140     */
141    private function placeIdsForLocation(PlaceLocation $location): array
142    {
143        $hierarchy = [];
144
145        while ($location->id() !== 0) {
146            array_unshift($hierarchy, $location->locationName());
147            $location = $location->parent();
148        }
149
150        $place_ids = ['0'];
151
152        foreach ($hierarchy as $place_name) {
153            $place_ids = DB::table('places')
154                ->whereIn('p_parent_id', $place_ids)
155                ->where('p_place', '=', $place_name)
156                ->groupBy(['p_id'])
157                ->pluck('p_id')
158                ->all();
159        }
160
161        return $place_ids;
162    }
163
164    /**
165     * @param int $location_id
166     */
167    public function deleteRecursively(int $location_id): void
168    {
169        $child_ids = DB::table('placelocation')
170            ->where('pl_parent_id', '=', $location_id)
171            ->pluck('pl_id');
172
173        foreach ($child_ids as $child_id) {
174            $this->deleteRecursively((int) $child_id);
175        }
176
177        DB::table('placelocation')
178            ->where('pl_id', '=', $location_id)
179            ->delete();
180    }
181}
182