summaryrefslogtreecommitdiff
path: root/app/Place.php
diff options
context:
space:
mode:
Diffstat (limited to 'app/Place.php')
-rw-r--r--app/Place.php175
1 files changed, 96 insertions, 79 deletions
diff --git a/app/Place.php b/app/Place.php
index de97ebc3dd..02e1219625 100644
--- a/app/Place.php
+++ b/app/Place.php
@@ -18,14 +18,18 @@ declare(strict_types=1);
namespace Fisharebest\Webtrees;
use Illuminate\Database\Capsule\Manager as DB;
+use Illuminate\Support\Collection;
/**
* A GEDCOM place (PLAC) object.
*/
class Place
{
- /** @var string[] e.g. array('Westminster', 'London', 'England') */
- private $gedcom_place;
+ /** @var string e.g. "Westminster, London, England" */
+ private $place_name;
+
+ /** @var Collection|string[] The parts of a place name, e.g. ["Westminster", "London", "England"] */
+ private $parts;
/** @var Tree We may have the same place name in different trees. */
private $tree;
@@ -33,57 +37,93 @@ class Place
/**
* Create a place.
*
- * @param string $gedcom_place
+ * @param string $place_name
* @param Tree $tree
*/
- public function __construct($gedcom_place, Tree $tree)
+ public function __construct(string $place_name, Tree $tree)
{
- if ($gedcom_place === '') {
- $this->gedcom_place = [];
- } else {
- $this->gedcom_place = explode(Gedcom::PLACE_SEPARATOR, $gedcom_place);
- }
+ // Ignore any empty parts in place names such as "Village, , , Country".
+ $this->parts = (new Collection(preg_split(Gedcom::PLACE_SEPARATOR_REGEX, $place_name)))
+ ->filter();
+
+ // Rebuild the placename in the correct format.
+ $this->place_name = $this->parts->implode(Gedcom::PLACE_SEPARATOR);
+
$this->tree = $tree;
}
/**
- * Extract the country (last part) of a place name.
+ * Get the higher level place.
*
- * @return string - e.g. "England"
+ * @return Place
*/
- public function lastPart(): string
+ public function parent(): Place
{
- return $this->gedcom_place[count($this->gedcom_place) - 1] ?? '';
+ return new static($this->parts->slice(1)->implode(Gedcom::PLACE_SEPARATOR), $this->tree);
}
/**
- * Get the identifier for a place.
+ * The database row that contains this place.
+ * Note that due to database collation, both "Quebec" and "Québec" will share the same row.
*
* @return int
*/
- public function getPlaceId(): int
+ public function id(): int
{
- $place_id = 0;
+ return app()->make('cache.array')->rememberForever('place:' . $this->place_name, function () {
+ // The "top-level" place won't exist in the database.
+ if ($this->parts->isEmpty()) {
+ return 0;
+ }
+
+ $parent_place = $this->parent();
- foreach (array_reverse($this->gedcom_place) as $place) {
$place_id = (int) DB::table('places')
->where('p_file', '=', $this->tree->id())
- ->where('p_place', '=', $place)
- ->where('p_parent_id', '=', $place_id)
+ ->where('p_place', '=', $this->parts->first())
+ ->where('p_parent_id', '=', $parent_place->id())
->value('p_id');
- }
- return $place_id;
+ if ($place_id === 0) {
+ $place = $this->parts->first();
+
+ DB::table('places')->insert([
+ 'p_file' => $this->tree->id(),
+ 'p_place' => $place,
+ 'p_parent_id' => $parent_place->id(),
+ 'p_std_soundex' => Soundex::russell($place),
+ 'p_dm_soundex' => Soundex::daitchMokotoff($place),
+ ]);
+
+ $place_id = (int) DB::connection()->getPdo()->lastInsertId();
+ }
+
+ return $place_id;
+ });
}
/**
- * Get the higher level place.
+ * Extract the locality (first parts) of a place name.
*
- * @return Place
+ * @param int $n
+ *
+ * @return Collection
*/
- public function getParentPlace(): Place
+ public function firstParts(int $n): Collection
{
- return new self(implode(Gedcom::PLACE_SEPARATOR, array_slice($this->gedcom_place, 1)), $this->tree);
+ return $this->parts->slice(0, $n);
+ }
+
+ /**
+ * Extract the country (last parts) of a place name.
+ *
+ * @param int $n
+ *
+ * @return Collection
+ */
+ public function lastParts(int $n): Collection
+ {
+ return $this->parts->slice(-$n);
}
/**
@@ -93,15 +133,15 @@ class Place
*/
public function getChildPlaces(): array
{
- if ($this->getPlaceId()) {
- $parent_text = Gedcom::PLACE_SEPARATOR . $this->getGedcomName();
+ if ($this->place_name !== '') {
+ $parent_text = Gedcom::PLACE_SEPARATOR . $this->place_name;
} else {
$parent_text = '';
}
return DB::table('places')
->where('p_file', '=', $this->tree->id())
- ->where('p_parent_id', '=', $this->getPlaceId())
+ ->where('p_parent_id', '=', $this->id())
->orderBy(DB::raw('p_place /*! COLLATE ' . I18N::collation() . ' */'))
->pluck('p_place')
->map(function (string $place) use ($parent_text): Place {
@@ -118,19 +158,19 @@ class Place
public function url(): string
{
return route('place-hierarchy', [
- 'parent' => array_reverse($this->gedcom_place),
+ 'parent' => $this->parts->reverse()->all(),
'ged' => $this->tree->name(),
]);
}
/**
- * Format this name for GEDCOM data.
+ * Format this place for GEDCOM data.
*
* @return string
*/
- public function getGedcomName(): string
+ public function gedcomName(): string
{
- return implode(Gedcom::PLACE_SEPARATOR, $this->gedcom_place);
+ return $this->place_name;
}
/**
@@ -138,85 +178,62 @@ class Place
*
* @return string
*/
- public function getPlaceName(): string
+ public function placeName(): string
{
- if (empty($this->gedcom_place)) {
- return I18N::translate('unknown');
- }
+ $place_name = $this->parts->first() ?? I18N::translate('unknown');
- return '<span dir="auto">' . e($this->gedcom_place[0]) . '</span>';
- }
-
- /**
- * Is this a null/empty/missing/invalid place?
- *
- * @return bool
- */
- public function isEmpty(): bool
- {
- return empty($this->gedcom_place);
+ return '<span dir="auto">' . e($place_name) . '</span>';
}
/**
* Generate the place name for display, including the full hierarchy.
*
+ * @param bool $link
+ *
* @return string
*/
- public function getFullName()
+ public function fullName(bool $link = false)
{
- if (true) {
- // If a place hierarchy is a single entity
- return '<span dir="auto">' . e(implode(I18N::$list_separator, $this->gedcom_place)) . '</span>';
+ if ($this->parts->isEmpty()) {
+ return '';
}
- // If a place hierarchy is a list of distinct items
- $tmp = [];
- foreach ($this->gedcom_place as $place) {
- $tmp[] = '<span dir="auto">' . e($place) . '</span>';
+ $full_name = $this->parts->implode(I18N::$list_separator);
+
+ if ($link) {
+ return '<a dir="auto" href="' . e($this->url()) . '">' . e($full_name) . '</a>';
}
- return implode(I18N::$list_separator, $tmp);
+ return '<span dir="auto">' . e($full_name) . '</span>';
}
/**
* For lists and charts, where the full name won’t fit.
*
+ * @param bool $link
+ *
* @return string
*/
- public function getShortName()
+ public function shortName(bool $link = false)
{
$SHOW_PEDIGREE_PLACES = (int) $this->tree->getPreference('SHOW_PEDIGREE_PLACES');
- if ($SHOW_PEDIGREE_PLACES >= count($this->gedcom_place)) {
- // A short place name - no need to abbreviate
- return $this->getFullName();
- }
-
// Abbreviate the place name, for lists
if ($this->tree->getPreference('SHOW_PEDIGREE_PLACES_SUFFIX')) {
- // The *last* $SHOW_PEDIGREE_PLACES components
- $short_name = implode(Gedcom::PLACE_SEPARATOR, array_slice($this->gedcom_place, -$SHOW_PEDIGREE_PLACES));
+ $parts = $this->lastParts($SHOW_PEDIGREE_PLACES);
} else {
- // The *first* $SHOW_PEDIGREE_PLACES components
- $short_name = implode(Gedcom::PLACE_SEPARATOR, array_slice($this->gedcom_place, 0, $SHOW_PEDIGREE_PLACES));
+ $parts = $this->firstParts($SHOW_PEDIGREE_PLACES);
}
+ $short_name = $parts->implode(I18N::$list_separator);
+
// Add a tool-tip showing the full name
- return '<span title="' . e($this->getGedcomName()) . '" dir="auto">' . e($short_name) . '</span>';
- }
+ $title = strip_tags($this->fullName());
- /**
- * For the Place hierarchy "list all" option
- *
- * @return string
- */
- public function getReverseName(): string
- {
- $tmp = [];
- foreach (array_reverse($this->gedcom_place) as $place) {
- $tmp[] = '<span dir="auto">' . e($place) . '</span>';
+ if ($link) {
+ return '<a dir="auto" href="' . e($this->url()) . '" title="' . $title . '"">' . e($short_name) . '</a>';
}
- return implode(I18N::$list_separator, $tmp);
+ return '<span dir="auto">' . e($short_name) . '</span>';
}
}