- 分類: 軟體文章
【Laravel】以 slug 取代 id,讓網址更好閱讀
以別名取代 id,讓網址更容易閱讀
以下是某個網站的網址:
https://www.example.com/blog/3
從網址可以大致知道該頁面會是網站部落格的第 3 篇文章或是 id 為 3 的文章,在未瀏覽之前其實不曉得頁面的內容。那麼如果網址換成以下格式:
https://www.example.com/blog/why-i-make-blog
這樣就能從網址知道該頁面應該是敘述為何製作部落格的文章,是不是差別很大?
我的 side project 之一是建置部落格系統,對「以別名而非 id 方式顯示文章網址」這件事特別注重,不過就本文撰寫的當下,實做上述功能的 Laravel 中文教學文章很少,會不會是因為實做這個功能實在太簡單?
我需要這個功能,然後搜尋網路文章尋找解答,透過一段時間的嘗試之後完成實做,所以覺得有必要把這個功能的實做流程紀錄下來,給自己以及其他有需要的人做個參考。
專案基本資料
- Laravel 版本: 8.x
- 運作平台:macbook air 2020(intel),macOS 11.6,Nginx + PHP v7.4 + MySQL 5.7 以 valet 運行。
準備
在資料表規劃時我設定名為「slug」的文字欄位存放內容物件(文章、分類、標籤等)的別名,以存放文章的資料表來說,migration 中的 up 方法會是以下內容:
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('category_id')->constrained();
$table->string('title');
$table->string('slug')->unique();
$table->string('cover_image');
$table->string('introtext');
$table->text('content');
$table->foreignId('user_id')->constrained();
$table->integer('sort')->default(0);
$table->enum('status',['pending', 'published', 'unpublished']);
$table->enum('featured',['yes','no']);
$table->timestamps();
});
}
如果尚未建立別名欄位則可以參考以下指令建立 mirgation:
php artisan make:migration add_slug_column_to_posts_table --table=posts
up 與 down 方法參考如下:
public function up()
{
Schema::table('posts', function (Blueprint $table) {
$table->string('slug')->nullable();
});
}
public function down()
{
Schema::table('posts', function (Blueprint $table) {
$table->dropColumn('slug');
});
}
執行 php artisan migrate
完成資料欄位加入。
下載套件
在終端機視窗下載套件
composer require cviebrock/eloquent-sluggable
在 Model 加入 slug 敘述
在需要使用別名(slug)的 Model 引用套件,以文章相關的 Model「Post」來說會是這樣子:
namespace App\\Models;
use Illuminate\\Database\\Eloquent\\Factories\\HasFactory;
use Illuminate\\Database\\Eloquent\\Model;
use TCG\\Voyager\\Traits\\Translatable;
use Cviebrock\\EloquentSluggable\\Sluggable;
class Post extends Model
{
use HasFactory, Translatable ,Sluggable;
protected $table = 'posts';
protected $fillable = [
'category_id',
'title',
'slug',
'cover_image',
'introtext',
'content',
'user_id',
'sort',
'status',
'featured',
];
public function sluggable()
{
return [
'slug' => [
'source' => 'slug'
]
];
}
public function getRouteKeyName()
{
return 'slug';
}
透過 use Cviebrock\\EloquentSluggable\\Sluggable;
加入 Sluggable,然後就可以在 Post 類別中使用了。我新增 sluggable()
方法,以資料表「slug」欄位作為 slug 來源,並建立 getRouteKeyName()
方法要求路由改成回傳 slug。
到此為止就可以利用 slug 欄位內容作為網址的一部分。
Controller、Blade 頁面與 web.php
Model 部分完成後就可以到 Contoller 撰寫顯示內容的方法,例如處理 blog 單篇文章方法如下:
public function renderBlogPage($slug){
$post = Post::where('slug', $slug)->first();
return view('blog_page', compact('post'));
}
在顯示文章內容的 Blade template(blog_page.blade.php)中以 Eloquent 語法使用資料庫資源,以帶自身連結的標題為例:
<h2 class="entry-title">
<a href="{{ $post->slug }}">{{ $post->title }}</a>
</h2>
管理網頁路由的 web.php 的語法參考:
Route::get('/blog/{slug}', 'App\\Http\\Controllers\\SiteController@renderBlogPage');
參考資料
- 3分鐘短文:Laravel slug,讓你的url地址更“好記”:https://iter01.com/538126.html
- Eloquent-Sluggable 套件:https://github.com/cviebrock/eloquent-sluggable