xref: /webtrees/app/Place.php (revision e9e853987811e8bd423dccf26f6ef57571f393eb)
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