Доступ к связанным с маршрутом моделям в запросах формы Laravel с #[RouteParameter]
#[RouteParameter]
, который используется в Laravel для доступа к моделям, связанным с маршрутом, в классах запросов форм. Мы подробно рассмотрим, как применять этот атрибут и какие проблемы он решает.Введение
Недавно я начал использовать новый атрибут #[RouteParameter]
в Laravel, и мне он понравился. Он решает проблему, с которой я сталкивался долгое время при работе с запросами формы, и, на мой взгляд, делает код намного чище.
В этой статье я расскажу, что такое атрибут #[RouteParameter]
, как его использовать и какую проблему он решает.
Что такое атрибут #[RouteParameter]
Атрибут #[Illuminate\Container\Attributes\RouteParameter]
— новый PHP атрибут, внесённый в Laravel Bastien Philippe (@bastien-phi) в PR #53080. Он вышел в Laravel v11.28.0.
Атрибут позволяет разрешать параметр маршрута непосредственно в сигнатурах методов запроса формы. Но что это значит? Давайте рассмотрим несколько примеров его использования.
Как использовать атрибут #[RouteParameter]
Атрибут #[RouteParameter]
можно использовать в методах authorize
и rules
запросов формы.
Использование атрибута #[RouteParameter]
в методе authorize
Представьте, что есть класс запроса формы App\Http\Requests\UpdateArticleRequest
, используемый при обновлении модели App\Models\Article
. В методе authorize
запроса формы необходимо проверить, является ли авторизованный пользователь владельцем статьи, которую он пытается обновить. Если да, то разрешаем доступ, в противном случае — запрещаем.
Предположим, что этот запрос формы используется методом контроллера, доступ к которому осуществляется по следующему маршруту:
// routes/web.php
use App\Http\Controllers\ArticleController;
use Illuminate\Support\Facades\Route;
Route::patch('/articles/{article}', [ArticleController::class, 'update']);
Используя атрибут #[RouteParameter]
, можно разрешить модель App\Models\Article
из параметров маршрута непосредственно в сигнатуре метода, как это делается в методе контроллера.
Запрос формы будет выглядеть так:
// app/Http/Requests/UpdateArticleRequest.php
declare(strict_types=1);
namespace App\Http\Requests;
use App\Models\Article;
use Illuminate\Container\Attributes\RouteParameter;
use Illuminate\Foundation\Http\FormRequest;
final class UpdateArticleRequest extends FormRequest
{
public function authorize(
#[RouteParameter('article')] Article $article
): bool {
return $article->user->is($this->user());
}
// ...
}
Как видите, мы добавили параметр $article
в метод authorize
и применили к нему атрибут #[RouteParameter('article')]. Строка "article", переданная в атрибут, является именем параметра маршрута, который необходимо разрешить. Мы также указали тип параметра с помощью модели App\Models\Article
, чтобы убедиться, что разрешённый параметр маршрута является экземпляром той модели, которая ожидается.
Например, если мы посетили articles/123
, переменная $article
теперь будет экземпляром App\Models\Article
для статьи с идентификатором 123
.
Используя этот атрибут, можно улучшить понимание кода интегрированной средой разработки (IDE) и инструментами статического анализа, чтобы обеспечить лучшее завершение кода и проверку типов.
Довольно круто, правда?
Использование атрибута #[RouteParameter]
в методе rules
Помимо использования атрибута #[RouteParameter]
в методе authorize
, его также можно использовать в методе rules
запроса формы.
Например, можно передать экземпляр модели в правило следующим образом:
// app/Http/Requests/UpdateArticleRequest.php
declare(strict_types=1);
namespace App\Http\Requests;
use App\Models\Article;
use Illuminate\Container\Attributes\RouteParameter;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
final class UpdateArticleRequest extends FormRequest
{
// ...
public function rules(#[RouteParameter('article')] Article $article): array
{
return [
'slug' => Rule::unique('articles')->ignoreModel($article),
];
}
}
Ошибки при использовании атрибута #[RouteParameter]
Я предпочитаю по возможности избегать сырых строк в своём коде, поскольку склонен к опечаткам. И давайте будем честными, ошибки, вызванные опечатками, всегда труднее всего обнаружить!
Но если вы уверены, что указываете тип параметра с классом модели, который ожидаете, то получите сообщение об ошибке, если допустили опечатку в строке атрибута. Например, допустим, вы случайно набрали "artiicle"
вместо "article"
:
// app/Http/Requests/UpdateArticleRequest.php
declare(strict_types=1);
namespace App\Http\Requests;
use App\Models\Article;
use Illuminate\Container\Attributes\RouteParameter;
use Illuminate\Foundation\Http\FormRequest;
final class UpdateArticleRequest extends FormRequest
{
public function authorize(#[RouteParameter('artiicle')] Article $article): bool
{
return $article->user->is($this->user());
}
// ...
}
Выполнение вышеописанных действий приведёт к ошибке:
App\Http\Requests\UpdateArticleRequest::authorize(): Argument #1 ($article) must be of type App\Models\Article, null given, called in /Users/ashallen/www/laravel-app/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php on line 36
Проблема, решаемая атрибутом #[RouteParameter]
Теперь, когда мы рассмотрели, как использовать атрибут #[RouteParameter]
, давайте посмотрим на решаемую им проблему. Возможно, проблема
— это слишком сильно сказано, но мне всегда казалось, что она требует обходного пути.
Раньше, когда требовалось получить доступ к параметрам маршрута в запросах формы, я использовал метод route
в объекте запроса для их получения. Например, в методе authorize
запроса формы App\Http\Requests\UpdateArticleRequest
я делал следующее:
// app/Http/Requests/UpdateArticleRequest.php
declare(strict_types=1);
namespace App\Http\Requests;
use App\Models\Article;
use Illuminate\Container\Attributes\RouteParameter;
use Illuminate\Foundation\Http\FormRequest;
final class UpdateArticleRequest extends FormRequest
{
public function authorize(): bool
{
return $this->route('article')->user->is($this->user());
}
// ...
}
Выполнение приведённого выше кода полностью корректно, и вызов метода $this->route('article)
вернёт экземпляр модели App\Models\Article
, разрешённый из параметра маршрута. Однако он не предоставляет достаточно информации для моей IDE или инструментов статического анализа, чтобы понять, что возвращает метод.
Один из используемых мною способов обойти это — создание приватного метода в запросе формы, возвращающего разрешённый параметр маршрута. Например:
// app/Http/Requests/UpdateArticleRequest.php
declare(strict_types=1);
namespace App\Http\Requests;
use App\Models\Article;
use Illuminate\Container\Attributes\RouteParameter;
use Illuminate\Foundation\Http\FormRequest;
final class UpdateArticleRequest extends FormRequest
{
public function authorize(): bool
{
return $this->article()->user->is($this->user());
}
private function article(): Article
{
return $this->route('article');
}
// ...
}
Это поможет моей IDE с автозавершением в методе authorize
, но на самом деле я лишь перекладываю проблему на другой метод (в данном случае на метод article()
). Конечно, можно использовать docblock, чтобы предоставить IDE больше информации, но я предпочитаю по возможности избегать их, поскольку они могут устареть и легко забыть их обновить.
По этим причинам мне нравится атрибут #[RouteParameter]
. Это небольшое изменение, но оно делает код чище и проще для понимания, а также радует мою IDE и инструменты статического анализа.
Заключение
В статье мы рассмотрели атрибут #[RouteParameter]
в Laravel. Выяснили, как использовать его в методах authorize
и rules
запросов формы и как он решает проблему получения параметров маршрута в этих методах.