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