URLの生成 (URL Generation)
導入 (Introduction)
Laravel には、アプリケーションの URL の生成を支援するいくつかのヘルパが用意されています。これらのヘルパは主に、テンプレートや API 応答にリンクを構築するとき、またはアプリケーションの別の部分へのリダイレクト応答を生成するときに役立ちます。
基本 (The Basics)
URLの生成
url ヘルパは、アプリケーションの任意の URL を生成するために使用できます。生成された URL は、アプリケーションによって処理されている現在のリクエストのスキーム (HTTP または HTTPS) とホストを自動的に使用します。
$post = App\Models\Post::find(1);
echo url("/posts/{$post->id}");
// http://example.com/posts/1
クエリ文字列パラメーターを含む URL を生成するには、query メソッドを使用できます。
echo url()->query('/posts', ['search' => 'Laravel']);
// https://example.com/posts?search=Laravel
echo url()->query('/posts?sort=latest', ['search' => 'Laravel']);
// http://example.com/posts?sort=latest&search=Laravel
パス内にすでに存在するクエリ文字列パラメーターを指定すると、既存の値が上書きされます。
echo url()->query('/posts?sort=latest', ['sort' => 'oldest']);
// http://example.com/posts?sort=oldest
値の配列をクエリ パラメーターとして渡すこともできます。これらの値は、生成された URL 内で適切にキー設定され、エンコードされます。
echo $url = url()->query('/posts', ['columns' => ['title', 'body']]);
// http://example.com/posts?columns%5B0%5D=title&columns%5B1%5D=body
echo urldecode($url);
// http://example.com/posts?columns[0]=title&columns[1]=body
現在の URL にアクセスする
url ヘルパにパスが指定されていない場合は、Illuminate\Routing\UrlGenerator インスタンスが返され、現在の URL に関する情報にアクセスできるようになります。
// Get the current URL without the query string...
echo url()->current();
// Get the current URL including the query string...
echo url()->full();
これらの各メソッドには、URL facade 経由でもアクセスできます。
use Illuminate\Support\Facades\URL;
echo URL::current();
以前の URL にアクセスする
ユーザーが以前にアクセスしていた URL を知っておくと役立つ場合があります。以前の URL には、url ヘルパの previous および previousPath メソッドを介してアクセスできます。
// Get the full URL for the previous request...
echo url()->previous();
// Get the path for the previous request...
echo url()->previousPath();
または、session 経由で、流暢なURI インスタンスとして前の URL にアクセスできます。
use Illuminate\Http\Request;
Route::post('/users', function (Request $request) {
$previousUri = $request->session()->previousUri();
// ...
});
セッションを通じて、以前にアクセスした URL のルート名を取得することもできます。
$previousRoute = $request->session()->previousRoute();
名前付きルートの URL (URLs for Named Routes)
route ヘルパを使用して、名前付きルート への URL を生成できます。名前付きルートを使用すると、ルート上で定義された実際の URL に結合せずに URL を生成できます。したがって、ルートの URL が変更された場合、route 関数の呼び出しを変更する必要はありません。たとえば、アプリケーションに次のように定義されたルートが含まれていると想像してください。
Route::get('/post/{post}', function (Post $post) {
// ...
})->name('post.show');
このルートへの URL を生成するには、次のように route ヘルパを使用します。
echo route('post.show', ['post' => 1]);
// http://example.com/post/1
もちろん、route ヘルパを使用して、複数のパラメータを持つルートの URL を生成することもできます。
Route::get('/post/{post}/comment/{comment}', function (Post $post, Comment $comment) {
// ...
})->name('comment.show');
echo route('comment.show', ['post' => 1, 'comment' => 3]);
// http://example.com/post/1/comment/3
ルートの定義パラメーターに対応しない追加の配列要素は、URL のクエリ文字列に追加されます。
echo route('post.show', ['post' => 1, 'search' => 'rocket']);
// http://example.com/post/1?search=rocket
Eloquent モデル
多くの場合、Eloquent モデル のルート キー (通常は主キー) を使用して URL を生成します。このため、Eloquent モデルをパラメーター値として渡すことができます。 route ヘルパは、モデルのルート キーを自動的に抽出します。
echo route('post.show', ['post' => $post]);
署名付き URL
Laravel を使用すると、名前付きルートへの「署名付き」URL を簡単に作成できます。これらの URL にはクエリ文字列に「署名」ハッシュが追加されており、これにより Laravel は URL が作成されてから変更されていないことを確認できます。署名付き URL は、公的にアクセス可能でありながら、URL 操作に対する保護層が必要なルートに特に役立ちます。
たとえば、署名付き URL を使用して、顧客に電子メールで送信される公開「購読解除」リンクを実装できます。名前付きルートへの署名付き URL を作成するには、URL ファサードの signedRoute メソッドを使用します。
use Illuminate\Support\Facades\URL;
return URL::signedRoute('unsubscribe', ['user' => 1]);
absolute 引数を signedRoute メソッドに指定することで、署名付き URL ハッシュからドメインを除外できます。
return URL::signedRoute('unsubscribe', ['user' => 1], absolute: false);
指定した時間が経過すると期限切れになる一時的な署名付きルート URL を生成したい場合は、temporarySignedRoute メソッドを使用できます。 Laravel は、一時的な署名付きルート URL を検証するときに、署名付き URL にエンコードされている有効期限タイムスタンプが経過していないことを確認します。
use Illuminate\Support\Facades\URL;
return URL::temporarySignedRoute(
'unsubscribe', now()->plus(minutes: 30), ['user' => 1]
);
署名されたルートリクエストの検証
受信リクエストに有効な署名があることを確認するには、受信 Illuminate\Http\Request インスタンスで hasValidSignature メソッドを呼び出す必要があります。
use Illuminate\Http\Request;
Route::get('/unsubscribe/{user}', function (Request $request) {
if (! $request->hasValidSignature()) {
abort(401);
}
// ...
})->name('unsubscribe');
場合によっては、クライアント側でページネーションを実行する場合など、アプリケーションのフロントエンドが署名付き URL にデータを追加できるようにする必要があります。したがって、hasValidSignatureWhileIgnoring メソッドを使用して署名付き URL を検証するときに無視する必要があるリクエスト クエリ パラメーターを指定できます。パラメーターを無視すると、誰でもリクエストでそれらのパラメーターを変更できるようになることに注意してください。
if (! $request->hasValidSignatureWhileIgnoring(['page', 'order'])) {
abort(401);
}
受信リクエスト インスタンスを使用して署名付き URL を検証する代わりに、signed (Illuminate\Routing\Middleware\ValidateSignature) middleware をルートに割り当てることができます。受信リクエストに有効な署名がない場合、ミドルウェアは自動的に 403 HTTP レスポンスを返します。
Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed');
署名付き URL の URL ハッシュにドメインが含まれていない場合は、relative 引数をミドルウェアに提供する必要があります。
Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed:relative');
無効な署名付きルートへの対応
有効期限が切れた署名付き URL にアクセスすると、403 HTTP ステータス コードの一般的なエラー ページが表示されます。ただし、アプリケーションの bootstrap/app.php ファイルで InvalidSignatureException 例外のカスタム "render" クロージャを定義することで、この動作をカスタマイズできます。
use Illuminate\Routing\Exceptions\InvalidSignatureException;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->render(function (InvalidSignatureException $e) {
return response()->view('errors.link-expired', status: 403);
});
})
コントローラアクションのURL (URLs for Controller Actions)
action 関数は、指定されたコントローラ アクションの URL を生成します。
use App\Http\Controllers\HomeController;
$url = action([HomeController::class, 'index']);
コントローラ メソッドがルート パラメーターを受け入れる場合、ルート パラメーターの連想配列を 2 番目の引数として関数に渡すことができます。
$url = action([UserController::class, 'profile'], ['id' => 1]);
流暢な URI オブジェクト (Fluent URI Objects)
Laravel の Uri クラスは、オブジェクト経由で URI を作成および操作するための便利で流暢なインターフェイスを提供します。このクラスは、基礎となる League URI パッケージによって提供される機能をラップし、Laravel のルーティング システムとシームレスに統合します。
静的メソッドを使用して、Uri インスタンスを簡単に作成できます。
use App\Http\Controllers\UserController;
use App\Http\Controllers\InvokableController;
use Illuminate\Support\Uri;
// Generate a URI instance from the given string...
$uri = Uri::of('https://example.com/path');
// Generate URI instances to paths, named routes, or controller actions...
$uri = Uri::to('/dashboard');
$uri = Uri::route('users.show', ['user' => 1]);
$uri = Uri::signedRoute('users.show', ['user' => 1]);
$uri = Uri::temporarySignedRoute('user.index', now()->plus(minutes: 5));
$uri = Uri::action([UserController::class, 'index']);
$uri = Uri::action(InvokableController::class);
// Generate a URI instance from the current request URL...
$uri = $request->uri();
// Generate a URI instance from the previous request URL...
$uri = $request->session()->previousUri();
URI インスタンスを取得したら、それをスムーズに変更できます。
$uri = Uri::of('https://example.com')
->withScheme('http')
->withHost('test.com')
->withPort(8000)
->withPath('/users')
->withQuery(['page' => 2])
->withFragment('section-1');
Fluent URI オブジェクトの操作の詳細については、URI ドキュメント を参照してください。
デフォルト値 (Default Values)
一部のアプリケーションでは、特定の URL パラメータに対してリクエスト全体のデフォルト値を指定することが必要な場合があります。たとえば、多くのルートで {locale} パラメータが定義されていると想像してください。
Route::get('/{locale}/posts', function () {
// ...
})->name('post.index');
route ヘルパを呼び出すたびに、常に locale を渡すのは面倒です。したがって、URL::defaults メソッドを使用して、現在のリクエスト中に常に適用されるこのパラメータのデフォルト値を定義できます。現在のリクエストにアクセスできるように、ルートミドルウェア からこのメソッドを呼び出すこともできます。
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\URL;
use Symfony\Component\HttpFoundation\Response;
class SetDefaultLocaleForUrls
{
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
URL::defaults(['locale' => $request->user()->locale]);
return $next($request);
}
}
locale パラメータのデフォルト値を設定すると、route ヘルパを介して URL を生成するときにその値を渡す必要はなくなります。
URL のデフォルトとミドルウェアの優先順位
URL のデフォルト値を設定すると、Laravel による暗黙的なモデルバインディングの処理が妨げられる可能性があります。したがって、URL のデフォルトを設定する ミドルウェアに優先順位を付ける は、Laravel 独自の SubstituteBindings ミドルウェアよりも前に実行する必要があります。これは、アプリケーションの bootstrap/app.php ファイルで priority ミドルウェア メソッドを使用して実現できます。
->withMiddleware(function (Middleware $middleware): void {
$middleware->prependToPriorityList(
before: \Illuminate\Routing\Middleware\SubstituteBindings::class,
prepend: \App\Http\Middleware\SetDefaultLocaleForUrls::class,
);
})