ルーティング
URL とそれを受け持つ処理を結びつけるルーティングの書き方を説明します。パラメータ・名前・グループ・モデルの自動取得・回数の制限まで引けます。
ルーティングは、「この URL に来たら、この処理を動かす」と決めることです。お店でいえば、お客さんの用件を聞いて担当の窓口へ案内する受付係の仕事にあたります。Laravel のルート(URL と処理の組)は、いちばん簡単な形だと、URL とクロージャ(名前のない関数)を書くだけで作れます。
いちばん基本のルート#
use Illuminate\Support\Facades\Route;
Route::get('/greeting', function () {
return 'Hello World';
});
/greeting を開くと、Hello World と表示されます。
ルートを書くファイル#
ルートは routes フォルダの中のファイルに書きます。bootstrap/app.php の設定で、Laravel が自動で読み込みます。画面用のルートは routes/web.php に書きます。ここに書いたルートには web というミドルウェアのグループ(リクエストの前に間に入る処理のまとまり)がかかります。そのおかげで、セッション(同じ人のアクセスをまたいで情報を覚えておくしくみ)や CSRF 対策(なりすましの送信を防ぐしくみ)が使えます。
はじめは routes/web.php に書けば足ります。たとえば、次のルートは http://example.com/user をブラウザで開くと動きます。
use App\Http\Controllers\UserController;
Route::get('/user', [UserController::class, 'index']);
API 用のルート#
ログイン状態を持たない API(ほかのプログラムからデータをやり取りする窓口)も作るなら、次の Artisan コマンド(php artisan で動かす Laravel のコマンド)で、API 用のルートを使えるようにします。
php artisan install:api
このコマンドは Laravel Sanctum(API の利用者を確かめる道具)を入れ、routes/api.php を作ります。
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('auth:sanctum');
だれでも使ってよいルートなら、auth:sanctum は付けなくてかまいません。
routes/api.php のルートは、api ミドルウェアグループがかかり、ログイン状態を覚えません。URL の頭には自動で /api が付くので、1本ずつ書く必要はありません。この頭の部分は bootstrap/app.php で変えられます。
->withRouting(
api: __DIR__.'/../routes/api.php',
apiPrefix: 'api/admin',
// ...
)
使えるルーターのメソッド#
どの HTTP メソッド(リクエストの種類)にも対応するルートを書けます。
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);
| メソッド | 説明 |
|---|---|
Route::get |
GET(ページを見る)に答える |
Route::post |
POST(データを送る)に答える |
Route::put |
PUT(まるごと更新する)に答える |
Route::patch |
PATCH(一部を更新する)に答える |
Route::delete |
DELETE(消す)に答える |
Route::options |
OPTIONS(使える種類をたずねる)に答える |
Route::match |
指定した複数のメソッドに答える |
Route::any |
すべてのメソッドに答える |
複数のメソッドに答えたいときは match、全部に答えたいときは any を使います。
Route::match(['get', 'post'], '/', function () {
// ...
});
Route::any('/', function () {
// ...
});
補足
同じ URL のルートを複数書くときは、get・post・put・patch・delete・options を先に、any・match・redirect を後に書きます。そうしないと、リクエストが正しいルートに結びつきません。
必要なものを受け取る(依存性の注入)#
ルートの関数の引数に型を書くと、サービスコンテナ(クラスを作って渡してくれる道具箱)が、必要なものを自動で渡してくれます。これを依存性の注入といいます。たとえば Request を書けば、いまのリクエストが渡されます。
use Illuminate\Http\Request;
Route::get('/users', function (Request $request) {
// ...
});
CSRF 対策#
web のルートで POST・PUT・PATCH・DELETE に送るフォームには、CSRF トークン(なりすましを防ぐ合言葉)の欄が要ります。ないと、リクエストは受け付けられません。くわしくは CSRF 対策を見てください。
<form method="POST" action="/profile">
@csrf
...
</form>
転送するルート(リダイレクト)#
別の URL へ転送するだけなら、Route::redirect が近道です。コントローラーを作らなくて済みます。
Route::redirect('/here', '/there');
ふつうは 302(一時的な転送)を返します。3つ目の引数で変えられます。
Route::redirect('/here', '/there', 301);
301(ずっとそこへ移った、という転送)なら Route::permanentRedirect でも書けます。
Route::permanentRedirect('/here', '/there');
注意
転送のルートでルートパラメータ(URL の一部を受け取るしくみ)を使うときは、destination と status は Laravel が使うので、名前にできません。
ビューを返すだけのルート#
ビュー(画面の見た目を書いたファイル)を返すだけなら、Route::view が使えます。1つ目が URL、2つ目がビューの名前、3つ目(省略できます)がビューへ渡すデータです。
Route::view('/welcome', 'welcome');
Route::view('/welcome', 'welcome', ['name' => 'Taylor']);
注意
ビューのルートでルートパラメータを使うときは、view・data・status・headers は名前にできません。
ルートの一覧を見る#
route:list コマンドで、アプリにあるルートを一覧で見られます。
php artisan route:list
ふつうはミドルウェアが出ません。-v を付けると、ミドルウェアとグループの名前が出ます。
php artisan route:list -v
# ミドルウェアのグループを広げて見る
php artisan route:list -vv
| オプション | 説明 |
|---|---|
-v |
ルートにかかるミドルウェアとグループの名前も出す |
-vv |
グループを広げて、中のミドルウェアまで出す |
--path=api |
URL に指定の文字を含むルートだけ出す |
--except-vendor |
パッケージが作ったルートを隠す |
--only-vendor |
パッケージが作ったルートだけ出す |
php artisan route:list --path=api
php artisan route:list --except-vendor
php artisan route:list --only-vendor
ルートの読み込み方を変える#
ルートは、ふつう bootstrap/app.php が読み込みます。
<?php
use Illuminate\Foundation\Application;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)->create();
ルートの一部を別のファイルに分けたいときは、withRouting に then のクロージャを渡して、足したいルートを登録します。
use Illuminate\Support\Facades\Route;
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
then: function () {
Route::middleware('api')
->prefix('webhooks')
->name('webhooks.')
->group(base_path('routes/webhooks.php'));
},
)
ルートの登録を全部自分でしたいときは、using のクロージャを渡します。すると Laravel は HTTP のルートを1つも登録しないので、すべて自分で書きます。
use Illuminate\Support\Facades\Route;
->withRouting(
commands: __DIR__.'/../routes/console.php',
using: function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
},
)
ルートパラメータ(URL の一部を受け取る)#
必ず要るパラメータ#
URL の一部を取り出して使いたいときは、{} で囲んだルートパラメータを書きます。たとえば、URL の中のユーザー番号を受け取れます。
Route::get('/user/{id}', function (string $id) {
return 'User '.$id;
});
いくつでも書けます。
Route::get('/posts/{post}/comments/{comment}', function (string $postId, string $commentId) {
// ...
});
パラメータの名前は、英字と _ で作ります。関数の引数には、名前ではなく並んだ順で渡されます。なので、引数の名前は何でもかまいません。
パラメータと依存性の注入#
道具箱から渡してほしいものがあるときは、それを先に書き、ルートパラメータを後ろに並べます。
use Illuminate\Http\Request;
Route::get('/user/{id}', function (Request $request, string $id) {
return 'User '.$id;
});
あってもなくてもよいパラメータ#
名前のあとに ? を付けると、URL にその部分がなくてもよくなります。そのとき、関数の引数には初期値を書きます。
Route::get('/user/{name?}', function (?string $name = null) {
return $name;
});
Route::get('/user/{name?}', function (?string $name = 'John') {
return $name;
});
正規表現で形を決める#
where メソッドで、パラメータの形を正規表現(文字の並びのパターンを書く決まり)で縛れます。
Route::get('/user/{name}', function (string $name) {
// ...
})->where('name', '[A-Za-z]+');
Route::get('/user/{id}', function (string $id) {
// ...
})->where('id', '[0-9]+');
Route::get('/user/{id}/{name}', function (string $id, string $name) {
// ...
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
よく使う形には、近道のメソッドがあります。
| メソッド | 説明 |
|---|---|
whereNumber |
数字だけ |
whereAlpha |
英字だけ |
whereAlphaNumeric |
英字と数字だけ |
whereUuid |
UUID の形だけ |
whereUlid |
ULID の形だけ |
whereIn |
決めた値のどれかだけ |
Route::get('/user/{id}/{name}', function (string $id, string $name) {
// ...
})->whereNumber('id')->whereAlpha('name');
Route::get('/user/{name}', function (string $name) {
// ...
})->whereAlphaNumeric('name');
Route::get('/user/{id}', function (string $id) {
// ...
})->whereUuid('id');
Route::get('/user/{id}', function (string $id) {
// ...
})->whereUlid('id');
Route::get('/category/{category}', function (string $category) {
// ...
})->whereIn('category', ['movie', 'song', 'painting']);
Route::get('/category/{category}', function (string $category) {
// ...
})->whereIn('category', CategoryEnum::cases());
形に合わない URL が来たときは、404(ページが見つからない)が返ります。
どのルートにも同じ決まりを付ける#
同じ名前のパラメータを、いつも同じ形に縛りたいときは pattern を使います。App\Providers\AppServiceProvider(アプリの起動のときに設定を書くクラス)の boot メソッドに書きます。
use Illuminate\Support\Facades\Route;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Route::pattern('id', '[0-9]+');
}
こうすると、{id} という名前のパラメータを持つルートすべてに、この決まりがかかります。
Route::get('/user/{id}', function (string $id) {
// {id} が数字のときだけ動く
});
スラッシュを含む値#
ルートパラメータの値には、/ 以外のどんな文字も使えます。/ を含めたいときは、where で正規表現を書いて許します。
Route::get('/search/{search}', function (string $search) {
return $search;
})->where('search', '.*');
注意
エンコードした /(URL の中で %2F と書きかえた /)を含められるのは、ルートのいちばん最後の部分だけです。
名前を付けたルート#
ルートに名前を付けておくと、URL や転送先を、名前から作れます。name メソッドをつなぎます。
Route::get('/user/profile', function () {
// ...
})->name('profile');
コントローラーのルートにも付けられます。
Route::get(
'/user/profile',
[UserProfileController::class, 'show']
)->name('profile');
注意
ルートの名前は、ほかと重ならないようにします。
名前から URL を作る#
名前を付けたら、route と redirect のヘルパー関数で、名前から URL や転送を作れます。URL を直接書かないので、あとで URL を変えても直す場所が少なく済みます。
// URL を作る
$url = route('profile');
// 転送を作る
return redirect()->route('profile');
return to_route('profile');
ルートにパラメータがあるときは、route の2つ目の引数に配列で渡すと、正しい位置に入ります。
Route::get('/user/{id}/profile', function (string $id) {
// ...
})->name('profile');
$url = route('profile', ['id' => 1]);
配列に余分な値を入れると、クエリ文字列(URL の ? 以降)として付きます。
Route::get('/user/{id}/profile', function (string $id) {
// ...
})->name('profile');
$url = route('profile', ['id' => 1, 'photos' => 'yes']);
// http://example.com/user/1/profile?photos=yes
補足
言語の設定のように、リクエスト全体で使う URL パラメータの初期値を決めたいときは、URL::defaults メソッドを使います。くわしくは URL を作るを見てください。
いまのルートを調べる#
いまのリクエストが、ある名前のルートに来たものか調べるには、ルートの named メソッドを使います。たとえば、ミドルウェアの中で調べられます。
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
if ($request->route()->named('profile')) {
// ...
}
return $next($request);
}
ルートグループ#
ルートグループを使うと、たくさんのルートに同じ設定(ミドルウェアなど)を、1つずつ書かずにまとめて付けられます。
グループの中にグループを入れると、設定は外側と内側が合わさります。ミドルウェアと where の条件は足し合わされ、名前と URL の頭は後ろへつながります。名前空間(クラスの置き場所を表す名前)の区切りや、URL のスラッシュは、必要なところに自動で入ります。
ミドルウェアをまとめて付ける#
グループの前で middleware メソッドを呼びます。ミドルウェアは、配列に書いた順に動きます。
Route::middleware(['first', 'second'])->group(function () {
Route::get('/', function () {
// first と second のミドルウェアが使われる
});
Route::get('/user/profile', function () {
// first と second のミドルウェアが使われる
});
});
コントローラーをまとめる#
同じコントローラーを使うルートが並ぶなら、controller メソッドでまとめます。各ルートには、動かすメソッドの名前だけを書きます。
use App\Http\Controllers\OrderController;
Route::controller(OrderController::class)->group(function () {
Route::get('/orders/{id}', 'show');
Route::post('/orders', 'store');
});
サブドメインで分ける#
サブドメイン(example.com の前に付く名前。shop.example.com の shop)ごとにルートを分けられます。サブドメインにもルートパラメータが使え、その部分を取り出せます。グループの前で domain メソッドを呼びます。
Route::domain('{account}.example.com')->group(function () {
Route::get('/user/{id}', function (string $account, string $id) {
// ...
});
});
URL の頭をそろえる#
prefix メソッドで、グループ内のすべてのルートの URL の頭に、同じ文字を付けられます。
Route::prefix('admin')->group(function () {
Route::get('/users', function () {
// "/admin/users" の URL に当たる
});
});
ルートの名前の頭をそろえる#
name メソッドで、グループ内のすべてのルートの名前の頭に、同じ文字を付けられます。書いた文字がそのまま頭に付くので、最後の . も自分で書きます。
Route::name('admin.')->group(function () {
Route::get('/users', function () {
// このルートの名前は "admin.users" になる
})->name('users');
});
ルートモデルバインディング#
ルートに ID が来ると、その ID のデータをデータベースから取り出す処理をよく書きます。ルートモデルバインディングを使うと、ID ではなく、その ID に当たるモデル(データベースの表を PHP から扱いやすくしたクラス)を、そのまま受け取れます。
自動で結びつける(暗黙の結合)#
関数の引数に Eloquent(Laravel のモデルのしくみ)のモデルの型を書き、変数の名前をルートパラメータの名前に合わせます。すると、Laravel が自動でそのモデルを探して渡します。
use App\Models\User;
Route::get('/users/{user}', function (User $user) {
return $user->email;
});
$user に App\Models\User の型が付き、名前が {user} と同じなので、URL の値と同じ ID を持つユーザーが渡されます。見つからなければ、自動で 404 が返ります。
コントローラーのメソッドでも同じです。
use App\Http\Controllers\UserController;
use App\Models\User;
// ルートの書き方
Route::get('/users/{user}', [UserController::class, 'show']);
// コントローラーのメソッドの書き方
public function show(User $user)
{
return view('user.profile', ['user' => $user]);
}
論理削除したモデル#
論理削除(消したことにして、データは残す方法)したモデルは、ふつう取り出されません。取り出したいときは、ルートに withTrashed をつなぎます。
use App\Models\User;
Route::get('/users/{user}', function (User $user) {
return $user->email;
})->withTrashed();
id 以外の列で探す#
id 以外の列で探したいときは、パラメータの定義に列の名前を書きます。
use App\Models\Post;
Route::get('/posts/{post:slug}', function (Post $post) {
return $post;
});
そのモデルをいつも別の列で探したいときは、モデルに RouteKey の属性(PHP のクラスの前に書く印)を付けます。
use Illuminate\Database\Eloquent\Attributes\RouteKey;
use Illuminate\Database\Eloquent\Model;
#[RouteKey('slug')]
class Post extends Model
{
// ...
}
親の中から探す(スコープ)#
1つのルートに複数のモデルがあるとき、2つ目を「1つ目の子」に限りたいことがあります。たとえば、あるユーザーの投稿を、スラッグ(URL に使う名前)で探すルートです。
use App\Models\Post;
use App\Models\User;
Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
return $post;
});
入れ子(親の中に子がある形)のパラメータに id 以外の列を指定すると、Laravel は親の中から子を探します。このとき、親のモデルに、パラメータ名の複数形のリレーション(表どうしのつながり)がある、と見なします。この例なら、User に posts というリレーションがあるはずです。
列を指定しなくても、子を親の中から探したいときは、scopeBindings を呼びます。
use App\Models\Post;
use App\Models\User;
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
return $post;
})->scopeBindings();
グループ全体に付けることもできます。
Route::scopeBindings()->group(function () {
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
return $post;
});
});
逆に、親の中から探さないと決めるには withoutScopedBindings を呼びます。
Route::get('/users/{user}/posts/{post:slug}', function (User $user, Post $post) {
return $post;
})->withoutScopedBindings();
見つからないときの動きを変える#
見つからないときは、ふつう 404 になります。missing メソッドにクロージャを渡すと、その場合の動きを変えられます。
use App\Http\Controllers\LocationsController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
Route::get('/locations/{location:slug}', [LocationsController::class, 'show'])
->name('locations.view')
->missing(function (Request $request) {
return Redirect::route('locations.index');
});
Enum で結びつける#
PHP 8.1 から Enum(決めた値だけを持てるクラス)が使えます。文字を値に持つ Enum を型に書くと、URL の値がその Enum の値のどれかのときだけ、ルートが動きます。ちがえば自動で 404 です。
<?php
namespace App\Enums;
enum Category: string
{
case Fruits = 'fruits';
case People = 'people';
}
次のルートは、{category} が fruits か people のときだけ動きます。
use App\Enums\Category;
use Illuminate\Support\Facades\Route;
Route::get('/categories/{category}', function (Category $category) {
return $category->value;
});
自分で結びつけを決める(明示的な結合)#
名前の決まりに頼らず、パラメータとモデルの組み合わせを自分で決めることもできます。AppServiceProvider の boot メソッドの最初に、ルーターの model メソッドを書きます。
use App\Models\User;
use Illuminate\Support\Facades\Route;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Route::model('user', User::class);
}
あとは {user} を持つルートを書きます。
use App\Models\User;
Route::get('/users/{user}', function (User $user) {
// ...
});
{user} はすべて App\Models\User に結びついたので、users/1 へのリクエストには、ID が 1 のユーザーが渡されます。見つからなければ、自動で 404 が返ります。
探し方を自分で書く#
モデルの探し方そのものを変えたいときは、Route::bind を使います。クロージャは URL の値を受け取り、渡したいモデルを返します。これも AppServiceProvider の boot メソッドに書きます。
use App\Models\User;
use Illuminate\Support\Facades\Route;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Route::bind('user', function (string $value) {
return User::where('name', $value)->firstOrFail();
});
}
別の方法として、Eloquent モデルの resolveRouteBinding メソッドを上書きできます。URL の値を受け取り、渡したいモデルを返します。
/**
* Retrieve the model for a bound value.
*
* @param mixed $value
* @param string|null $field
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value, $field = null)
{
return $this->where('name', $value)->firstOrFail();
}
親の中から子を探す(スコープ)ルートでは、子を探すのに resolveChildRouteBinding メソッドが使われます。
/**
* Retrieve the child model for a bound value.
*
* @param string $childType
* @param mixed $value
* @param string|null $field
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveChildRouteBinding($childType, $value, $field)
{
return parent::resolveChildRouteBinding($childType, $value, $field);
}
どれにも当たらなかったとき(フォールバック)#
Route::fallback で、ほかのどのルートにも当たらなかったときに動くルートを決められます。ふつう、どのルートにも当たらなかったリクエストには、エラーを扱う処理が自動で「404」のページを返します。ただ、fallback はふつう routes/web.php に書くので、web ミドルウェアグループの処理もすべてかかります。必要ならミドルウェアも足せます。
Route::fallback(function () {
// ...
});
回数を制限する(レート制限)#
制限のルールを作る#
Laravel には、ルート(またはルートのグループ)へのアクセスの数を制限するしくみがあります。まず、制限のルールを作ります。
ルールは App\Providers\AppServiceProvider の boot メソッドに書きます。
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
}
RateLimiter ファサード(クラス名と :: で呼べる窓口)の for メソッドに、ルールの名前と、制限を返すクロージャを渡します。制限は Illuminate\Cache\RateLimiting\Limit クラスで表します。このクラスには、制限を組み立てるためのメソッドがそろっています。ルールの名前は好きな文字でかまいません。
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
RateLimiter::for('global', function (Request $request) {
return Limit::perMinute(1000);
});
}
制限を超えると、Laravel は自動で 429 のエラーを返します。返す内容を自分で決めたいときは response メソッドを使います。
RateLimiter::for('global', function (Request $request) {
return Limit::perMinute(1000)->response(function (Request $request, array $headers) {
return response('Custom response...', 429, $headers);
});
});
クロージャはリクエストを受け取るので、リクエストやログインしている人によって、制限を変えられます。
RateLimiter::for('uploads', function (Request $request) {
return $request->user()?->vipCustomer()
? Limit::none()
: Limit::perHour(10);
});
人ごとに制限を分ける#
by メソッドを使うと、好きな値ごとに別々に数えられます。たとえば、IP アドレス(ネットワーク上の住所のようなもの)ごとに、1分に100回までにできます。
RateLimiter::for('uploads', function (Request $request) {
return $request->user()->vipCustomer()
? Limit::none()
: Limit::perMinute(100)->by($request->ip());
});
別の例です。ログインしている人は、ユーザー ID ごとに1分に100回まで、ログインしていない人は、IP アドレスごとに1分に10回までにします。
RateLimiter::for('uploads', function (Request $request) {
return $request->user()
? Limit::perMinute(100)->by($request->user()->id)
: Limit::perMinute(10)->by($request->ip());
});
制限を2つ以上かける#
制限の配列を返すと、並んだ順に、1つずつ調べられます。
RateLimiter::for('login', function (Request $request) {
return [
Limit::perMinute(500),
Limit::perMinute(3)->by($request->input('email')),
];
});
同じ値で分けた制限を複数置くときは、by の値がそれぞれ別になるようにします。値の頭に印を付けるのが簡単です。
RateLimiter::for('uploads', function (Request $request) {
return [
Limit::perMinute(10)->by('minute:'.$request->user()->id),
Limit::perDay(1000)->by('day:'.$request->user()->id),
];
});
返した結果で数える#
after メソッドを使うと、リクエストではなく、返したレスポンスを見て、数えるかどうかを決められます。入力のエラーや 404 など、特定の結果だけ数えたいときに便利です。
after には、レスポンスを受け取るクロージャを渡します。数えるなら true、数えないなら false を返します。たとえば、404 が続く回数を制限すれば、ID を片っぱしから試して探られるのを防げます。また、入力のチェックで止められてやり直した分は数えず、成功した操作だけを制限することもできます。
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Symfony\Component\HttpFoundation\Response;
RateLimiter::for('resource-not-found', function (Request $request) {
return Limit::perMinute(10)
->by($request->user()?->id ?: $request->ip())
->after(function (Response $response) {
// 404 のレスポンスだけを数える(IDの総当たりを防ぐため)
return $response->status() === 404;
});
});
Limit で使うメソッドを、ここで出てきたものだけまとめます。
| メソッド | 説明 |
|---|---|
Limit::perMinute |
1分あたりの回数で制限する |
Limit::perHour |
1時間あたりの回数で制限する |
Limit::perDay |
1日あたりの回数で制限する |
Limit::none |
制限しない |
by |
数える単位(人・IP など)を決める |
response |
超えたときに返す内容を決める |
after |
返したレスポンスを見て、数えるか決める |
ルートに制限を付ける#
ルールは、throttle ミドルウェアで、ルートやグループに付けます。throttle: の後ろにルールの名前を書きます。
Route::middleware(['throttle:uploads'])->group(function () {
Route::post('/audio', function () {
// ...
});
Route::post('/video', function () {
// ...
});
});
Redis で数える#
throttle ミドルウェアの中身は、ふつう Illuminate\Routing\Middleware\ThrottleRequests クラスです。キャッシュ(一度作った結果を取っておくしくみ)に Redis(高速なデータの保存先)を使っているなら、回数を数えるのも Redis に任せられます。bootstrap/app.php で throttleWithRedis メソッドを呼ぶと、throttle が Illuminate\Routing\Middleware\ThrottleRequestsWithRedis になります。
->withMiddleware(function (Middleware $middleware): void {
$middleware->throttleWithRedis();
// ...
})
フォームで PUT・PATCH・DELETE を使う#
HTML のフォームは、PUT・PATCH・DELETE で送れません。そのため、フォームから呼ぶこれらのルートには、_method という隠し入力欄を足します。この欄の値が、HTTP メソッドとして使われます。
<form action="/example" method="POST">
<input type="hidden" name="_method" value="PUT">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>
Blade の @method ディレクティブ(@ で始まる命令)を使うと、_method の欄を作れます。
<form action="/example" method="POST">
@method('PUT')
@csrf
</form>
いまのルートの情報を取り出す#
Route ファサードの current・currentRouteName・currentRouteAction で、いまのリクエストを受け持つルートの情報が取れます。
| メソッド | 説明 |
|---|---|
Route::current |
ルートそのもの(Illuminate\Routing\Route)を返す |
Route::currentRouteName |
ルートの名前を返す |
Route::currentRouteAction |
ルートが動かす処理(コントローラーとメソッドなど)を返す |
use Illuminate\Support\Facades\Route;
$route = Route::current(); // Illuminate\Routing\Route
$name = Route::currentRouteName(); // string
$action = Route::currentRouteAction(); // string
使えるメソッドの全部は、Laravel の API ドキュメントの、Route ファサードの元のクラスと、ルートのクラスの説明で調べられます。
CORS(ほかのサイトからの呼び出しを許す決まり)#
CORS は、あるサイトの JavaScript が、別のドメインのサーバーへリクエストしてよいかを決める、ブラウザの決まりです。Laravel は、CORS をたずねる OPTIONS リクエストに、設定した値で自動で答えます。この仕事は、すべてのリクエストにかかるミドルウェアの HandleCors が受け持ちます。これは最初から入っています。
設定を変えたいときは、config:publish コマンドで cors の設定ファイルを取り出します。
php artisan config:publish cors
config フォルダに cors.php ができます。
補足
CORS と、その HTTP ヘッダーについては、MDN の CORS の説明に詳しく書かれています。
ルートのキャッシュ#
本番へ公開するときは、ルートのキャッシュ(作った結果を取っておくこと)を使います。すべてのルートを登録する時間が、ぐっと短くなります。route:cache コマンドで作ります。
php artisan route:cache
そのあとは、キャッシュされたルートのファイルが、リクエストのたびに読み込まれます。ルートを足したら、キャッシュを作り直します。そのため、route:cache は公開(デプロイ)のときだけ実行します。
キャッシュは route:clear で消せます。
php artisan route:clear
関連するページ#
公式ドキュメント(英語)
2026年10月5日時点の内容をもとに、日本語でまとめています。