#Введение
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
Если в хелпер url не передать путь, будет возвращён экземпляр Illuminate\Routing\UrlGenerator, позволяющий получить информацию о текущем URL:
// Получить текущий URL без строки запроса...
echo url()->current();
// Получить текущий URL вместе со строкой запроса...
echo url()->full();
// Получить полный URL предыдущего запроса...
echo url()->previous();
Каждый из этих методов также доступен через фасад URL:
use Illuminate\Support\Facades\URL;
echo URL::current();
#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 для реализации публичной ссылки «отписаться», которая отправляется вашим клиентам по электронной почте. Чтобы создать подписанный URL для именованного маршрута, используйте метод signedRoute фасада URL:
use Illuminate\Support\Facades\URL;
return URL::signedRoute('unsubscribe', ['user' => 1]);
Вы можете исключить домен из хеша подписанного URL, передав аргумент absolute в метод signedRoute:
return URL::signedRoute('unsubscribe', ['user' => 1], absolute: false);
Если нужно сгенерировать временный подписанный URL, который истекает через заданное время, используйте метод temporarySignedRoute. При проверке временного подписанного URL Laravel убедится, что время истечения, закодированное в URL, не прошло:
use Illuminate\Support\Facades\URL;
return URL::temporarySignedRoute(
'unsubscribe', now()->addMinutes(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, например, при постраничной навигации на клиенте. Для этого можно указать параметры запроса, которые должны игнорироваться при проверке подписи, используя метод hasValidSignatureWhileIgnoring. Помните, что игнорирование параметров позволяет любому изменять их в запросе:
if (! $request->hasValidSignatureWhileIgnoring(['page', 'order'])) {
abort(401);
}
Вместо проверки подписанных URL через экземпляр запроса, вы можете назначить middleware Illuminate\Routing\Middleware\ValidateSignature маршруту. Если оно ещё не зарегистрировано, вы можете добавить псевдоним для этого middleware в массив $middlewareAliases вашего HTTP ядра:
/**
* Псевдонимы middleware приложения.
*
* Псевдонимы позволяют удобно назначать middleware маршрутам и группам.
*
* @var array<string, class-string|string>
*/
protected $middlewareAliases = [
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
];
После регистрации middleware в ядре, вы можете прикрепить его к маршруту. Если входящий запрос не имеет действительной подписи, middleware автоматически вернёт HTTP-ответ с кодом 403:
Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed');
Если ваши подписанные URL не включают домен в хеш URL, укажите аргумент relative для middleware:
Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
})->name('unsubscribe')->middleware('signed:relative');
#Обработка недействительных подписанных маршрутов
Если кто-то перейдёт по подписанному URL, срок действия которого истёк, он увидит стандартную страницу ошибки с HTTP статусом 403. Однако вы можете изменить это поведение, определив кастомный замыкание «renderable» для исключения InvalidSignatureException в обработчике исключений. Это замыкание должно возвращать HTTP-ответ:
use Illuminate\Routing\Exceptions\InvalidSignatureException;
/**
* Регистрация обработчиков исключений для приложения.
*/
public function register(): void
{
$this->renderable(function (InvalidSignatureException $e) {
return response()->view('error.link-expired', [], 403);
});
}
#URL для действий контроллеров
Функция action генерирует URL для указанного действия контроллера:
use App\Http\Controllers\HomeController;
$url = action([HomeController::class, 'index']);
Если метод контроллера принимает параметры маршрута, вы можете передать ассоциативный массив параметров вторым аргументом функции:
$url = action([UserController::class, 'profile'], ['id' => 1]);
#Значения по умолчанию
В некоторых приложениях может потребоваться задать значения по умолчанию для определённых параметров 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
{
/**
* Обработка входящего запроса.
*
* @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. Это можно сделать, разместив ваше middleware перед SubstituteBindings в свойстве $middlewarePriority HTTP ядра приложения.
Свойство $middlewarePriority определено в базовом классе Illuminate\Foundation\Http\Kernel. Вы можете скопировать его определение из этого класса и переопределить в вашем HTTP ядре для изменения порядка:
/**
* Список middleware, отсортированных по приоритету.
*
* Это гарантирует, что неглобальные middleware всегда выполняются в заданном порядке.
*
* @var array
*/
protected $middlewarePriority = [
// ...
\App\Http\Middleware\SetDefaultLocaleForUrls::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
// ...
];