sancherie/annotations

为 Laravel 框架的路由提供注释。

9.0.0 2022-03-24 06:59 UTC

README

Build Status Total Downloads Latest Stable Version Latest Unstable Version License

注释

安装

如果您已将顶级命名空间更改为类似 'MyCompany' 的内容,那么您将使用新的命名空间而不是 'App'。

首先通过 Composer 安装此包。编辑您的项目 composer.json 文件以要求 laravelcollective/annotations

"require": {
    "laravelcollective/annotations": "8.0.\*"
}

接下来,从终端更新 Composer

composer update

Composer 完成后,您需要在 app/Providers/AnnotationsServiceProvider.php 中创建一个 Service Provider。

<?php namespace App\Providers;

use Collective\Annotations\AnnotationsServiceProvider as ServiceProvider;

class AnnotationsServiceProvider extends ServiceProvider {

    /**
     * The classes to scan for event annotations.
     *
     * @var array
     */
    protected $scanEvents = [];

    /**
     * The classes to scan for route annotations.
     *
     * @var array
     */
    protected $scanRoutes = [];

    /**
     * The classes to scan for model annotations.
     *
     * @var array
     */
    protected $scanModels = [];

    /**
     * Determines if we will auto-scan in the local environment.
     *
     * @var bool
     */
    protected $scanWhenLocal = false;

    /**
     * Determines whether or not to automatically scan the controllers
     * directory (App\Http\Controllers) for routes
     *
     * @var bool
     */
    protected $scanControllers = false;

    /**
     * Determines whether or not to automatically scan all namespaced
     * classes for event, route, and model annotations.
     *
     * @var bool
     */
    protected $scanEverything = false;

    /**
     * Determines whether to use attributes for scanning.
     *
     * @var bool
     */
    protected $useAttribute = false;
    
}

最后,将新提供者添加到 config/app.phpproviders 数组中

  'providers' => [
    // ...
    App\Providers\AnnotationsServiceProvider::class
    // ...
  ];

这不会替换 RouteServiceProvider,它仍然需要,因为它处理路由缓存等的加载。

设置扫描

将事件处理器类添加到 protected $scanEvents 数组中以扫描事件注释。

    /**
     * The classes to scan for event annotations.
     *
     * @var array
     */
    protected $scanEvents = [
      App\Handlers\Events\MailHandler::class,
    ];

将控制器添加到 protected $scanRoutes 数组中以扫描路由注释。

    /**
     * The classes to scan for route annotations.
     *
     * @var array
     */
    protected $scanRoutes = [
      App\Http\Controllers\HomeController::class,
    ];

将模型添加到 protected $scanModels 数组中以扫描模型注释。

    /**
     * The classes to scan for model annotations.
     *
     * @var array
     */
    protected $scanModels = [
      'App\User',
    ];

或者,您可以将 protected $scanEverything 设置为 true 以自动扫描您应用程序命名空间内的所有类。 注意: 这可能会根据您应用程序的大小增加执行扫描器所需的时间。

您可以通过使用 php artisan event:scanphp artisan route:scanphp artisan model:scan 分别手动扫描事件处理器、控制器和模型。在本地环境中,您可以通过设置 protected $scanWhenLocal = true 自动扫描它们。

事件注释

@Hears

@Hears 注释为特定事件注册一个事件监听器。对任何方法使用 @Hears("SomeEventName") 注释将注册一个事件监听器,当 SomeEventName 事件被触发时将调用该方法。

<?php namespace App\Handlers\Events;

use App\User;

class MailHandler {

  /**
   * Send welcome email to User
   * @Hears("UserWasRegistered")
   */
  public function sendWelcomeEmail(User $user)
  {
    // send welcome email to $user
  }

}

如果您更喜欢使用属性,请将 $useAttribute 设置为 true 并这样做。请注意,与注释不同,需要使用使用语句才能使用属性。

<?php namespace App\Handlers\Events;

use App\User;
use Collective\Annotations\Events\Attributes\Attributes\Hears;

class MailHandler {

  #[Hears('UserWasRegistered')]
  public function sendWelcomeEmail(User $user)
  {
    // send welcome email to $user
  }

}

路由注释

路由注释非常强大,但是您的路由定义顺序可能会影响您的应用程序匹配特定路由的方式,特别是任何通配符路由。如果 protected $scanEverything 设置为 true,您将无法控制路由定义的顺序。

@Get

@Get 注释为 HTTP GET 请求注册一个路由。

<?php namespace App\Http\Controllers;

class HomeController {

  /**
   * Show the Index Page
   * @Get("/")
   */
  public function getIndex()
  {
    return view('index');
  }

}

您也可以设置路由名称。

  /**
   * @Get("/", as="index")
   */

   #[Get(path: '/', as: 'index')]

... 或者中间件。

  /**
   * @Get("/", middleware="guest")
   */

   #[Get(path: '/', middleware: 'guest')]

... 或者两者都设置。

  /**
   * @Get("/", as="index", middleware="guest")
   */

   #[Get(path: '/', as: 'index', middleware: 'guest')]

以下是一个使用所有可用的 @Get 注解参数的示例

  /**
   * @Get("/profiles/{id}", as="profiles.show", middleware="guest", domain="foo.com", where={"id": "[0-9]+"}, no_prefix="true")
   */

   #[Get(path: '/profiles/{id}', as: 'profiles.show', middleware: 'guest', domain: 'foo.com', where: ['id' => '[0-9]+'], noPrefix: true)]

no_prefix 允许忽略添加到该控制器中路由的任何前缀,对这条特定路由进行忽略。

@Post, @Options, @Put, @Patch, @Delete, @any

@Post@Options@Put@Patch@Delete@Any 注释的语法与 @Get 注释相同,但它将注册一个针对相应 HTTP 动词的路由,而不是 GET 动词。

@Middleware

除了在路由定义标签中(如 @Get@Post 等)内联定义中间件外,您还可以使用 @Middleware 标签。它可以用于单个方法

  /**
   * Show the Login Page
   *
   * @Get("login")
   * @Middleware("guest")
   */
  public function login()
  {
    return view('index');
  }

  #[Get(path: 'login')]
  #[Middleware(name: 'guest')]
  public function login()
  {
    return view('index');
  }

或者整个控制器,使用与您在 Laravel 其他地方可以使用的相同的 only/exclude 过滤语法

/**
 * @Middleware("guest", except={"logout"}, prefix="/your/prefix")
 */
class AuthController extends Controller {

  /**
   * Log the user out.
   *
   * @Get("logout", as="logout")
   * @Middleware("auth")
   *
   * @return Response
   */
  public function logout()
  {
    $this->auth->logout();

    return redirect( route('login') );
  }

}

#[Middleware(name: 'guest', except: ['logout'], prefix: '/your/prefix')]
class AuthController extends Controller {

  #[Get(path: 'logout', as: 'logout')]
  #[Middleware(name: 'auth')]
  public function logout()
  {
    $this->auth->logout();

    return redirect( route('login') );
  }

}

@Resource

在控制器上使用 @Resource 注释可以轻松设置资源控制器。

<?php
/**
 * @Resource('users')
 */
class UsersController extends Controller {
  // index(), create(), store(), show($id), edit($id), update($id), destroy($id)
}

您可以使用 onlyexcept 参数,就像您可以使用常规 Route::resource() 命令一样。

  /**
   * @Resource('users', only={"index", "show"})
   */

   #[Resource('users', only: ['index', 'show'])]

您还可以指定每个资源方法的路由名称。

  /**
   * @Resource('users', names={"index"="user.all", "show"="user.view"})
   */

   #[Resource('users', names: ['index' => 'user.all', 'show' => 'user.view'])]

@Controller

在控制器上使用 @Controller 注解,可以设置其中包含的路由的各种选项

<?php
/**
 * @Controller(prefix="admin", domain="foo.com")
 */
class AdminController extends Controller {
  // All routes will be prefixed by admin/
}

#[Controller(prefix: 'admin', domain: 'foo.com')]
class AdminController extends Controller {
  // All routes will be prefixed by admin/
}

扫描控制器目录

要递归扫描整个控制器命名空间(App\Http\Controllers),可以将 $scanControllers 标志设置为 true。

它将自动将 App 调整为您的应用程序命名空间。

    $scanControllers = true;

高级

如果您想使用任何逻辑将类添加到扫描列表中,可以重写 routeScans()eventScans() 方法。

以下是一个示例,如果当前环境是 local,则将控制器添加到扫描列表中

public function routeScans() {
    $classes = parent::routeScans();

    if ( $this->app->environment('local') ) {
        $classes = array_merge($classes, [App\Http\Controllers\LocalOnlyController::class]);
    }

    return $classes;
}

扫描命名空间

您可以使用 getClassesFromNamespace( $namespace ) 方法递归地将命名空间添加到列表中。这将扫描给定的命名空间。它仅适用于 app 目录中的类,并且依赖于 PSR-4 命名空间标准。

这是 $scanControllers 标志与控制器目录一起使用的。

以下是一个示例,基于上一个示例 - 添加一个仅限本地的完整命名空间。

public function routeScans() {
    $classes = parent::routeScans();

    if ( $this->app->environment('local') ) {
        $classes = array_merge(
            $classes,
            $this->getClassesFromNamespace( App\Http\Controllers\Local::class )
        );
    }

    return $classes;
}

模型注释

您可以使用注解自动将模型绑定到路由参数,使用 路由模型绑定。为此,请使用 @Bind 注解。

/**
 * @Bind("users")
 */
class User extends Eloquent {
  //
}

#[Bind('users')]
class User extends Eloquent {
  //
}

这与调用 Route::model('users', 'App\Users') 相当。

自定义注释

请注意:版本 8.1 中已更新命名空间,以允许 PHP 8 属性支持。

如果您想注册自己的注解,创建一个包含 Collective\Annotations\Routing\Meta 的子类的命名空间 - 假设为 App\Http\Annotations
(对于版本 8.0 及更早版本,扩展 Collective\Annotations\Routing\Annotations\Annotations\Annotation

然后,在您的注解服务提供程序中,重写 addRoutingAnnotations( RouteScanner $scanner ) 方法,并注册您的路由注解命名空间

<?php namespace App\Providers;

use Collective\Annotations\Routing\Scanner as RouteScanner;
# For version 8.0 and older use this instead of the above:
# use Collective\Annotations\Routing\Annotations\Scanner as RouteScanner;

/* ... then, in the class definition ... */

    /**
     * Add annotation classes to the route scanner
     *
     * @param RouteScanner $namespace
     */
    public function addRoutingAnnotations( RouteScanner $scanner )
    {
      $scanner->addAnnotationNamespace( 'App\Http\Annotations' );
    }

您的注解类必须具有 @Annotation 类注解(请参阅以下示例)。

对于事件注解也有一个等效方法:addEventAnnotations( EventScanner $scanner )

自定义注解示例

以下是一个示例,创建一个 @Auth 注解。它提供了与使用注解 @Middleware("auth") 相同的功能。

在一个命名空间中 - 在此示例中,App\Annotations

<?php namespace App\Http\Annotations;

use Collective\Annotations\Routing\Meta;
# For version 8.0 and older use this instead of the above:
# use Collective\Annotations\Routing\Annotations\Annotations\Annotation;
use Collective\Annotations\Routing\Annotations\MethodEndpoint;
use ReflectionMethod;

/**
 * @Annotation
 */
class Auth extends Annotation {

  /**
   * {@inheritdoc}
   */
  public function modify(MethodEndpoint $endpoint, ReflectionMethod $method)
  {
    if ($endpoint->hasPaths())
    {
      foreach ($endpoint->getPaths() as $path)
      {
        $path->middleware = array_merge($path->middleware, (array) 'auth');
      }
    }
    else
    {
      $endpoint->middleware = array_merge($endpoint->middleware, (array) 'auth');
    }
  }

}