diff --git a/app/Blog/Controllers/ArchiveController.php b/app/Blog/Controllers/ArchiveController.php index b988edb..c4f3885 100644 --- a/app/Blog/Controllers/ArchiveController.php +++ b/app/Blog/Controllers/ArchiveController.php @@ -20,8 +20,17 @@ class ArchiveController extends Controller return theme_view('archives', compact('archives')); } - public function month(string $year, string $month) + /** + * sablog 格式:/archives-{YYYYMM}.shtml + */ + public function month(string $date) { + if (! preg_match('/^(\d{4})(\d{2})$/', $date, $m)) { + abort(404); + } + + [$year, $month] = [$m[1], $m[2]]; + $start = Carbon::create((int) $year, (int) $month, 1)->startOfMonth(); $end = (clone $start)->endOfMonth(); diff --git a/app/Blog/Controllers/LegacyController.php b/app/Blog/Controllers/LegacyController.php index 8335849..50cbbf7 100644 --- a/app/Blog/Controllers/LegacyController.php +++ b/app/Blog/Controllers/LegacyController.php @@ -78,7 +78,7 @@ class LegacyController extends Controller $tag = Tag::findFromString($name, 'post'); return $tag - ? redirect()->route('tags.show', $tag->slug ?: $tag->name, 301) + ? redirect()->route('tags.show', $tag->name, 301) : abort(404); } @@ -152,7 +152,7 @@ class LegacyController extends Controller app(\App\Blog\Support\PluginManager::class)->doAction('comment.created', $comment); - return redirect()->route('posts.show', $post->slug ?: $post->id) + return redirect()->route('posts.show', $post->id) ->with($status === 'published' ? 'success' : 'error', $status === 'published' ? '评论已发布' : '评论已提交,等待审核') ->withFragment('comments'); } @@ -162,7 +162,7 @@ class LegacyController extends Controller $post = Post::find($id); return $post - ? redirect()->route('posts.show', $post->slug ?: $post->id, 301) + ? redirect()->route('posts.show', $post->id, 301) : abort(404); } @@ -171,7 +171,7 @@ class LegacyController extends Controller $category = Category::find($cid); return $category - ? redirect()->route('category.show', $category->slug ?: $category->id, 301) + ? redirect()->route('category.show', $category->id, 301) : abort(404); } @@ -185,8 +185,8 @@ class LegacyController extends Controller $month = $m[2] ?? null; return $month - ? redirect()->route('archives.month', [$year, $month], 301) - : redirect()->route('archives.month', [$year, '01'], 301); + ? redirect()->route('archives.month', $year.$month, 301) + : redirect()->route('archives.month', $year.'01', 301); } private function tagList(Request $request) @@ -198,7 +198,7 @@ class LegacyController extends Controller $tag = $tagId ? Tag::find((int) $tagId) : Tag::find((int) $request->query('id')); return $tag - ? redirect()->route('tags.show', $tag->slug ?: $tag->name, 301) + ? redirect()->route('tags.show', $tag->name, 301) : abort(404); } diff --git a/app/Blog/Controllers/PostController.php b/app/Blog/Controllers/PostController.php index e1af2df..b1aa016 100644 --- a/app/Blog/Controllers/PostController.php +++ b/app/Blog/Controllers/PostController.php @@ -14,17 +14,22 @@ class PostController extends Controller { } - public function show(string $slug) + public function show(string $id, ?string $page = null) { $post = Post::query() - ->where('slug', $slug) - ->orWhere('id', (int) $slug) + ->where('id', (int) $id) + ->orWhere('slug', $id) ->first(); abort_unless($post && $post->status === 'published', 404); $this->ensureReadable($post); + // sablog 式 /show-{id}-{page}.shtml 的评论分页 + if ($page !== null) { + \Illuminate\Pagination\Paginator::currentPageResolver(fn () => max(1, (int) $page)); + } + // 浏览量由 /track-view 端点统计(页面整页缓存下控制器不执行,避免双计) $comments = $post->comments() diff --git a/app/Blog/Controllers/TagController.php b/app/Blog/Controllers/TagController.php index b29c374..28073d6 100644 --- a/app/Blog/Controllers/TagController.php +++ b/app/Blog/Controllers/TagController.php @@ -28,9 +28,12 @@ class TagController extends Controller return theme_view('tags', compact('tags')); } - public function show(string $slug) + /** + * sablog 格式:/tag/{name},按名称(而非 slug)解析 + */ + public function show(string $name) { - $tag = Tag::findFromString($slug, 'post'); + $tag = Tag::findFromString(urldecode($name), 'post'); abort_unless($tag, 404); diff --git a/app/Filament/Resources/Comments/Tables/CommentsTable.php b/app/Filament/Resources/Comments/Tables/CommentsTable.php index 99cbff6..65a9f82 100644 --- a/app/Filament/Resources/Comments/Tables/CommentsTable.php +++ b/app/Filament/Resources/Comments/Tables/CommentsTable.php @@ -32,7 +32,7 @@ class CommentsTable ->label('文章') ->limit(30) ->description(fn ($record) => $record->post ? \Illuminate\Support\Str::limit($record->post->excerpt_or_fallback, 50) : null) - ->url(fn ($record) => $record->post ? route('posts.show', $record->post->slug ?: $record->post->id) : null) + ->url(fn ($record) => $record->post ? route('posts.show', $record->post->id) : null) ->openUrlInNewTab(), TextColumn::make('status') ->label('状态') diff --git a/app/Http/Controllers/Api/ApiController.php b/app/Http/Controllers/Api/ApiController.php index 2df9716..17fa675 100644 --- a/app/Http/Controllers/Api/ApiController.php +++ b/app/Http/Controllers/Api/ApiController.php @@ -110,7 +110,7 @@ class ApiController extends Controller 'views' => $post->views, 'comment_count' => $post->comment_count, 'published_at' => $post->published_at?->toIso8601String(), - 'url' => route('posts.show', $post->slug ?: $post->id), + 'url' => route('posts.show', $post->id), ]); } @@ -237,7 +237,7 @@ class ApiController extends Controller 'views' => $post->views, 'comment_count' => $post->comment_count, 'published_at' => $post->published_at?->toIso8601String(), - 'url' => route('posts.show', $post->slug ?: $post->id), + 'url' => route('posts.show', $post->id), ]; } } diff --git a/app/Models/Category.php b/app/Models/Category.php index 60db3ef..c67af9e 100644 --- a/app/Models/Category.php +++ b/app/Models/Category.php @@ -30,7 +30,7 @@ class Category extends Model implements HasMedia public function getUrlAttribute(): string { - return route('category.show', $this->slug ?? $this->id); + return route('category.show', $this->id); } public function registerMediaCollections(): void diff --git a/app/Models/Post.php b/app/Models/Post.php index bc9cee7..e9ad1d8 100644 --- a/app/Models/Post.php +++ b/app/Models/Post.php @@ -104,7 +104,7 @@ class Post extends Model implements HasMedia public function getUrlAttribute(): string { - return route('posts.show', $this->slug ?? $this->id); + return route('posts.show', $this->id); } /** diff --git a/plugins/neatstudio.blog-seo/src/Http/Middleware/SeoMiddleware.php b/plugins/neatstudio.blog-seo/src/Http/Middleware/SeoMiddleware.php index 68d1809..df456be 100644 --- a/plugins/neatstudio.blog-seo/src/Http/Middleware/SeoMiddleware.php +++ b/plugins/neatstudio.blog-seo/src/Http/Middleware/SeoMiddleware.php @@ -75,13 +75,13 @@ class SeoMiddleware '@context' => 'https://schema.org', '@type' => 'BlogPosting', 'headline' => $post->title, - 'url' => route('posts.show', $post->slug ?: $post->id), + 'url' => route('posts.show', $post->id), 'datePublished' => $post->published_at?->toIso8601String(), 'dateModified' => $post->updated_at?->toIso8601String(), 'author' => ['@type' => 'Person', 'name' => $post->author?->name ?? $siteName] + ($sameAs ? ['sameAs' => $sameAs] : []), 'publisher' => ['@type' => 'Organization', 'name' => $siteName, 'url' => url('/')] + ($sameAs ? ['sameAs' => $sameAs] : []), 'description' => Str::limit(strip_tags($post->excerpt_or_fallback), 200), - 'mainEntityOfPage' => route('posts.show', $post->slug ?: $post->id), + 'mainEntityOfPage' => route('posts.show', $post->id), ]; if ($cover = $post->getFirstMedia('cover')) { @@ -105,7 +105,7 @@ class SeoMiddleware '@type' => 'BreadcrumbList', 'itemListElement' => [ ['@type' => 'ListItem', 'position' => 1, 'name' => $siteName, 'item' => url('/')], - ['@type' => 'ListItem', 'position' => 2, 'name' => $category->name, 'item' => route('category.show', $category->slug ?: $category->id)], + ['@type' => 'ListItem', 'position' => 2, 'name' => $category->name, 'item' => route('category.show', $category->id)], ], ]; } diff --git a/plugins/neatstudio.blog-seo/src/Http/SeoController.php b/plugins/neatstudio.blog-seo/src/Http/SeoController.php index dba9731..27c90af 100644 --- a/plugins/neatstudio.blog-seo/src/Http/SeoController.php +++ b/plugins/neatstudio.blog-seo/src/Http/SeoController.php @@ -30,7 +30,7 @@ class SeoController $lines[] = ''; foreach ($posts as $post) { - $lines[] = '- ['.$post->title.']('.route('posts.show', $post->slug ?: $post->id).') — '.Str::limit(strip_tags($post->excerpt_or_fallback), 120); + $lines[] = '- ['.$post->title.']('.route('posts.show', $post->id).') — '.Str::limit(strip_tags($post->excerpt_or_fallback), 120); } return response(implode("\n", $lines)."\n") diff --git a/plugins/neatstudio.membership/src/Http/MembershipController.php b/plugins/neatstudio.membership/src/Http/MembershipController.php index 460f85b..ca87080 100644 --- a/plugins/neatstudio.membership/src/Http/MembershipController.php +++ b/plugins/neatstudio.membership/src/Http/MembershipController.php @@ -81,7 +81,7 @@ class MembershipController // 已能读付费内容(会员 / 作者 / 已解锁):直接查看文章,避免重复支付。 // 注意:付费块文章的整篇是公开的,不能用 canReadPost 判断(否则永远"已有权限") if ($this->service->canReadPaidContent($user, $post)) { - return redirect()->route('posts.show', $post->slug ?? $post->id); + return redirect()->route('posts.show', $post->id); } // 单篇付费解锁:创建一笔定向支付 diff --git a/resources/views/archives.blade.php b/resources/views/archives.blade.php index ce0e0b6..ad89bb1 100644 --- a/resources/views/archives.blade.php +++ b/resources/views/archives.blade.php @@ -11,11 +11,11 @@