Генерація URL
Вступ
Laravel надає кілька хелперів, які допомагають генерувати URL для вашого застосунку. Вони насамперед корисні, коли ви створюєте посилання у шаблонах і відповідях API або генеруєте відповіді-перенаправлення до іншої частини застосунку.
Основи
Генерація 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:
use Illuminate\Support\Facades\URL;
echo URL::current();
Доступ до попереднього URL
Іноді корисно знати попередній URL, з якого прийшов користувач. Отримати його можна методами previous і previousPath хелпера url:
// Get the full URL for the previous request...
echo url()->previous();
// Get the path for the previous request...
echo url()->previousPath();
Або ж через сесію ви можете отримати попередній URL як екземпляр плинного URI:
use Illuminate\Http\Request;
Route::post('/users', function (Request $request) {
$previousUri = $request->session()->previousUri();
// ...
});
Через сесію також можна отримати ім'я маршруту попередньо відвіданого URL:
$previousRoute = $request->session()->previousRoute();
URL для іменованих маршрутів
Хелпер route дозволяє генерувати 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 можна використовувати й для маршрутів із кількома параметрами:
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
Часто ви генеруватимете URL за ключем маршруту (зазвичай первинним ключем) моделей Eloquent. Тому ви можете передавати моделі Eloquent як значення параметрів - хелпер route автоматично візьме ключ маршруту моделі:
echo route('post.show', ['post' => $post]);
Підписані URL
Laravel дозволяє легко створювати «підписані» URL до іменованих маршрутів. До рядка запиту таких URL додається хеш-«підпис», що дозволяє Laravel перевірити, чи не змінювався URL після створення. Підписані URL особливо корисні для маршрутів, які є загальнодоступними, але потребують шару захисту від маніпуляцій з адресою.
Наприклад, підписані URL можна використати для публічного посилання «відписатися», яке надсилають клієнтам електронною поштою. Щоб створити підписаний URL до іменованого маршруту, скористайтеся методом signedRoute фасаду URL:
use Illuminate\Support\Facades\URL;
return URL::signedRoute('unsubscribe', ['user' => 1]);
Ви можете виключити домен із хешу підписаного URL, передавши методу signedRoute аргумент absolute:
return URL::signedRoute('unsubscribe', ['user' => 1], absolute: false);
Якщо ви хочете згенерувати тимчасовий підписаний URL маршруту, що спливає через певний час, скористайтеся методом temporarySignedRoute. Перевіряючи тимчасовий підписаний URL, Laravel переконається, що закодована в ньому мітка часу спливання ще не минула:
use Illuminate\Support\Facades\URL;
return URL::temporarySignedRoute(
'unsubscribe', now()->plus(minutes: 30), ['user' => 1]
);
Перевірка запитів до підписаних маршрутів
Щоб перевірити, чи має вхідний запит дійсний підпис, викличте метод hasValidSignature на екземплярі Illuminate\Http\Request:
use Illuminate\Http\Request;
Route::get('/unsubscribe/{user}', function (Request $request) {
if (! $request->hasValidSignature()) {
abort(401);
}
// ...
})->name('unsubscribe');
Іноді фронтенду вашого застосунку потрібно додавати дані до підписаного URL - наприклад, під час пагінації на боці клієнта. Тому ви можете вказати параметри рядка запиту, які слід ігнорувати під час перевірки підписаного URL, методом hasValidSignatureWhileIgnoring. Пам'ятайте: ігнорування параметрів дозволяє будь-кому змінювати їх у запиті:
if (! $request->hasValidSignatureWhileIgnoring(['page', 'order'])) {
abort(401);
}
Замість перевіряти підписані URL через екземпляр вхідного запиту, ви можете призначити маршруту middleware signed (Illuminate\Routing\Middleware\ValidateSignature). Якщо вхідний запит не має дійсного підпису, middleware автоматично поверне HTTP-відповідь 403:
Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed');
Якщо ваші підписані URL не містять домену в хеші, передайте middleware аргумент relative:
Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed:relative');
Реакція на недійсні підписані маршрути
Коли хтось відвідує підписаний URL, термін дії якого сплив, він отримає загальну сторінку помилки для HTTP-статусу 403. Однак ви можете налаштувати цю поведінку, визначивши власне замикання «render» для винятку InvalidSignatureException у файлі bootstrap/app.php вашого застосунку:
use Illuminate\Routing\Exceptions\InvalidSignatureException;
->withExceptions(function (Exceptions $exceptions): void {
$exceptions->render(function (InvalidSignatureException $e) {
return response()->view('errors.link-expired', status: 403);
});
})
URL для дій контролерів
Функція action генерує URL для вказаної дії контролера:
use App\Http\Controllers\HomeController;
$url = action([HomeController::class, 'index']);
Якщо метод контролера приймає параметри маршруту, ви можете передати асоціативний масив параметрів другим аргументом функції:
$url = action([UserController::class, 'profile'], ['id' => 1]);
Плинні об'єкти URI
Клас Uri Laravel надає зручний плинний інтерфейс для створення 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');
Докладніше про роботу з плинними об'єктами URI дивіться в документації URI.
Значення за замовчуванням
У деяких застосунках вам може знадобитися задати значення за замовчуванням для певних параметрів URL у межах усього запиту. Наприклад, уявіть, що багато ваших маршрутів визначають параметр {locale}:
Route::get('/{locale}/posts', function () {
// ...
})->name('post.index');
Марудно щоразу передавати locale, викликаючи хелпер route. Тож ви можете скористатися методом URL::defaults, щоб задати значення за замовчуванням для цього параметра, яке застосовуватиметься протягом поточного запиту. Цей метод варто викликати з middleware маршруту, щоб мати доступ до поточного запиту:
<?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 задано, вам більше не потрібно передавати його, генеруючи URL хелпером route.
Значення URL за замовчуванням і пріоритет middleware
Задавання значень URL за замовчуванням може заважати обробці неявних прив'язок моделей у Laravel. Тому вам слід задати пріоритет своєму middleware, який встановлює ці значення, щоб він виконувався перед власним middleware SubstituteBindings від Laravel. Це можна зробити методом priority у файлі bootstrap/app.php вашого застосунку:
->withMiddleware(function (Middleware $middleware): void {
$middleware->prependToPriorityList(
before: \Illuminate\Routing\Middleware\SubstituteBindings::class,
prepend: \App\Http\Middleware\SetDefaultLocaleForUrls::class,
);
})