tlr / enum
基于myclabs/enum针对laravel优化的enum包
Requires
- php: ^8.0.0
- ext-json: *
- danielstjules/stringy: ^3.1
Requires (Dev)
- mockery/mockery: ^1.2
- phpunit/php-code-coverage: ^9.0
- phpunit/phpunit: ^9.0
- squizlabs/php_codesniffer: ^3.4
Suggests
- laravel/framework: Some version of laravel is required for this to be used.
- laravel/nova: Required to use the enum Field type.
This package is auto-updated.
Last update: 2021-07-26 15:20:55 UTC
README
弃用
** 此包已被弃用,因为PHP 8.1将支持原生的/基本枚举类型,并解决了此包旨在解决的90%的问题。**
源代码将无限期保留,供已使用此库的人使用。
简介
一个原始的PHP枚举/标志库。在很大程度上基于myclabs/php-enum(我没有分叉,因为我对核心类做了一些细微的修改)。
支持Laravel(以及Nova)。
主要功能
- 可实例化、可类型提示的枚举。
- 标志枚举类型(用于与标志和掩码一起使用/操作)。
针对Laravel的主要功能
- 枚举验证规则。
- 从枚举中获取显示值的有用工具。
- 用于Eloquent的使用获取器和设置器。
- 为Laravel Nova提供的简单枚举字段。
安装
composer require tlr/enum
如果您使用的是Laravel且启用了包自动检测,那么您已经设置好了。如果没有,请将 Tlr\Phpnum\Laravel\EnumServiceProvider
添加到您的已加载服务提供者。
什么是枚举?
一个枚举值 - 它是代表在具体值列表中的一个可能值的值。它允许您定义一个特定的值列表,并确信您的枚举符合该列表。
考虑以下函数
function createNewArticle(string $type) { // ... }
可以将任何字符串传递给此函数 - 'article'、'recipe'、'monkeys',尽管处理许多可能类型可能很简单,但您始终必须处理不存在的任何类型。
枚举允许您从代码中定义的预定义值列表中对值进行类型提示,因此您可以确保您正在处理离散(有限)的选择值(在这种情况下是类型)。例如...
基本用法(枚举)
我个人将一个应用命名空间专门用于项目中所有枚举,并且为了这些示例,我将使用
App\Values
作为枚举的命名空间。
<?php namespace App\Values; use Tlr\Phpnum\Enum; class ArticleType extends Enum { const BLOG_POST = 'blog'; const REVIEW = 'review'; const RECIPE = 'recipe'; const CODE_SAMPLE = 'code'; }
use App\Values\ArticleType; $type = ArticleType::REVIEW(); $type->value(); // 'review' $type->is($type); // true $type->is(ArticleType::RECIPE()); // false $typeFromDb = new ArticleType('review'); $type->is($typeFromDb); // true new ArticleType('bleh'); // throws exception - not in enum // ... function createNewArticle(ArticleType $type) { if($type->is(ArticleType::RECIPE())) { // do something here... } switch($type->value()) { case ArticleType::RECIPE(): case ArticleType::BLOG_POST(): break; } }
完整文档(枚举)
设置
您可以通过三种方式声明枚举的值
如上所述 - 使用const声明在声明类时。这可能是声明枚举的“默认”方式。
class ArticleType extends Enum { const BLOG_POST = 'blog'; const REVIEW = 'review'; const RECIPE = 'recipe'; const CODE_SAMPLE = 'code'; }
在$enum
静态变量中。
class ArticleType extends Enum { protected static $enum = [ 'BLOG_POST' = 'blog', 'REVIEW' = 'review', 'RECIPE' = 'recipe', 'CODE_SAMPLE' = 'code', ]; }
重写generateEnums
方法(默认实现定义了上述两种声明值的方式)。这可以用于从数据库等加载值,应该小心使用。此方法将仅调用一次,因为其结果被缓存。
class ArticleType extends Enum { /** * Get the values for the enum * * @return array */ public static function generateEnums() : array { return [ 'BLOG_POST' = 'blog', 'REVIEW' = 'review', 'RECIPE' = 'recipe', 'CODE_SAMPLE' = 'code', ]; } }
实例化枚举
枚举实例化的两种主要方式是
您可以使用声明的任何枚举名称来静态实例化枚举实例。这在手动声明值时很有用,例如将其保存到数据库中。
ArticleType::REVIEW();
您可以将枚举的值提供给构造函数。这在从数据库加载数据或从用户获取输入时很有用。
new ArticleType($request->input('article_type'));
您还可以使用all静态方法获取所有实例化的枚举列表
foreach(ArticleType::all() as $name => $enum) { echo "{$name} : {$enum->value()}"; }
获取枚举信息
您可以使用辅助方法从枚举中获取各种信息
$type = ArticleType::CODE_SAMPLE(); $type->value(); // 'code' $type->name(); // 'CODE_SAMPLE' $type->friendlyName(); // 'Code Sample'
可以使用friendlyName方法将值显示给用户。它将尝试将键名转换为单词,并首字母大写
。您可以通过两种方式覆盖此方法
覆盖friendlifier
静态方法。它将传递枚举的键名,并应返回其友好值。
protected static function friendlifier(string $name) : string;
覆盖friendlyNames
静态方法,该方法应返回一个数组/映射,其中左侧是友好名称,右侧是普通值。
public static function friendlyNames() : array { return [ return [ 'Blog Post' = 'blog', 'Review' = 'review', 'Recipe' = 'recipe', 'Code Sample' = 'code', ]; ]; }
比较枚举
您可以使用is
或equals
方法(它们是相同的,两者都包含在内,以便在编写代码时更有语法/英语感)将任何枚举与另一个枚举进行比较。
$type = $request->input('type'); // 'review' (new ArticleType($type))->is(ArticleType::REVIEW()); // true
此比较考虑了枚举的类名以及其值,因此不同的多个枚举不能与原始枚举进行交叉比较。
ArticleType::RECIPE()->is(SharedItemType::RECIPE()); // false
标志枚举
基本说明
标志(或位掩码)是许多编程语言的常见特性——PHP在标准库中使用一些位掩码常量——例如,请参见json_encode
的第二个参数的例子。
它们是一种枚举,其中每个值都是2的幂次(1,2,4,8,16等)。虽然标志在技术上只是一个整数值,但如果你用二进制表示这些整数,你会得到一些有趣的东西
1 : 00001
2 : 00010
4 : 00100
8 : 01000
16 : 10000
如果你为每个位位置(从右到左)分配一个意义,那么你就有一种方法使用整数值来存储一组开关或标志。你可以将它们链接在一起——例如,整数5
是00101
——或1和4的组合;整数31是所有这些的组合;整数0是没有。
起初这可能会有些复杂,但一旦你对此有了一定的了解,标志就可以是一个非常强大的工具,允许你在单个参数中传递完整的选项开关集,而不会丢失任何上下文意义。在上面的例子中,我们可以为值分配一些名称(在这里,使用json_encode
的例子——写作时的正确值)。
1 : 00001 : JSON_HEX_TAG
2 : 00010 : JSON_HEX_AMP
4 : 00100 : JSON_HEX_APOS
8 : 01000 : JSON_HEX_QUOT
16 : 10000 : JSON_FORCE_OBJECT
您可以通过|
运算符将前三个传递给json_encode
,如下所示
$flag = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS; json_encode([], $flag);
您可以使用&
运算符检查标志是否匹配可能的标志值
$flag & JSON_HEX_AMP; // true $flag & JSON_FORCE_OBJECT; // false
当然,您可以使用===
检查它是否与确切集合匹配
$flag === JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS; // true $flag === JSON_HEX_TAG; // false
您可以使用位运算符和标志做更多的事情,但这些是基础知识。
注意
一旦定义了标志值,在未来版本的应用程序中就不能更改。如果PHP决定删除JSON_HEX_AMP
并添加一个新的可能开关,例如JSON_HEX_SPACE
,他们必须将JSON_HEX_SPACE
添加到值列表的末尾,并简单地不允许JSON_HEX_AMP
的值,或者如果传递了它就忽略它(即,它会弃用值2
)。
标志应该用于不经常更改的核心事物。
标志和枚举
json_encode
选项只是设置为特定值的常量(如上所述的2的幂)。使用此类包的标志的优点是,标志、值和验证都封装在一个对象中,使得定义、引用、验证和比较更容易。
上面使用的Flag
类和Enum
类共享相同的基类,因此几乎所有上面的内容都适用,只是定义上有一点差异,以及一些额外的比较方法。
定义标志
以一个非常简单的权限集为例。
class Permission extends Flag { protected static $flags = [ 'MANAGE_STAFF', 'MANAGE_RESEARCH_PROJECTS', 'VIEW_SECRET_RESEARCH_PROJECTS', 'VIEW_RESEARCH_REPORTS', ]; } // in some code $user->permissions = Permission::MANAGE_STAFF(); $reporter->permissions = Permission::VIEW_RESEARCH_REPORTS();
使用标志的掩码
以下都是相同的
$user->permissions = Permission::MANAGE_STAFF(); $user->permissions = new Permission(0b0001); $user->permissions = new Permission(1); // although you would probably get this from some user input.
如果我们只能设置用户能够做上述其中之一,那么将会有些限制。我们可以像这样给一个值分配多个标志
// The following are equivalent: $user->permissions = Permission::combineFlags([ Permission::VIEW_SECRET_RESEARCH_PROJECTS(), Permission::VIEW_RESEARCH_REPORTS(), ]); $user->permissions = new Permission( Permission::VIEW_SECRET_RESEARCH_PROJECTS()->value() | Permission::VIEW_RESEARCH_REPORTS() ); $user->permissions = new Permission(0b1100); $user->permissions = new Permission(12);
比较标志
除了枚举比较方法(如$enum->is($other)
)之外,还有一些专门针对标志的方法。
// @todo - 比较
// @todo - 读取
Laravel
// @todo
验证规则
// @todo
Nova字段
// @todo