annotation/routing 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

annotation/routing

Composer 安装命令:

composer require annotation/routing

包简介

Use PHP 8 attributes to register routes in a Laravel Application.

README 文档

README

Laravel Annotation Routing

Use PHP 8 attributes to register routes in a Laravel Application.

GitHub Tag Total Downloads Packagist Version Packagist PHP Version Support Packagist License

Table of Contents
  1. Quick Start
  2. Installation
  3. Usage
    1. Basic Usage
    2. Advanced Usage
    3. Specifying Prefix
    4. Specify Named
    5. Specifying Middleware
    6. Specifying Domain
    7. Specify Config
    8. Specifying ScopeBindings
    9. Specifying Where
    10. Specifying Group
    11. Specifying Defaults
    12. Specifying WithTrashed
    13. Specifying Fallback
  4. Contributing
  5. Contributors
  6. License

Quick Start

This package provides attributes to automatically register routes. Here's a quick example:

namespace App\Http\Controllers\Backend;

use Annotation\Route\Prefix;

#[Prefix('backend')]
class BaseController extends Controller
{}
namespace App\Http\Controllers\Backend;

use Annotation\Route\Domain;
use Annotation\Route\Group;
use Annotation\Route\Prefix;
use Annotation\Route\Route\Get;

#[Group(prefix: 'home')]
class HomeController extends BaseController
{
    #[Get('index', 'index')]
    public function index(Request $request)
    {
        //
    }
}

This attribute will automatically register this route:

use App\Http\Controllers\Backend\HomeController;
use Illuminate\Support\Facades\Route;

Route::prefix('backend/home')
    ->name('backend.home.')
    ->group(function () {
        Route::get('index', [HomeController::class, 'index'])->name('index');
    });

Installation

You can install the package via Composer:

composer require annotation/routing

You can publish the config file with:

php artisan vendor:publish --provider="Annotation\Routing\RouteServiceProvider" --tag="config"

This is the contents of the published config file:

return [

    /*
    |--------------------------------------------------------------------------
    | Automatic Registration Routes
    |--------------------------------------------------------------------------
    |
    | Automatic registration of routes will only happen if this setting is `true`
    |
    */

    'enabled' => true,

    /*
    |--------------------------------------------------------------------------
    | Automatically Registered Paths
    |--------------------------------------------------------------------------
    |
    | 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',
        ],
    ],
];

[back to top]

Usage

The package provides several annotations that should be put on controller classes and methods. These annotations will be used to automatically register routes.

Basic Usage

namespace App\Http\Controllers;

use Annotation\Route\Route\Get;

class MyController extends Controller
{
    #[Get('route')]
    public function myMethod()
    {
        //
    }
}

This attribute will automatically register this route:

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

Advanced Usage

BaseController has two subclasses: HomeController and AccountController.

namespace App\Http\Controllers\Backend;

use Annotation\Route\Prefix;
use Annotation\Route\Routing\Config;
use App\Http\Controllers\Controller;

#[Prefix('backend')]
#[Config('domains.backend', 'backend.local.dev')]
class BaseController extends Controller
{}

HomeController:

namespace App\Http\Controllers\Backend;

use Annotation\Route\Prefix;
use Annotation\Route\Route\Get;

#[Prefix('home')]
class HomeController extends BaseController
{
    #[Get]
    public function index(Request $request)
    {
    }
}

This attribute will automatically register this route:

use App\Http\Controllers\Backend\HomeController;
use Illuminate\Support\Facades\Route;

Route::prefix('backend/home')
    ->name('backend.home.')
    ->domain('backend.local.dev')
    ->group(function () {
        Route::get('index', [HomeController::class, 'index'])->name('index');
    });

Route URI: http://backend.local.dev/home/index

Route Named: backend.home.index

AccountController:

namespace App\Http\Controllers\Backend;

use Annotation\Route\Group;
use Annotation\Route\Route\Get;

#[Group(prefix: 'account', domain: 'passport.local.dev', as: 'passport')]
class AccountController extends BaseController
{
    #[Get]
    public function login(Request $request)
    {
    }
}

This attribute will automatically register this route:

use App\Http\Controllers\Backend\AccountController;
use Illuminate\Support\Facades\Route;

Route::prefix('backend/account')
    ->name('backend.passport.')
    ->group(function () {
        Route::get('login', [AccountController::class, 'login'])->name('login')->domain('passport.local.dev');
    });

Route URI: http://passport.local.dev/account/login

Route Named: backend.passport.login

Specifying Prefix

You can use the Prefix annotation on a class to prefix the routes of all methods of that class.

namespace App\Http\Controllers;

use Annotation\Route\Prefix;
use Annotation\Route\Route\Get;

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

These annotations will automatically register these routes:

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

Specify Named

All HTTP verb attributes accept a parameter named name that accepts a route name.

namespace App\Http\Controllers;

use Annotation\Route\Route\Get;

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

This attribute will automatically register this route:

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

Using other HTTP verbs

We have left no HTTP verb behind. You can use these attributes on controller methods.

#[Annotation\Route\Route\Post('uri')]
#[Annotation\Route\Route\Put('uri')]
#[Annotation\Route\Route\Patch('uri')]
#[Annotation\Route\Route\Delete('uri')]
#[Annotation\Route\Route\Options('uri')]

Using multiple verbs

To register a route for all verbs, you can use the Any attribute:

#[Annotation\Route\Route\Any('uri')]

To register a route for a few verbs at once, you can use the Route attribute directly:

#[Annotation\Route\Route(['put', 'patch'], 'uri')]

Specifying Middleware

All HTTP verb attributes accept a parameter named middleware that accepts a middleware class or an array of middleware classes.

namespace App\Http\Controllers;

use Annotation\Route\Route\Get;

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

This annotation will automatically register this route:

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

To apply middleware on all methods of a class you can use the Middleware attribute. You can mix this with applying attribute on a method.

namespace App\Http\Controllers;

use Annotation\Route\Route\Get;
use Annotation\Route\Middleware;
use Annotation\Route\WithoutMiddleware;

#[Middleware(GlobalMiddleware::class)]
class MyController
{
    #[Get('route', middleware: MyMiddleware::class)]
    public function myMethod()
    {
    }
    
    #[Get('global-middleware')]
    public function globalMiddleware()
    {
    }
    
    #[Get('without-middleware', withoutMiddleware: GlobalMiddleware::class)]
    // or
    // #[WithoutMiddleware(GlobalMiddleware::class)]
    public function withoutMiddleware()
    {
    }
}

These annotations will automatically register these routes:

Route::get('route', [MyController::class, 'myMethod'])->middleware([GlobalMiddleware::class, MyMiddleware::class]);
Route::get('global-middleware', [MyController::class, 'globalMiddleware'])->middleware(GlobalMiddleware::class);
Route::get('without-middleware', [MyController::class, 'withoutMiddleware']);

Specifying Domain

You can use the Domain annotation on a class to prefix the routes of all methods of that class.

namespace App\Http\Controllers;

use Annotation\Route\Domain;
use Annotation\Route\Route\Get;

#[Domain('subdomain.localhost')]
class MyController
{
    #[Get('route')]
    public function myMethod()
    {
    }
}

These annotations will automatically register these routes:

Route::get('route', [MyController::class, 'myMethod'])->domain('subdomain.localhost');

Specify Config

There maybe a need to define a domain from a configuration file, for example where your subdomain will be different on your development environment to your production environment.

// config/app.php
return [
    'url' => env('APP_URL', 'http://localhost'),
];
namespace App\Http\Controllers;

use Annotation\Route\Route\Get;
use Annotation\Route\Routing\Config;

#[Config('app.url', '127.0.0.1')]
class MyController
{
    #[Get('route')]
    public function myMethod()
    {
    }
}

When this is parsed, it will get the value of app.url from the config file and register the route as follows:

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

If app.url does not exist and register the route as follows:

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

Specifying ScopeBindings

When implicitly binding multiple Eloquent models in a single route definition, you may wish to scope the second Eloquent model such that it must be a child of the previous Eloquent model.

By adding the ScopeBindings annotation, you can enable this behaviour:

namespace App\Http\Controllers;

use Annotation\Route\Route\Get;
use Annotation\Route\ScopeBindings;

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

This is akin to using the ->scopeBindings() method on the route registrar manually:

Route::get('users/{user}/posts/{post}', [MyController::class, 'myMethod'])->scopeBindings();

By default, Laravel will enabled scoped bindings on a route when using a custom keyed implicit binding as a nested route parameter, such as /users/{user}/posts/{post:slug}.

To disable this behaviour, you can pass false to the attribute:

#[Annotation\Route\ScopeBindings(false)]

This is the equivalent of calling ->withoutScopedBindings() on the route registrar manually.

You can also use the annotation on controllers to enable implicitly scoped bindings for all its methods. For any methods where you want to override this, you can pass false to the attribute on those methods, just like you would normally.

Specifying Where

You can use the Where annotation on a class or method to constrain the format of your route parameters.

namespace App\Http\Controllers;

use Annotation\Route\Route\Get;
use Annotation\Route\Route\Post;
use Annotation\Route\Routing\WhereAlphaNumeric;
use Annotation\Route\Where;

#[Where('custom', '[0-9]+')]
class MyController
{
    #[Get('route/{custom}')]
    public function myMethod()
    {
    }

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

These annotations will automatically register these routes:

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

For convenience, some commonly used regular expression patterns have helper attributes that allow you to quickly add pattern constraints to your routes.

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

Specifying Group

You can use the Group annotation on a class to create multiple groups with different domains and prefixes for the routes of all methods of that class.

namespace App\Http\Controllers;

use Annotation\Route\Group;
use Annotation\Route\Route\Get;

#[Group(prefix: 'domain', domain: 'domain.localhost')]
#[Group(prefix: 'subdomain', domain: 'subdomain.localhost')]
class MyController
{
    #[Get('route')]
    public function myMethod()
    {
    }
}

These annotations will automatically register these routes:

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

Specifying Defaults

You can use the Defaults annotation on a class or method to define the default values of your optional route parameters.

namespace App\Http\Controllers;

use Annotation\Route\Route\Get;
use Annotation\Route\Route\Post;
use Annotation\Route\Routing\Defaults;

#[Defaults('param', 'default')]
class MyController
{
    #[Get('route/{param?}')]
    public function myMethod($param)
    {
    }

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

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

These annotations will automatically register these routes:

Route::get('route/{param?}', [MyController::class, 'myMethod'])->setDefaults(['param' => 'default']);
Route::post('route/{param?}/{param2?}', [MyController::class, 'myPostMethod'])->setDefaults(['param' => 'default', 'param2' => 'post-default']);
Route::get('override-route/{param?}', [MyController::class, 'myOverrideMethod'])->setDefaults(['param' => 'override-default']);

Specifying WithTrashed

  • You can use the WithTrashed annotation on a class or method to enable WithTrashed bindings to the model.
  • You can explicitly override the behaviour using WithTrashed(false) if it is applied at the class level.
namespace App\Http\Controllers;

use Annotation\Route\Route\Get;
use Annotation\Route\Route\Post;
use Annotation\Route\WithTrashed;

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

    #[Post('route')]
    #[WithTrashed(false)]
    public function myPostMethod()
    {
    }

    #[Get('default-route')]
    public function myDefaultMethod()
    {
    }
}

These annotations will automatically register these routes:

Route::get('route', [MyController::class, 'myMethod'])->WithTrashed();
Route::post('route', [MyController::class, 'myPostMethod'])->withTrashed(false);
Route::get('default-route', [MyController::class, 'myDefaultMethod'])->withTrashed();

Specifying Fallback

  • You can use the Fallback annotation on a method that will be executed when no other route matches the incoming request.
namespace App\Http\Controllers;

use Annotation\Route\Fallback;

class MyController
{
    #[Fallback]
    public function __invoke(Request $request)
    {
        return '404';
    }
}

These annotations will automatically register these routes:

Route::fallback(\App\Http\Controllers\MyController::class)->name('fallback');

Resource Controllers

To register a resource controller, use the Resource attribute as shown in the example below.

  • You can use only or except parameters to manage your resource routes availability.
  • You can use parameters parameter to modify the default parameters set by the resource attribute.
  • You can use the names parameter to set the route names for the resource controller actions. Pass a string value to set a base route name for each controller action or pass an array value to define the route name for each controller action.
  • You can use shallow parameter to make a nested resource to apply nesting only to routes without a unique child identifier (index, create, store).
  • You can use apiResource boolean parameter to only include actions used in APIs. Alternatively, you can use the ApiResource attribute, which extends the Resource attribute class, but the parameter apiResource is already set to true.
  • Using Resource attribute with Domain, Prefix and Middleware attributes works as well.
namespace App\Http\Controllers;

use Annotation\Route\Prefix;
use Annotation\Route\Resource;
use Illuminate\Http\Request;

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

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

    public function show($comment)
    {}

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

The attribute in the example above will automatically register following routes:

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

Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

[back to top]

Contributors

Thanks goes to these wonderful people:

contrib.rocks image

Contributions of any kind are welcome!

[back to top]

License

Distributed under the MIT License (MIT). Please see License File for more information.

[back to top]

annotation/routing 适用场景与选型建议

annotation/routing 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 9.62k 次下载、GitHub Stars 达 0, 最近一次更新时间为 2025 年 05 月 23 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「route」 「routing」 「annotation」 「attribute」 「laravel」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 annotation/routing 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 annotation/routing 我们能提供哪些服务?
定制开发 / 二次开发

基于 annotation/routing 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 9.62k
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 20
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-05-23