##はじめに
LaravelでWebアプリケーションを開発していると、 /discussion/17
のようにIDからURLが生成されます。
しかし、これではSEOやユーザーフレンドリーの観点から適切ではない場合もあるでしょう。
そこでIDで渡されてくる箇所を、 /discussion/hello-laravel
のように文字列(ここではタイトル名のスラッグ)にする方法を紹介します。
##スラッグを使用するために
まず、URLにスラッグを使用するためには、 URLを生成する箇所
と URLを解釈する箇所
の2つを修正する必要があります。
##URLを生成する箇所
コントローラー内のDBに保存する箇所で新たに slug
を追加します。このslugがURLのIDの代わりになる部分です。
public function store(Request $request)
{
App\Discussion::create([
'title' => $request->title,
'slug' => str_slug($request->title),
'content' => $request->content,
]);
}
DBに保存するためには、マイグレーションファイルを修正して新たにカラムを追加する必要があります。
URLが重複しないように unique()
制約をつけます。
public function up()
{
Schema::create('discussions', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('title');
$table->string('slug')->unique();
$table->text('content');
$table->timestamps();
});
}
##URLを解釈する箇所
まずLaravelではどのようにURLを解釈しているのかというと、Laravelではインプリシットバインディングという機能があり、タイプヒントされた変数名とルートセグメント名が一致する場合、ルートまたはコントローラーアクションで定義されたEloquentモデルを自動的に解決してくれます。
例えば以下の場合だと、
Route::get('discussions/{discussion}', function (App\Discussion $discussion) {
dd($discussion->toArray());
});
$discussion
はApp\Discussion Eloquentモデルとしてタイプヒントされて、$discussion
とURIの一部である{discussion}
が一致するため、リクエストURIの対応する値と一致するIDを持つモデルインスタンスを自動的に挿入してくれます。
そのため、http://localhost/discussions/1
でアクセスした際に、IDが1のDiscussionインスタンスが自動的に挿入されます。
ここでは、IDが1の箇所をタイトル名のスラッグに変更したいので、Discussionモデル内でgetRouteKeyName()
を使用して、変更したいカラム名でオーバーライドしてあげます。
public function getRouteKeyName()
{
return 'slug';
}
これで、以下のレコードが生成されている場合、slugを利用したURLでアクセスすることができるようになります。
id | title | slug | content | created_at | updated_at |
---|---|---|---|---|---|
1 | Hello Laravel | hello-laravel | I Love Laravel | 2020-01-01 00:00:00 | 2020-01-01 00:00:00 |
http://localhost/discussions/hello-laravel