🕳️

【Laravel】ルートパラメータを定義するときに注意すること

2022/10/18に公開約3,600字3件のコメント

この記事で出てくるルーティングとは何かについて知りたい方は以下よりどうぞ
https://zenn.dev/souhal/articles/b07adc15209ab5

Laravelのバージョン

$ php artisan -V
Laravel Framework 9.26.1

エラー

前提条件

  • 簡単なブログアプリを作っている
  • ブログアプリには投稿一覧ページ(index),投稿詳細ページ(show),投稿作成ページ(create)の3種類のページがある.
  • それぞれのページはPostControllerindexメソッド,showメソッド,createメソッドによって表示出来るように実装した(詳細は省略).


ブログアプリ

ルーティングは以下のようになっています.

/routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PostController;

Route::get('/', [PostController::class, 'index']);
Route::get('/posts/{post}', [PostController::class, 'show']);
Route::get('/posts/create', [PostController::class, 'create']);

...(以下省略)...

ルートパラメータについて

ルートパラメータとは,上のweb.php内の{post}となっている部分のことを指します.
ブログアプリの構造からわかるように,投稿が複数あり,それぞれにidがついています(図ではid=3のshowページ(/posts/3)を表示しています).
このとき,以下のようにidごとにルーティングを用意するのはナンセンスですよね.

/routes/web.php
- Route::get('/posts/{post}', [PostController::class, 'show']);
+ Route::get('/posts/1', [PostController::class, 'show1']);
+ Route::get('/posts/2', [PostController::class, 'show2']);
+ Route::get('/posts/3', [PostController::class, 'show3']);

そこで,{post}の部分に任意のパラメータを受け取り,それをpostというキーでコントローラに渡すようにすると,投稿の数がいくら増えても,showメソッドは一つで済みます.

発生したエラー

ということで,さっそくブログアプリを使ってみたいと思います.

・・・

あれ?


エラー

showページは表示されるのに,createページはNot foundのエラーが出てしまいました.

なぜでしょう?

・・・

原因

なぜこのようなエラーが出てしまったのでしょう.
indexページでcreateボタンが押されたところから処理の流れを辿っていきましょう.

  1. createボタンが押される
  2. web.php/posts/createというURLをGETメソッドで受け取る
  3. web.phpが上記のリクエストに合致するルーティングをファイルの上の行から順に探す
  4. showメソッドの行のルーティングと合致する

・・・

ん?なんで?と思いますよね.
しかし,この場合/posts/createcreateメソッドより先にshowメソッドと合致してしまいます.
これは,

です.

つまり,{post}の部分にcreateが入ってしまったということです.落とし穴みたいですね.

ルートパラメータの落とし穴

これにより,showメソッドが実行され,その時にpostというキーでcreateという文字列がコントローラに渡されてしまうため,「リソース(ページ)が見つかりません」という意味のNot foundエラーが出てしまうのですね.

対処

どうすればこのエラーを回避しつつ,ルートパラメータを利用することが出来るでしょう?

・・・

私は主に2つの対処法があると思います(他にもあるかもしれないので自身でもぜひ考えてみてください).

  1. showメソッドとcreateメソッドのweb.php内での記述順を入れ替える
  2. showメソッドのURLを変更する

それぞれ説明していきます.

1. showメソッドとcreateメソッドのweb.php内での記述順を入れ替える

web.phpを以下のように修正します.

/routes/web.php
- Route::get('/posts/{post}', [PostController::class, 'show']);
Route::get('/posts/create', [PostController::class, 'create']);
+ Route::get('/posts/{post}', [PostController::class, 'show']);

こうすることで,落とし穴にはまる前に目的地にたどり着くことが出来ます.

対処法1

ただ,ルーティングの数が増えていくとどのように順序を変えればいいかが分かりづらくなっていくという懸念点があります.

2. showメソッドのURLを変更する

web.phpを以下のように修正します.

/routes/web.php
- Route::get('/posts/{post}', [PostController::class, 'show']);
+ Route::get('/posts/{post}/show', [PostController::class, 'show']);
Route::get('/posts/create', [PostController::class, 'create']);

こうすることで,URLが別物になるので,落とし穴はなくなります.

対処法2

ただ,URLは「リソースを表す固有名詞」で構成されることが望ましい(RESTful)ので,あまりクールなURLとは言えないという懸念点があります.

まとめ

ルートパラメータの利用目的とその落とし穴について説明しました.
大切なのは

ということです.

また,対処法については,ともに懸念点が残るので,クールでスマートな対処法があれば教えてください.

では,

参考

https://readouble.com/laravel/9.x/ja/routing.html

Discussion

僕が思いついたアプローチとしては、記事内の方法に加えて{post}whereメソッドやwhereNumberメソッドを使って{post}に入る値を制限するのもありかな、と思いました!🙆‍♂️

Laravelは正規表現などでルートパラメータに制約をかけられるんですね。

その認識で合っています!🙆‍♂️
他のヘルパメソッドもありますので、調べられてみるといいかも知れません!👍

ログインするとコメントできます