编程

在 Laravel FormRequest 中使用 #[RouteParameter] 访问路由模型绑定模型

49 2025-01-16 05:20:00

介绍

本文将解释 #[RouteParameter] 注解是什么,如何使用它,以及它所解决的问题。

什么是 #[RouteParameter] 注解?

#[Illuminate\Container\Attributes\RouteParameter] 注解是 Bastien Philippe (@bastien-phi) 在 PR #53080 中为 Laravel 贡献的一个新的 PHP 注解。在 Laravel v11.28.0 中发布的。

它允许你直接在表单请求方法的方法签名中解析路由参数。但这意味着什么?让我们来看一些如何使用它的例子。

如何使用 #[RouteParameter] 注解

#[RouteParameter] 注解可以在表单请求的 authorizerules 方法中使用。

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] 注解。我们已经了解了如何在表单请求的 authorizerules 方法中使用它,以及它如何解决在这些方法中检索路由参数的问题。