xref: /webtrees/app/Place.php (revision c1fdddd6cc1e4d739654ac456f211e6d53bcbf14)
1<?php
2namespace Fisharebest\Webtrees;
3
4/**
5 * webtrees: online genealogy
6 * Copyright (C) 2015 webtrees development team
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * Class Place - Gedcom Place functionality.
21 */
22class Place {
23	const GEDCOM_SEPARATOR = ', ';
24
25	/**
26	 * @var string[] e.g. array('Westminster', 'London', 'England')
27	 */
28	private $gedcom_place;
29
30	/**
31	 * @var Tree We may have the same place name in different trees
32	 */
33	private $tree;
34
35	/**
36	 * @param string $gedcom_place
37	 * @param Tree   $tree
38	 */
39	public function __construct($gedcom_place, Tree $tree) {
40		if ($gedcom_place) {
41			$this->gedcom_place = explode(self::GEDCOM_SEPARATOR, $gedcom_place);
42		} else {
43			// Empty => "Top level"
44			$this->gedcom_place = array();
45		}
46		$this->tree = $tree;
47	}
48
49	/**
50	 * @return integer
51	 */
52	public function getPlaceId() {
53		$place_id = 0;
54		foreach (array_reverse($this->gedcom_place) as $place) {
55			$place_id = Database::prepare(
56				"SELECT SQL_CACHE p_id FROM `##places` WHERE p_parent_id = :parent_id AND p_place = :place AND p_file = :tree_id"
57			)->execute(array(
58				'parent_id' => $place_id,
59				'place'     => $place,
60				'tree_id'   => $this->tree->getTreeId(),
61			))->fetchOne();
62		}
63
64		return $place_id;
65	}
66
67	/**
68	 * @return Place
69	 */
70	public function getParentPlace() {
71		return new Place(implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, 1)), $this->tree);
72	}
73
74	/**
75	 * @return Place[]
76	 */
77	public function getChildPlaces() {
78		$children = array();
79		if ($this->getPlaceId()) {
80			$parent_text = self::GEDCOM_SEPARATOR . $this->getGedcomName();
81		} else {
82			$parent_text = '';
83		}
84
85		$rows = Database::prepare(
86			"SELECT SQL_CACHE p_place FROM `##places`" .
87			" WHERE p_parent_id = :parent_id AND p_file = :tree_id" .
88			" ORDER BY p_place COLLATE :collation"
89		)->execute(array(
90			'parent_id' => $this->getPlaceId(),
91			'tree_id'   => $this->tree->getTreeId(),
92			'collation' => I18N::$collation,
93		))->fetchOneColumn();
94		foreach ($rows as $row) {
95			$children[] = new Place($row . $parent_text, $this->tree);
96		}
97
98		return $children;
99	}
100
101	/**
102	 * @return string
103	 */
104	public function getURL() {
105		$url = 'placelist.php';
106		foreach (array_reverse($this->gedcom_place) as $n => $place) {
107			$url .= $n ? '&amp;' : '?';
108			$url .= 'parent%5B%5D=' . rawurlencode($place);
109		}
110		$url .= '&amp;ged=' . $this->tree->getNameHtml();
111
112		return $url;
113	}
114
115	/**
116	 * @return string
117	 */
118	public function getGedcomName() {
119		return implode(self::GEDCOM_SEPARATOR, $this->gedcom_place);
120	}
121
122	/**
123	 * @return string
124	 */
125	public function getPlaceName() {
126		$place = reset($this->gedcom_place);
127
128		return $place ? '<span dir="auto">' . Filter::escapeHtml($place) . '</span>' : I18N::translate('unknown');
129	}
130
131	/**
132	 * @return bool
133	 */
134	public function isEmpty() {
135		return empty($this->gedcom_place);
136	}
137
138	/**
139	 * @return string
140	 */
141	public function getFullName() {
142		if (true) {
143			// If a place hierarchy is a single entity
144			return '<span dir="auto">' . Filter::escapeHtml(implode(I18N::$list_separator, $this->gedcom_place)) . '</span>';
145		} else {
146			// If a place hierarchy is a list of distinct items
147			$tmp = array();
148			foreach ($this->gedcom_place as $place) {
149				$tmp[] = '<span dir="auto">' . Filter::escapeHtml($place) . '</span>';
150			}
151
152			return implode(I18N::$list_separator, $tmp);
153		}
154	}
155
156	/**
157	 * For lists and charts, where the full name won’t fit.
158	 *
159	 * @return string
160	 */
161	public function getShortName() {
162		$SHOW_PEDIGREE_PLACES = $this->tree->getPreference('SHOW_PEDIGREE_PLACES');
163
164		if ($SHOW_PEDIGREE_PLACES >= count($this->gedcom_place)) {
165			// A short place name - no need to abbreviate
166			return $this->getFullName();
167		} else {
168			// Abbreviate the place name, for lists
169			if ($this->tree->getPreference('SHOW_PEDIGREE_PLACES_SUFFIX')) {
170				// The *last* $SHOW_PEDIGREE_PLACES components
171				$short_name = implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, -$SHOW_PEDIGREE_PLACES));
172			} else {
173				// The *first* $SHOW_PEDIGREE_PLACES components
174				$short_name = implode(self::GEDCOM_SEPARATOR, array_slice($this->gedcom_place, 0, $SHOW_PEDIGREE_PLACES));
175			}
176			// Add a tool-tip showing the full name
177			return '<span title="' . Filter::escapeHtml($this->getGedcomName()) . '" dir="auto">' . Filter::escapeHtml($short_name) . '</span>';
178		}
179	}
180
181	/**
182	 * For the "view all" option of placelist.php and find.php
183	 *
184	 * @return string
185	 */
186	public function getReverseName() {
187		$tmp = array();
188		foreach (array_reverse($this->gedcom_place) as $place) {
189			$tmp[] = '<span dir="auto">' . Filter::escapeHtml($place) . '</span>';
190		}
191
192		return implode(I18N::$list_separator, $tmp);
193	}
194
195	/**
196	 * @param Tree $tree
197	 *
198	 * @return string[]
199	 */
200	public static function allPlaces($tree) {
201		$places = array();
202		$rows =
203			Database::prepare(
204				"SELECT SQL_CACHE CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place)" .
205				" FROM      `##places` AS p1" .
206				" LEFT JOIN `##places` AS p2 ON (p1.p_parent_id = p2.p_id)" .
207				" LEFT JOIN `##places` AS p3 ON (p2.p_parent_id = p3.p_id)" .
208				" LEFT JOIN `##places` AS p4 ON (p3.p_parent_id = p4.p_id)" .
209				" LEFT JOIN `##places` AS p5 ON (p4.p_parent_id = p5.p_id)" .
210				" LEFT JOIN `##places` AS p6 ON (p5.p_parent_id = p6.p_id)" .
211				" LEFT JOIN `##places` AS p7 ON (p6.p_parent_id = p7.p_id)" .
212				" LEFT JOIN `##places` AS p8 ON (p7.p_parent_id = p8.p_id)" .
213				" LEFT JOIN `##places` AS p9 ON (p8.p_parent_id = p9.p_id)" .
214				" WHERE p1.p_file = :tree_id" .
215				" ORDER BY CONCAT_WS(', ', p9.p_place, p8.p_place, p7.p_place, p6.p_place, p5.p_place, p4.p_place, p3.p_place, p2.p_place, p1.p_place) COLLATE :collate"
216			)
217			->execute(array(
218				'tree_id' => $tree->getTreeId(),
219				'collate' => I18N::$collation,
220			))->fetchOneColumn();
221		foreach ($rows as $row) {
222			$places[] = new Place($row, $tree);
223		}
224		return $places;
225	}
226
227	/**
228	 * @param string  $filter
229	 * @param Tree    $tree
230	 *
231	 * @return Place[]
232	 */
233	public static function findPlaces($filter, Tree $tree) {
234		$places = array();
235		$rows =
236			Database::prepare(
237				"SELECT SQL_CACHE CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place)" .
238				" FROM      `##places` AS p1" .
239				" LEFT JOIN `##places` AS p2 ON (p1.p_parent_id = p2.p_id)" .
240				" LEFT JOIN `##places` AS p3 ON (p2.p_parent_id = p3.p_id)" .
241				" LEFT JOIN `##places` AS p4 ON (p3.p_parent_id = p4.p_id)" .
242				" LEFT JOIN `##places` AS p5 ON (p4.p_parent_id = p5.p_id)" .
243				" LEFT JOIN `##places` AS p6 ON (p5.p_parent_id = p6.p_id)" .
244				" LEFT JOIN `##places` AS p7 ON (p6.p_parent_id = p7.p_id)" .
245				" LEFT JOIN `##places` AS p8 ON (p7.p_parent_id = p8.p_id)" .
246				" LEFT JOIN `##places` AS p9 ON (p8.p_parent_id = p9.p_id)" .
247				" WHERE CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place) LIKE CONCAT('%', :filter_1, '%') AND CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place) NOT LIKE CONCAT('%,%', :filter_2, '%') AND p1.p_file = :tree_id" .
248				" ORDER BY  CONCAT_WS(', ', p1.p_place, p2.p_place, p3.p_place, p4.p_place, p5.p_place, p6.p_place, p7.p_place, p8.p_place, p9.p_place) COLLATE '" . I18N::$collation . "'"
249			)->execute(array(
250				'filter_1' => preg_quote($filter),
251				'filter_2' => preg_quote($filter),
252				'tree_id'  => $tree->getTreeId()
253			))->fetchOneColumn();
254		foreach ($rows as $row) {
255			$places[] = new Place($row, $tree);
256		}
257		return $places;
258	}
259}
260