spatie/laravel-route-attributes

使用PHP属性自动注册路由

1.23.0 2024-08-27 18:54 UTC

README

Latest Version on Packagist Tests Type Coverage Total Downloads

本包提供了自动注册路由的属性。以下是一个快速示例:

use Spatie\RouteAttributes\Attributes\Get;

class MyController
{
    #[Get('my-route')]
    public function myMethod()
    {
    }
}

此属性将自动注册此路由

Route::get('my-route', [MyController::class, 'myMethod']);

你是一个视觉学习者吗?

在这段视频中,你将了解PHP 8属性以及laravel-routes-attributes在底层是如何工作的。

支持我们

我们在创建最佳开源包上投入了大量的资源。您可以通过购买我们的付费产品之一来支持我们。

我们非常感谢您从家乡给我们寄来明信片,注明您正在使用我们的哪个包。您可以在我们的联系页面上找到我们的地址。我们将所有收到的明信片发布在我们的虚拟明信片墙上

安装

您可以通过composer安装此包

composer require spatie/laravel-route-attributes

您可以使用以下命令发布配置文件

php artisan vendor:publish --provider="Spatie\RouteAttributes\RouteAttributesServiceProvider" --tag="config"

这是已发布配置文件的内容

return [
    /*
     *  Automatic registration of routes will only happen if this setting is `true`
     */
    'enabled' => true,

    /*
     * Controllers in these directories that have routing attributes
     * will automatically be registered.
     *
     * Optionally, you can specify group configuration by using key/values
     */
    'directories' => [
        app_path('Http/Controllers'),

        app_path('Http/Controllers/Web') => [
            'middleware' => ['web']
        ],

        app_path('Http/Controllers/Api') => [
            'prefix' => 'api',
            'middleware' => 'api'
        ],
    ],
];

对于位于应用程序根命名空间目录之外的控制器,也可以使用目录数组中的namespace => path模式添加。在以下示例中,将包含来自Modules\Admin\Http\Controllers的控制器。

'directories' => [
    'Modules\Admin\Http\Controllers\\' => base_path('admin-module/Http/Controllers'),
    // Or
    base_path('admin-module/Http/Controllers') => [
        'namespace' => 'Modules\Admin\Http\Controllers\\'
    ],
    app_path('Http/Controllers'),
],

如果您正在使用将多种类型的文件放置在同一个目录中的目录结构,并且想更具体地指定哪些文件用于检查路由属性,则可以使用patternsnot_patterns选项。例如,如果您将测试与控制器放置在一起,则可以使用patterns选项仅查找控制器文件,或者您可以使用not_patterns将其配置为不在测试文件中查找路由属性。

'directories' => [
    base_path('app-modules/Blog') => [
        // only register routes in files that match the patterns
        'patterns' => ['*Controller.php'],
        // do not register routes in files that match the patterns
        'not_patterns => ['*Test.php'],
    ],
],

使用方法

该包提供了一些注解,应该放在控制器类和方法上。这些注解将被用来自动注册路由

添加GET路由

use Spatie\RouteAttributes\Attributes\Get;

class MyController
{
    #[Get('my-route')]
    public function myMethod()
    {

    }
}

此属性将自动注册此路由

Route::get('my-route', [MyController::class, 'myMethod']);

使用其他HTTP动词

我们没有遗漏任何HTTP动词。您可以在控制器方法上使用这些属性。

#[Spatie\RouteAttributes\Attributes\Post('my-uri')]
#[Spatie\RouteAttributes\Attributes\Put('my-uri')]
#[Spatie\RouteAttributes\Attributes\Patch('my-uri')]
#[Spatie\RouteAttributes\Attributes\Delete('my-uri')]
#[Spatie\RouteAttributes\Attributes\Options('my-uri')]

资源控制器

要注册一个资源控制器,如以下示例所示使用Resource属性。

您可以使用onlyexcept参数来管理资源路由的可用性。

您可以使用parameters参数来修改资源属性设置的默认参数。

您可以使用names参数来设置资源控制器动作的路由名称。传递字符串值以设置每个控制器动作的基础路由名称,或传递数组值以定义每个控制器动作的路由名称。

您可以使用shallow参数来使嵌套资源仅应用于没有唯一子标识符的路由(如indexcreatestore)。

您可以使用apiResource布尔参数来仅包括在API中使用的操作。或者,您可以使用扩展了Resource属性类的ApiResource属性,但apiResource参数已经设置为true

使用带有DomainPrefixMiddleware属性的Resource属性同样有效。

use Spatie\RouteAttributes\Attributes\Resource;

#[Prefix('api/v1')]
#[Resource(
    resource: 'photos.comments',
    apiResource: true,
    shallow: true,
    parameters: ['comments' => 'comment:uuid'],
    names: 'api.v1.photoComments',
    except: ['destroy'],
)]
// OR #[ApiResource(resource: 'photos.comments', shallow: true, ...)]
class PhotoCommentController
{
    public function index(Photo $photo)
    {
    }

    public function store(Request $request, Photo $photo)
    {
    }

    public function show(Comment $comment)
    {
    }

    public function update(Request $request, Comment $comment)
    {
    }
}

上面示例中的属性将自动注册以下路由

Route::get('api/v1/photos/{photo}/comments', [PhotoCommentController::class, 'index'])->name('api.v1.photoComments.index');
Route::post('api/v1/photos/{photo}/comments', [PhotoCommentController::class, 'store'])->name('api.v1.photoComments.store');
Route::get('api/v1/comments/{comment}', [PhotoCommentController::class, 'show'])->name('api.v1.photoComments.show');
Route::match(['put', 'patch'], 'api/v1/comments/{comment}', [PhotoCommentController::class, 'update'])->name('api.v1.photoComments.update');

使用多个动词

要注册适用于所有动词的路由,您可以使用Any属性

#[Spatie\RouteAttributes\Attributes\Any('my-uri')]

要一次性注册几个动词的路由,您可以直接使用Route属性

#[Spatie\RouteAttributes\Attributes\Route(['put', 'patch'], 'my-uri')]

指定路由名称

所有HTTP动词属性都接受一个名为name的参数,该参数接受路由名称。

use Spatie\RouteAttributes\Attributes\Get;

class MyController
{
    #[Get('my-route', name: "my-route-name")]
    public function myMethod()
    {
    }
}

此属性将自动注册此路由

Route::get('my-route', [MyController::class, 'myMethod'])->name('my-route-name');

添加中间件

所有HTTP动词属性都接受一个名为middleware的参数,该参数接受中间件类或中间件类数组。

use Spatie\RouteAttributes\Attributes\Get;

class MyController
{
    #[Get('my-route', middleware: MyMiddleware::class)]
    public function myMethod()
    {

    }
}

此注解将自动注册此路由

Route::get('my-route', [MyController::class, 'myMethod'])->middleware(MyMiddleware::class);

要在一个类的所有方法上应用中间件,您可以使用Middleware属性。您可以将其与在方法上应用属性结合起来。

use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Middleware;

#[Middleware(MyMiddleware::class)]
class MyController
{
    #[Get('my-route')]
    public function firstMethod()
    {
    }

    #[Get('my-other-route', middleware: MyOtherMiddleware::class)]
    public function secondMethod()
    {
    }
}

这些注解将自动注册这些路由

Route::get('my-route', [MyController::class, 'firstMethod'])->middleware(MyMiddleware::class);
Route::get('my-other-route', [MyController::class, 'secondMethod'])->middleware([MyMiddleware::class, MyOtherMiddleware::class]);

指定前缀

您可以在类上使用Prefix注解,为该类的所有方法的路由添加前缀。

use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\Prefix;

#[Prefix('my-prefix')]
class MyController
{
    #[Get('my-get-route')]
    public function myGetMethod()
    {
    }

    #[Post('my-post-route')]
    public function myPostMethod()
    {
    }
}

这些注解将自动注册这些路由

Route::get('my-prefix/my-get-route', [MyController::class, 'myGetMethod']);
Route::post('my-prefix/my-post-route', [MyController::class, 'myPostMethod']);

指定域名

您可以在类上使用Domain注解,为该类的所有方法的路由添加前缀。

use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\Domain;

#[Domain('my-subdomain.localhost')]
class MyController
{
    #[Get('my-get-route')]
    public function myGetMethod()
    {
    }

    #[Post('my-post-route')]
    public function myPostMethod()
    {
    }
}

这些注解将自动注册这些路由

Route::get('my-get-route', [MyController::class, 'myGetMethod'])->domain('my-subdomain.localhost');
Route::post('my-post-route', [MyController::class, 'myPostMethod'])->domain('my-subdomain.localhost');

从配置键指定域名

可能需要从配置文件中定义一个域名,例如,您的开发环境中的子域名与生产环境不同。

// config/domains.php
return [
    'main' => env('SITE_URL', 'example.com'),
    'subdomain' => env('SUBDOMAIN_URL', 'subdomain.example.com')
];
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\DomainFromConfig;

#[DomainFromConfig('domains.main')]
class MyController
{
    #[Get('my-get-route')]
    public function myGetMethod()
    {
    }
}

当它被解析时,它将从配置文件中获取domains.main的值,并按如下方式注册路由;

Route::get('my-get-route', [MyController::class, 'myGetMethod'])->domain('example.com');

范围绑定

在单个路由定义中隐式绑定多个Eloquent模型时,您可能希望将第二个Eloquent模型的范围限制为必须是先前Eloquent模型的子模型。

通过添加ScopeBindings注解,您可以启用此行为

use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\ScopeBindings;

class MyController
{
    #[Get('users/{user}/posts/{post}')]
    #[ScopeBindings]
    public function getUserPost(User $user, Post $post)
    {
        return $post;
    }
}

这类似于手动在路由注册器上使用->scopeBindings()方法

Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
    return $post;
})->scopeBindings();

默认情况下,Laravel会在使用自定义键的隐式绑定作为嵌套路由参数时,在路由上启用范围绑定,例如/users/{user}/posts/{post:slug}

要禁用此行为,您可以将false传递给属性

#[ScopeBindings(false)]

这相当于手动在路由注册器上调用->withoutScopedBindings()

您还可以在控制器上使用注解来为所有方法启用隐式范围绑定。对于任何您想要覆盖的方法,您可以在那些方法上传递false给属性,就像您通常做的那样。

指定位置

您可以在类或方法上使用Where注解来约束路由参数的格式。

use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\Where;
use Spatie\RouteAttributes\Attributes\WhereAlphaNumeric;

#[Where('my-where', '[0-9]+')]
class MyController
{
    #[Get('my-get-route/{my-where}')]
    public function myGetMethod()
    {
    }

    #[Post('my-post-route/{my-where}/{my-alpha-numeric}')]
    #[WhereAlphaNumeric('my-alpha-numeric')]
    public function myPostMethod()
    {
    }
}

这些注解将自动注册这些路由

Route::get('my-get-route/{my-where}', [MyController::class, 'myGetMethod'])->where(['my-where' => '[0-9]+']);
Route::post('my-post-route/{my-where}/{my-alpha-numeric}', [MyController::class, 'myPostMethod'])->where(['my-where' => '[0-9]+', 'my-alpha-numeric' => '[a-zA-Z0-9]+']);

为了方便起见,一些常用的正则表达式模式有辅助属性,允许您快速将模式约束添加到路由中。

#[WhereAlpha('alpha')]
#[WhereAlphaNumeric('alpha-numeric')]
#[WhereIn('in', ['value1', 'value2'])]
#[WhereNumber('number')]
#[WhereUlid('ulid')]
#[WhereUuid('uuid')]

指定分组

您可以在类上使用Group注解来为该类的所有方法的路由创建具有不同域名和前缀的多个分组。

use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\Domain;

#[Group(domain: 'my-subdomain.localhost', prefix: 'my-prefix')]
#[Group(domain: 'my-second-subdomain.localhost', prefix: 'my-second-prefix')]
class MyController
{
    #[Get('my-get-route')]
    public function myGetMethod()
    {
    }

    #[Post('my-post-route')]
    public function myPostMethod()
    {
    }
}

这些注解将自动注册这些路由

Route::get('my-get-route', [MyController::class, 'myGetMethod'])->prefix('my-prefix')->domain('my-subdomain.localhost');
Route::post('my-post-route', [MyController::class, 'myPostMethod'])->prefix('my-prefix')->domain('my-subdomain.localhost');
Route::get('my-get-route', [MyController::class, 'myGetMethod'])->prefix('my-second-prefix')->domain('my-second-subdomain.localhost');
Route::post('my-post-route', [MyController::class, 'myPostMethod'])->prefix('my-second-prefix')->domain('my-second-subdomain.localhost');

指定默认值

您可以在类或方法上使用Defaults注解来定义可选路由参数的默认值。

use Spatie\RouteAttributes\Attributes\Defaults;
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;

#[Defaults('param', 'controller-default')]
class MyController extends Controller
{
    #[Get('my-get-route/{param?}')]
    public function myGetMethod($param)
    {
    }

    #[Post('my-post-route/{param?}/{param2?}')]
    #[Defaults('param2', 'method-default')]
    public function myPostMethod($param, $param2)
    {
    }

    #[Get('my-default-route/{param?}/{param2?}/{param3?}')]
    #[Defaults('param2', 'method-default-first')]
    #[Defaults('param3', 'method-default-second')]
    public function myDefaultMethod($param, $param2, $param3)
    {
    }

    #[Get('my-override-route/{param?}')]
    #[Defaults('param', 'method-default')]
    public function myOverrideMethod($param)
    {
    }
}

这些注解将自动注册这些路由

Route::get('my-get-route/{param?}', [MyController::class, 'myGetMethod'])->setDefaults(['param', 'controller-default']);
Route::post('my-post-route/{param?}/{param2?}', [MyController::class, 'myPostMethod'])->setDefaults(['param', 'controller-default', 'param2' => 'method-default']);
Route::get('my-default-route/{param?}/{param2?}/{param3?}', [MyController::class, 'myDefaultMethod'])->setDefaults(['param', 'controller-default', 'param2' => 'method-default-first', 'param3' => 'method-default-second']);
Route::get('my-override-route/{param?}', [MyController::class, 'myOverrideMethod'])->setDefaults(['param', 'method-default']);

使用Trashed

您可以在类或方法上使用 WithTrashed 注解来启用模型 WithTrashed 绑定。如果是在类级别应用,您可以使用 WithTrashed(false) 显式覆盖该行为。

use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\WithTrashed;

#[WithTrashed]
class MyController extends Controller
{
    #[Get('my-get-route/{param}')]
    #[WithTrashed]
    public function myGetMethod($param)
    {
    }

    #[Post('my-post-route/{param}')]
    #[WithTrashed(false)]    
    public function myPostMethod($param)
    {
    }

    #[Get('my-default-route/{param}')]
    public function myDefaultMethod($param)
    {
    }    
}

这些注解将自动注册这些路由

Route::get('my-get-route/{param}', [MyController::class, 'myGetMethod'])->WithTrashed();
Route::post('my-post-route/{param}', [MyController::class, 'myPostMethod'])->withTrashed(false);
Route::get('my-default-route/{param}', [MyController::class, 'myDefaultMethod'])->withTrashed();

测试

composer test

更新日志

有关最近变更的详细信息,请参阅 更新日志

贡献

有关详细说明,请参阅 贡献指南

安全漏洞

请查阅我们关于如何报告安全漏洞的 安全策略

致谢

许可证

MIT 许可证 (MIT)。请参阅 许可证文件 获取更多信息。