使用属性添加值
发布时间 作者 Steve McDougall
PHP 属性是在语言的 8.0 版本中添加的,对许多开发者来说,这是一个误称。它们的优点是什么,我该如何使用它们?
自从它们发布以来,我一直问自己这个问题,直到最近我才找到它们的用例。在进行一个需要 API 访问的项目时,我决定使用 Laravel Breeze 并围绕 Sanctum 的 API 令牌添加一个包装器,而不是使用 Jetstream。这让我踏上了弄清楚如何充分利用令牌本身的道路。
如果您以前使用过 Laravel Jetstream,您会知道您会在服务提供者中注册权限和令牌能力。如果您有一个简单的 API,这是一个可以接受的方法。但是,我的需求比这更复杂,但还不至于需要设置 OAuth。
相反,我认为我会利用 PHP 本地的 enum
结构,这是我在存储用户角色时的常用方法之一。但是枚举不够详细,这带来了问题。然后我偶然发现了一个由 Rob Fonesca撰写的 精彩而鼓舞人心的教程。他写了如何使用属性扩展 PHP 的枚举。他的用例与我的不同,但哇,我的眼睛终于看到了用例!
我需要创建一组权限,允许用户创建的 API 令牌具有特定能力。但是,我希望能够让用户理解他们正在设置的权限。我的第一步是创建一个基本的枚举
enum Permission: string{ case ADMIN = 'ADMIN'; case EDITOR = 'EDITOR';}
这两种类型的权限有明确的区分。其中一个应该能够做任何事情,而另一个则具有更有限的访问权限。这时我回顾了 Rob 的教程,并实现了 description 属性。
use Attribute; #[Attribute]final readonly class Description{ public function __construct( public string $description, ) {}}
我希望我的属性是不可变的,这样就不会有任何东西可以更改它们。因此,一个只读类在这里很有意义,不是说我需要借口……
现在我所要做的就是将属性添加到我的枚举中
enum Permission: string{ #[Description('Admin users can perform any action.')] case ADMIN = 'ADMIN'; #[Description('Editor users have the ability to read, and update.')] case EDITOR = 'EDITOR';}
这提供了我希望用户能够理解的有关每个权限的信息。但是,我随后遇到了另一个问题。我如何存储能力?我知道枚举只允许字符串或整数作为用例,那我该怎么办?
同样,我意外地在属性中找到了答案。如果属性可以用来添加描述,它也可以用来添加其他东西。因此,我创建了另一个名为 Abilities
的属性,它将接收一个字符串数组,以自由形式的方式列出它们。
#[Attribute]final readonly class Abilities{ public function __construct( public array $abilities, ) {}}
现在我所要做的就是将它添加到我的枚举中,我可以将令牌设置为枚举并使用反射来提取能力,同时保存到数据库中。
enum Permission: string{ #[Key('admin')] #[Description('Admin users can perform any action.')] #[Abilities(['create','read','update','delete'])] case ADMIN = 'ADMIN'; #[Key('editor')] #[Description('Editor users have the ability to read, and update.')] #[Abilities(['read','update'])] case EDITOR = 'EDITOR';}
以下是我最终得到的结果。我希望有一个引用键,以便有一个更好的字符串来引用。现在我可以按照 Rob 的教程来实现一种访问这些属性的方法。
trait CanAccessAttributes{ public static function abilities(BackedEnum $enum): array { $reflection = new ReflectionClassConstant( class: self::class, constant: $enum->name, ); $attributes = $reflection->getAttributes( name: Abilities::class, ); if (0 === count($attributes)) { return [Str::headline( value: strval($enum->value) )]; } return $attributes[0]->newInstance()->abilities; } public static function key(BackedEnum $enum): string { $reflection = new ReflectionClassConstant( class: self::class, constant: $enum->name, ); $attributes = $reflection->getAttributes( name: Key::class, ); if (0 === count($attributes)) { return Str::headline( value: $enum->value ); } return $attributes[0]->newInstance()->key; } public static function description(BackedEnum $enum): string { $reflection = new ReflectionClassConstant( class: self::class, constant: $enum->name, ); $attributes = $reflection->getAttributes( name: Description::class, ); if (0 === count($attributes)) { return Str::headline( value: $enum->value ); } return $attributes[0]->newInstance()->description; }}
一个简单的 trait,允许我访问所有我想要的属性。因为我的应用程序使用的是 Inertia,我所要做的就是在 HandlesInertia
中间件中传递一个资源,这样我的 UI 就可以在任何地方详细地访问这些权限。我决定为此创建一个 API 资源,这样我就可以一致地处理格式。
/** * @property-read Permission $resource */final class PermissionResource extends JsonResource{ public function toArray(Request $request): array { return [ 'key' => $this->resource->key($this->resource), 'name' => $this->resource->name, 'value' => $this->resource->value, 'description' => $this->resource->description($this->resource), 'abilities' => $this->resource->abilities($this->resource), ]; }}
我终于找到了属性的用例,同时构建了我认为是在您的应用程序中注册这些数据的最佳方式。
技术作家,就职于 Laravel 新闻,开发者倡导者,就职于 Treblle。API 专家,经验丰富的 PHP/Laravel 工程师。 YouTube 直播主。