在 Laravel FormRequest 中使用 #[RouteParameter] 访问路由模型绑定模型
介绍
本文将解释 #[RouteParameter]
注解是什么,如何使用它,以及它所解决的问题。
什么是 #[RouteParameter]
注解?
#[Illuminate\Container\Attributes\RouteParameter]
注解是 Bastien Philippe (@bastien-phi) 在 PR #53080 中为 Laravel 贡献的一个新的 PHP 注解。在 Laravel v11.28.0 中发布的。
它允许你直接在表单请求方法的方法签名中解析路由参数。但这意味着什么?让我们来看一些如何使用它的例子。
如何使用 #[RouteParameter]
注解
#[RouteParameter]
注解可以在表单请求的 authorize
和 rules
方法中使用。
在 authorize
方法中使用 #[RouteParameter]
注解
假设我们有一个 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());
}
// ...
}
如上,我们在 authorize
方法中添加了一个 $article
参数,并对其应用了 #[RouteParameter('article')]
属性。传递给该属性的 "article"
字符串是我们要解析的路由参数的名称。我们还使用 App\Models\Article
模型对参数指定类型,以确保解析的路由参数是我们期望的模型的实例。
比如,如果我们访问 articles/123
,$article
变量现在将是 ID 为 123
的文章,App\Models\Article
的实例。
通过使用此属性,我们可以提高集成开发环境(IDE)和静态分析工具对代码的理解,以提供更好的代码完成和类型检查。
很酷,对吧?
在 rules
方法中使用 #[RouteParameter]
注解
与在 authorize
方法中使用 #[RouteParameter]
注解类似,我们也可以在表单请求的 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
方法来检索它们。比如,在 App\Http\Requests\UpdateArticleRequest
表单请求的 authorize
方法中,我可以这样:
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 和静态分析工具感到满意。
小结
本文中,我们研究了 Laravel 中的 #[RouteParameter]
注解。我们已经了解了如何在表单请求的 authorize
和 rules
方法中使用它,以及它如何解决在这些方法中检索路由参数的问题。