mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-12-13 07:42:23 +03:00
Slugs: Added lookup system using history
Switched page lookup to use this.
This commit is contained in:
@@ -17,7 +17,6 @@ use BookStack\Entities\Tools\PageContent;
|
||||
use BookStack\Entities\Tools\PageEditActivity;
|
||||
use BookStack\Entities\Tools\PageEditorData;
|
||||
use BookStack\Exceptions\NotFoundException;
|
||||
use BookStack\Exceptions\NotifyException;
|
||||
use BookStack\Exceptions\PermissionsException;
|
||||
use BookStack\Http\Controller;
|
||||
use BookStack\Permissions\Permission;
|
||||
@@ -140,9 +139,7 @@ class PageController extends Controller
|
||||
try {
|
||||
$page = $this->queries->findVisibleBySlugsOrFail($bookSlug, $pageSlug);
|
||||
} catch (NotFoundException $e) {
|
||||
$revision = $this->entityQueries->revisions->findLatestVersionBySlugs($bookSlug, $pageSlug);
|
||||
$page = $revision->page ?? null;
|
||||
|
||||
$page = $this->entityQueries->findVisibleByOldSlugs('page', $pageSlug, $bookSlug);
|
||||
if (is_null($page)) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
25
app/Entities/Models/SlugHistory.php
Normal file
25
app/Entities/Models/SlugHistory.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace BookStack\Entities\Models;
|
||||
|
||||
use BookStack\App\Model;
|
||||
use BookStack\Permissions\Models\JointPermission;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
* @property int $sluggable_id
|
||||
* @property string $sluggable_type
|
||||
* @property string $slug
|
||||
* @property ?string $parent_slug
|
||||
*/
|
||||
class SlugHistory extends Model
|
||||
{
|
||||
protected $table = 'slug_history';
|
||||
|
||||
public function jointPermissions(): HasMany
|
||||
{
|
||||
return $this->hasMany(JointPermission::class, 'entity_id', 'sluggable_id')
|
||||
->whereColumn('joint_permissions.entity_type', '=', 'slug_history.sluggable_type');
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace BookStack\Entities\Queries;
|
||||
|
||||
use BookStack\Entities\Models\Entity;
|
||||
use BookStack\Entities\Models\EntityTable;
|
||||
use BookStack\Entities\Tools\SlugHistory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
@@ -18,6 +19,7 @@ class EntityQueries
|
||||
public ChapterQueries $chapters,
|
||||
public PageQueries $pages,
|
||||
public PageRevisionQueries $revisions,
|
||||
protected SlugHistory $slugHistory,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -31,9 +33,30 @@ class EntityQueries
|
||||
$explodedId = explode(':', $identifier);
|
||||
$entityType = $explodedId[0];
|
||||
$entityId = intval($explodedId[1]);
|
||||
$queries = $this->getQueriesForType($entityType);
|
||||
|
||||
return $queries->findVisibleById($entityId);
|
||||
return $this->findVisibleById($entityType, $entityId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an entity by its ID.
|
||||
*/
|
||||
public function findVisibleById(string $type, int $id): ?Entity
|
||||
{
|
||||
$queries = $this->getQueriesForType($type);
|
||||
return $queries->findVisibleById($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an entity by looking up old slugs in the slug history.
|
||||
*/
|
||||
public function findVisibleByOldSlugs(string $type, string $slug, string $parentSlug = ''): ?Entity
|
||||
{
|
||||
$id = $this->slugHistory->lookupEntityIdUsingSlugs($type, $slug, $parentSlug);
|
||||
if ($id === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->findVisibleById($type, $id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,10 +4,16 @@ namespace BookStack\Entities\Tools;
|
||||
|
||||
use BookStack\Entities\Models\BookChild;
|
||||
use BookStack\Entities\Models\Entity;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use BookStack\Entities\Models\SlugHistory as SlugHistoryModel;
|
||||
use BookStack\Permissions\PermissionApplicator;
|
||||
|
||||
class SlugHistory
|
||||
{
|
||||
public function __construct(
|
||||
protected PermissionApplicator $permissions,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the current slugs for the given entity.
|
||||
*/
|
||||
@@ -17,31 +23,52 @@ class SlugHistory
|
||||
return;
|
||||
}
|
||||
|
||||
$latest = $this->getLatestEntryForEntity($entity);
|
||||
if ($latest && $latest->slug === $entity->slug && $latest->parent_slug === $entity->getParent()?->slug) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parentSlug = null;
|
||||
if ($entity instanceof BookChild) {
|
||||
$parentSlug = $entity->book()->first()?->slug;
|
||||
}
|
||||
|
||||
$latest = $this->getLatestEntryForEntity($entity);
|
||||
if ($latest && $latest->slug === $entity->slug && $latest->parent_slug === $parentSlug) {
|
||||
return;
|
||||
}
|
||||
|
||||
$info = [
|
||||
'sluggable_type' => $entity->getMorphClass(),
|
||||
'sluggable_id' => $entity->id,
|
||||
'slug' => $entity->slug,
|
||||
'parent_slug' => $parentSlug,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
|
||||
DB::table('slug_history')->insert($info);
|
||||
$entry = new SlugHistoryModel();
|
||||
$entry->forceFill($info);
|
||||
$entry->save();
|
||||
}
|
||||
|
||||
protected function getLatestEntryForEntity(Entity $entity): \stdClass|null
|
||||
/**
|
||||
* Find the latest visible entry for an entity which uses the given slug(s) in the history.
|
||||
*/
|
||||
public function lookupEntityIdUsingSlugs(string $type, string $slug, string $parentSlug = ''): ?int
|
||||
{
|
||||
return DB::table('slug_history')
|
||||
$query = SlugHistoryModel::query()
|
||||
->where('sluggable_type', '=', $type)
|
||||
->where('slug', '=', $slug);
|
||||
|
||||
if ($parentSlug) {
|
||||
$query->where('parent_slug', '=', $parentSlug);
|
||||
}
|
||||
|
||||
$query = $this->permissions->restrictEntityRelationQuery($query, 'slug_history', 'sluggable_id', 'sluggable_type');
|
||||
|
||||
/** @var SlugHistoryModel|null $result */
|
||||
$result = $query->orderBy('created_at', 'desc')->first();
|
||||
|
||||
return $result?->sluggable_id;
|
||||
}
|
||||
|
||||
protected function getLatestEntryForEntity(Entity $entity): SlugHistoryModel|null
|
||||
{
|
||||
return SlugHistoryModel::query()
|
||||
->where('sluggable_type', '=', $entity->getMorphClass())
|
||||
->where('sluggable_id', '=', $entity->id)
|
||||
->orderBy('created_at', 'desc')
|
||||
|
||||
Reference in New Issue
Block a user