1<?php 2/** 3 * webtrees: online genealogy 4 * Copyright (C) 2019 webtrees development team 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation, either version 3 of the License, or 8 * (at your option) any later version. 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16declare(strict_types=1); 17 18namespace Fisharebest\Webtrees; 19 20use Illuminate\Database\Capsule\Manager as DB; 21 22/** 23 * A GEDCOM place (PLAC) object. 24 */ 25class Place 26{ 27 public const GEDCOM_SEPARATOR = ', '; 28 29 /** @var string[] e.g. array('Westminster', 'London', 'England') */ 30 private $gedcom_place; 31 32 /** @var Tree We may have the same place name in different trees. */ 33 private $tree; 34 35 /** 36 * Create a place. 37 * 38 * @param string $gedcom_place 39 * @param Tree $tree 40 */ 41 public function __construct($gedcom_place, Tree $tree) 42 { 43 if ($gedcom_place === '') { 44 $this->gedcom_place = []; 45 } else { 46 $this->gedcom_place = explode(self::GEDCOM_SEPARATOR, $gedcom_place); 47 } 48 $this->tree = $tree; 49 } 50 51 /** 52 * Extract the country (last part) of a place name. 53 * 54 * @return string - e.g. "England" 55 */ 56 public function lastPart(): string 57 { 58 return $this->gedcom_place[count($this->gedcom_place) - 1] ?? ''; 59 } 60 61 /** 62 * Get the identifier for a place. 63 * 64 * @return int 65 */ 66 public function getPlaceId(): int 67 { 68 $place_id = 0; 69 70 foreach (array_reverse($this->gedcom_place) as $place) { 71 $place_id = (int) DB::table('places') 72 ->where('p_file', '=', $this->tree->id()) 73 ->where('p_place', '=', $place) 74 ->where('p_parent_id', '=', $place_id) 75 ->value('p_id'); 76 } 77 78 return $place_id; 79 } 80 81 /** 82 * Get the higher level place. 83 * 84 * @return Place 85 */ 86 public function getParentPlace(): Place 87 { 88 return new self(implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, 1)), $this->tree); 89 } 90 91 /** 92 * Get the lower level places. 93 * 94 * @return Place[] 95 */ 96 public function getChildPlaces(): array 97 { 98 if ($this->getPlaceId()) { 99 $parent_text = self::GEDCOM_SEPARATOR . $this->getGedcomName(); 100 } else { 101 $parent_text = ''; 102 } 103 104 return DB::table('places') 105 ->where('p_file', '=', $this->tree->id()) 106 ->where('p_parent_id', '=', $this->getPlaceId()) 107 ->orderBy(DB::raw('p_place /*! COLLATE ' . I18N::collation() . ' */')) 108 ->pluck('p_place') 109 ->map(function (string $place) use ($parent_text): Place { 110 return new self($place . $parent_text, $this->tree); 111 }) 112 ->all(); 113 } 114 115 /** 116 * Create a URL to the place-hierarchy page. 117 * 118 * @return string 119 */ 120 public function url(): string 121 { 122 return route('place-hierarchy', [ 123 'parent' => array_reverse($this->gedcom_place), 124 'ged' => $this->tree->name(), 125 ]); 126 } 127 128 /** 129 * Format this name for GEDCOM data. 130 * 131 * @return string 132 */ 133 public function getGedcomName(): string 134 { 135 return implode(self::GEDCOM_SEPARATOR, $this->gedcom_place); 136 } 137 138 /** 139 * Format this place for display on screen. 140 * 141 * @return string 142 */ 143 public function getPlaceName(): string 144 { 145 if (empty($this->gedcom_place)) { 146 return I18N::translate('unknown'); 147 } 148 149 return '<span dir="auto">' . e($this->gedcom_place[0]) . '</span>'; 150 } 151 152 /** 153 * Is this a null/empty/missing/invalid place? 154 * 155 * @return bool 156 */ 157 public function isEmpty(): bool 158 { 159 return empty($this->gedcom_place); 160 } 161 162 /** 163 * Generate the place name for display, including the full hierarchy. 164 * 165 * @return string 166 */ 167 public function getFullName() 168 { 169 if (true) { 170 // If a place hierarchy is a single entity 171 return '<span dir="auto">' . e(implode(I18N::$list_separator, $this->gedcom_place)) . '</span>'; 172 } 173 174 // If a place hierarchy is a list of distinct items 175 $tmp = []; 176 foreach ($this->gedcom_place as $place) { 177 $tmp[] = '<span dir="auto">' . e($place) . '</span>'; 178 } 179 180 return implode(I18N::$list_separator, $tmp); 181 } 182 183 /** 184 * For lists and charts, where the full name won’t fit. 185 * 186 * @return string 187 */ 188 public function getShortName() 189 { 190 $SHOW_PEDIGREE_PLACES = (int) $this->tree->getPreference('SHOW_PEDIGREE_PLACES'); 191 192 if ($SHOW_PEDIGREE_PLACES >= count($this->gedcom_place)) { 193 // A short place name - no need to abbreviate 194 return $this->getFullName(); 195 } 196 197 // Abbreviate the place name, for lists 198 if ($this->tree->getPreference('SHOW_PEDIGREE_PLACES_SUFFIX')) { 199 // The *last* $SHOW_PEDIGREE_PLACES components 200 $short_name = implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, -$SHOW_PEDIGREE_PLACES)); 201 } else { 202 // The *first* $SHOW_PEDIGREE_PLACES components 203 $short_name = implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, 0, $SHOW_PEDIGREE_PLACES)); 204 } 205 206 // Add a tool-tip showing the full name 207 return '<span title="' . e($this->getGedcomName()) . '" dir="auto">' . e($short_name) . '</span>'; 208 } 209 210 /** 211 * For the Place hierarchy "list all" option 212 * 213 * @return string 214 */ 215 public function getReverseName(): string 216 { 217 $tmp = []; 218 foreach (array_reverse($this->gedcom_place) as $place) { 219 $tmp[] = '<span dir="auto">' . e($place) . '</span>'; 220 } 221 222 return implode(I18N::$list_separator, $tmp); 223 } 224} 225