Skip to content

Commit dd39369

Browse files
committed
Slugs: Added lookup system using history
Switched page lookup to use this.
1 parent dd5375f commit dd39369

File tree

4 files changed

+89
-17
lines changed

4 files changed

+89
-17
lines changed

app/Entities/Controllers/PageController.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
use BookStack\Entities\Tools\PageEditActivity;
1818
use BookStack\Entities\Tools\PageEditorData;
1919
use BookStack\Exceptions\NotFoundException;
20-
use BookStack\Exceptions\NotifyException;
2120
use BookStack\Exceptions\PermissionsException;
2221
use BookStack\Http\Controller;
2322
use BookStack\Permissions\Permission;
@@ -140,9 +139,7 @@ public function show(string $bookSlug, string $pageSlug)
140139
try {
141140
$page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
142141
} catch (NotFoundException $e) {
143-
$revision = $this->entityQueries->revisions->findLatestVersionBySlugs($bookSlug, $pageSlug);
144-
$page = $revision->page ?? null;
145-
142+
$page = $this->entityQueries->findVisibleByOldSlugs('page', $pageSlug, $bookSlug);
146143
if (is_null($page)) {
147144
throw $e;
148145
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace BookStack\Entities\Models;
4+
5+
use BookStack\App\Model;
6+
use BookStack\Permissions\Models\JointPermission;
7+
use Illuminate\Database\Eloquent\Relations\HasMany;
8+
9+
/**
10+
* @property int $id
11+
* @property int $sluggable_id
12+
* @property string $sluggable_type
13+
* @property string $slug
14+
* @property ?string $parent_slug
15+
*/
16+
class SlugHistory extends Model
17+
{
18+
protected $table = 'slug_history';
19+
20+
public function jointPermissions(): HasMany
21+
{
22+
return $this->hasMany(JointPermission::class, 'entity_id', 'sluggable_id')
23+
->whereColumn('joint_permissions.entity_type', '=', 'slug_history.sluggable_type');
24+
}
25+
}

app/Entities/Queries/EntityQueries.php

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use BookStack\Entities\Models\Entity;
66
use BookStack\Entities\Models\EntityTable;
7+
use BookStack\Entities\Tools\SlugHistory;
78
use Illuminate\Database\Eloquent\Builder;
89
use Illuminate\Database\Query\Builder as QueryBuilder;
910
use Illuminate\Database\Query\JoinClause;
@@ -18,6 +19,7 @@ public function __construct(
1819
public ChapterQueries $chapters,
1920
public PageQueries $pages,
2021
public PageRevisionQueries $revisions,
22+
protected SlugHistory $slugHistory,
2123
) {
2224
}
2325

@@ -31,9 +33,30 @@ public function findVisibleByStringIdentifier(string $identifier): ?Entity
3133
$explodedId = explode(':', $identifier);
3234
$entityType = $explodedId[0];
3335
$entityId = intval($explodedId[1]);
34-
$queries = $this->getQueriesForType($entityType);
3536

36-
return $queries->findVisibleById($entityId);
37+
return $this->findVisibleById($entityType, $entityId);
38+
}
39+
40+
/**
41+
* Find an entity by its ID.
42+
*/
43+
public function findVisibleById(string $type, int $id): ?Entity
44+
{
45+
$queries = $this->getQueriesForType($type);
46+
return $queries->findVisibleById($id);
47+
}
48+
49+
/**
50+
* Find an entity by looking up old slugs in the slug history.
51+
*/
52+
public function findVisibleByOldSlugs(string $type, string $slug, string $parentSlug = ''): ?Entity
53+
{
54+
$id = $this->slugHistory->lookupEntityIdUsingSlugs($type, $slug, $parentSlug);
55+
if ($id === null) {
56+
return null;
57+
}
58+
59+
return $this->findVisibleById($type, $id);
3760
}
3861

3962
/**

app/Entities/Tools/SlugHistory.php

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@
44

55
use BookStack\Entities\Models\BookChild;
66
use BookStack\Entities\Models\Entity;
7-
use Illuminate\Support\Facades\DB;
7+
use BookStack\Entities\Models\SlugHistory as SlugHistoryModel;
8+
use BookStack\Permissions\PermissionApplicator;
89

910
class SlugHistory
1011
{
12+
public function __construct(
13+
protected PermissionApplicator $permissions,
14+
) {
15+
}
16+
1117
/**
1218
* Record the current slugs for the given entity.
1319
*/
@@ -17,31 +23,52 @@ public function recordForEntity(Entity $entity): void
1723
return;
1824
}
1925

20-
$latest = $this->getLatestEntryForEntity($entity);
21-
if ($latest && $latest->slug === $entity->slug && $latest->parent_slug === $entity->getParent()?->slug) {
22-
return;
23-
}
24-
2526
$parentSlug = null;
2627
if ($entity instanceof BookChild) {
2728
$parentSlug = $entity->book()->first()?->slug;
2829
}
2930

31+
$latest = $this->getLatestEntryForEntity($entity);
32+
if ($latest && $latest->slug === $entity->slug && $latest->parent_slug === $parentSlug) {
33+
return;
34+
}
35+
3036
$info = [
3137
'sluggable_type' => $entity->getMorphClass(),
3238
'sluggable_id' => $entity->id,
3339
'slug' => $entity->slug,
3440
'parent_slug' => $parentSlug,
35-
'created_at' => now(),
36-
'updated_at' => now(),
3741
];
3842

39-
DB::table('slug_history')->insert($info);
43+
$entry = new SlugHistoryModel();
44+
$entry->forceFill($info);
45+
$entry->save();
46+
}
47+
48+
/**
49+
* Find the latest visible entry for an entity which uses the given slug(s) in the history.
50+
*/
51+
public function lookupEntityIdUsingSlugs(string $type, string $slug, string $parentSlug = ''): ?int
52+
{
53+
$query = SlugHistoryModel::query()
54+
->where('sluggable_type', '=', $type)
55+
->where('slug', '=', $slug);
56+
57+
if ($parentSlug) {
58+
$query->where('parent_slug', '=', $parentSlug);
59+
}
60+
61+
$query = $this->permissions->restrictEntityRelationQuery($query, 'slug_history', 'sluggable_id', 'sluggable_type');
62+
63+
/** @var SlugHistoryModel|null $result */
64+
$result = $query->orderBy('created_at', 'desc')->first();
65+
66+
return $result?->sluggable_id;
4067
}
4168

42-
protected function getLatestEntryForEntity(Entity $entity): \stdClass|null
69+
protected function getLatestEntryForEntity(Entity $entity): SlugHistoryModel|null
4370
{
44-
return DB::table('slug_history')
71+
return SlugHistoryModel::query()
4572
->where('sluggable_type', '=', $entity->getMorphClass())
4673
->where('sluggable_id', '=', $entity->id)
4774
->orderBy('created_at', 'desc')

0 commit comments

Comments
 (0)