用户名/enum-helper

简单的、有偏见的、无框架依赖的PHP 8.1枚举助手

1.0.0 2024-01-23 16:04 UTC

This package is auto-updated.

Last update: 2024-09-23 17:32:31 UTC


README

Latest Version on Packagist Pest Tests number GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

一个简单的、有偏见的PHP 8.1枚举助手集合,灵感来自 archtechx/enumsBenSampo/laravel-enum
此包无框架依赖,但如果您使用Laravel,建议使用此链接包 suleymanozev/laravel-enum-helper

功能摘要

  • 可调用案例:通过静态调用获取枚举的值
  • 通过名称或值构建枚举:`from()`、`tryFrom()`、`fromName()`、`tryFromName()`、`fromValue()`、`tryFromValue()` 方法
  • 枚举检查:`isPure()`、`isBacked()`、`has()`、`hasName()`、`hasValue()` 方法
  • 枚举相等性:`is()`、`isNot()`、`in()`、`notIn()` 方法
  • 名称:获取案例名称列表的方法(`names()`、`namesByValue()`)
  • :获取案例值列表的方法(`values()`、`valuesByName()`)
  • 唯一ID:从实例获取唯一标识符或从标识符获取实例(`uniqueId()`、`fromUniqueId()`)
  • 描述 & 翻译:向枚举添加描述(可选翻译)(`description()`、`descriptions()`、`descriptionsByName()`、`descriptionsByValue()`、`nullableDescriptionsByValue()`)

安装

需要PHP 8.1+。

composer require suleymanozev/enum-helper

用法

您可以使用所需的特性,但为了方便,您可以使用包含(`EnumInvokable`、`EnumFroms`、`EnumNames`、`EnumValues`、`EnumInspection`、`EnumEquality`)的`EnumHelper`特性。
`EnumDescription`和`EnumUniqueId`与`EnumHelper`分开,因为它们涵盖了边缘情况。

该助手支持纯枚举(例如`PureEnum`、`PascalCasePureEnum`)和`BackedEnum`(例如`IntBackedEnum`、`StringBackedEnum`)。

在所有示例中,我们将使用下面描述的类

use Suleymanozev\EnumHelper\EnumHelper;

// Pure enum
enum PureEnum
{
    use EnumHelper;
    
    case PENDING;
    case ACCEPTED;
    case DISCARDED;
    case NO_RESPONSE;
}
enum PascalCasePureEnum
{
    use EnumHelper;
    
    case Pending;
    case Accepted;
    case Discarded;
    case NoResponse;
}

// BackedEnum
enum StringBackedEnum: string
{
    use EnumHelper;
    
    case PENDING = 'P';
    case ACCEPTED = 'A';
    case DISCARDED = 'D';
    case NO_RESPONSE = 'N';
}
enum IntBackedEnum: int
{
    use EnumHelper;
    
    case PENDING = 0;
    case ACCEPTED = 1;
    case DISCARDED = 2;
    case NO_RESPONSE = 3;
}

该包与以UPPER_CASE、snake_case和PascalCase编写的案例一起工作

跳转至

可调用案例

此助手允许您通过静态调用(`PureEnum::pending()`)和实例调用(`$status()`)来获取`BackedEnum`的值或纯枚举的名称。
一个好的做法是以camelCase模式调用方法,但您可以在所有情况下调用枚举`::STATICALLY()`、`::statically()`或`::Statically()`。

IntBackedEnum::PENDING // PureEnum enum instance
IntBackedEnum::pending(); // 0

这样允许您在数组键定义中使用枚举调用

'statuses' => [
    PureEnum::pending() => 'some configuration',
...

或在与数据库交互中使用`$db_field_definition->default(PureEnum::pending())`或调用实例以获取原始值

public function updateStatus(int $status): void;

$task->updateStatus(IntBackedEnum::pending());

示例使用静态调用获取原始值

// Pure Enum
PureEnum::noResponse(); // 'NO_RESPONSE'
PureEnum::NO_RESPONSE(); // 'NO_RESPONSE'
PureEnum::NoResponse(); // 'NO_RESPONSE'

// Pure Enum with PascalCase
PascalCasePureEnum::noResponse(); // 'NoResponse'
PascalCasePureEnum::NO_RESPONSE(); // 'NoResponse'
PascalCasePureEnum::NoResponse(); // 'NoResponse'

// IntBackedEnum
IntBackedEnum::pending(); // 0

// StringBackedEnum
StringBackedEnum::pending(); // 'P'

IDE代码补全

要获得代码补全,您可以在键入枚举案例时获取自动建议,然后添加(),或者您可以在枚举类中添加phpDoc @method标签来定义所有可调用案例,如下所示

/**
 * @method static string pending()
 * @method static string accepted()
 * @method static string discarded()
 * @method static string noResponse()
 */
enum PureEnum
...

FromFromName

此辅助函数为纯枚举添加了 from()tryFrom(),为所有枚举添加了 fromValue()tryFromValue()from()tryFrom() 的别名),以及 fromName()tryFromName()

重要提示

  • BackedEnum 实例已经实现了自己的 from()tryFrom() 方法,这些方法不会被此特质覆盖。

from()

// Pure Enum
PureEnum::from('PENDING'); // PureEnum::PENDING
PascalCasePureEnum::from('Pending'); // PascalCasePureEnum::Pending
PureEnum::from('MISSING'); // ValueError Exception
// BackedEnum
StringBackedEnum::from('P'); // StringBackedEnum::PENDING
StringBackedEnum::from('M'); // ValueError Exception

tryFrom()

// Pure Enum
PureEnum::tryFrom('PENDING'); // PureEnum::PENDING
PureEnum::tryFrom('MISSING'); // null
// BackedEnum
StringBackedEnum::tryFrom('P'); // StringBackedEnum::PENDING
StringBackedEnum::tryFrom('M'); // null

fromName()

// Pure Enum
PureEnum::fromName('PENDING'); // PureEnum::PENDING
PureEnum::fromName('MISSING'); // ValueError Exception
// BackedEnum
StringBackedEnum::fromName('PENDING'); // StringBackedEnum::PENDING
StringBackedEnum::fromName('MISSING'); // ValueError Exception

tryFromName()

// Pure Enum
PureEnum::tryFromName('PENDING'); // PureEnum::PENDING
PureEnum::tryFromName('MISSING'); // null
// BackedEnum
StringBackedEnum::tryFromName('PENDING'); // StringBackedEnum::PENDING
StringBackedEnum::tryFromName('MISSING'); // null

检查

此辅助函数允许检查枚举的类型(isPure()isBacked())以及枚举是否包含某个案例名称或值(has()doesntHave()hasName()doesntHaveName()hasValue()doesntHaveValue())。

isPure()isBacked()

使用这些方法可以检查枚举实例的类型。

PureEnum::PENDING->isPure() // true
PureEnum::PENDING->isBacked() // false
IntBackedEnum::PENDING->isPure() // false
StringBackedEnum::PENDING->isBacked() // true

has()doesntHave()

has() 方法允许通过传递整数、字符串或枚举实例来检查枚举是否具有某个案例(名称或值)。为了方便,还有一个 doesntHave() 方法,它是 has() 方法的逆。

PureEnum::has('PENDING') // true
IntBackedEnum::has(10) // false
IntBackedEnum::has(1) // true
IntBackedEnum::has('1') // true
StringBackedEnum::has('ACCEPTED') // true
StringBackedEnum::has('A') // true
StringBackedEnum::doesntHave('A') // false

hasName()doesntHaveName()

hasName() 方法允许检查枚举是否具有某个案例名称。为了方便,还有一个 doesntHaveName() 方法,它是 hasName() 方法的逆。

PureEnum::hasName('PENDING') // true
PureEnum::hasName('P') // false
IntBackedEnum::hasName('ACCEPTED') // true
IntBackedEnum::hasName(1) // false
StringBackedEnum::doesntHaveName('ACDSIED') // true
StringBackedEnum::hasName('A') // false

hasValue()doesntHaveValue()

hasValue() 方法允许通过传递整数、字符串或枚举实例来检查枚举是否具有某个案例。为了方便,还有一个 doesntHaveValue() 方法,它是 hasValue() 方法的逆。

PureEnum::hasValue('PENDING') // true
PureEnum::hasValue('P') // false
IntBackedEnum::hasValue('ACCEPTED') // false
IntBackedEnum::hasValue(1) // true
StringBackedEnum::doesntHaveValue('Z') // true
StringBackedEnum::hasValue('A') // true

相等性

此辅助函数允许比较枚举实例(is()isNot())并搜索它是否存在于数组中(in()notIn())。

is()isNot()

is() 方法允许检查实例与枚举实例、案例名称或案例值的相等性。
为了方便,还有一个 isNot() 方法,它是 is() 方法的逆。

$enum = PureEnum::PENDING;
$enum->is(PureEnum::PENDING); // true
PureEnum::PENDING->is(PureEnum::ACCEPTED); // false
PureEnum::PENDING->is('PENDING'); // true
PureEnum::PENDING->is('ACCEPTED'); // false
PureEnum::PENDING->isNot('ACCEPTED'); // true


$backedEnum = IntBackedEnum::PENDING;
$backedEnum->is(IntBackedEnum::PENDING); // true
IntBackedEnum::PENDING->is(IntBackedEnum::ACCEPTED); // false
IntBackedEnum::PENDING->is(0); // true
IntBackedEnum::PENDING->is('PENDING'); // true
StringBackedEnum::PENDING->is('P'); // true
StringBackedEnum::PENDING->isNot('P'); // false

in()notIn()

in() 方法允许检查实例是否与实例数组、名称或值匹配。为了方便,还有一个 notIn() 方法,它是 in() 方法的逆。

$enum = PureEnum::PENDING;
$enum->in([PureEnum::PENDING,PureEnum::ACCEPTED]); // true
PureEnum::PENDING->in([PureEnum::DISCARDED, PureEnum::ACCEPTED]); // false
PureEnum::PENDING->in(['PENDING', 'ACCEPTED']); // true
PureEnum::PENDING->in(['ACCEPTED', 'DISCARDED']); // false
PureEnum::PENDING->notIn(['ACCEPTED']); // true

$backedEnum = IntBackedEnum::PENDING;
$backedEnum->in([IntBackedEnum::PENDING, IntBackedEnum::ACCEPTED]); // true
IntBackedEnum::PENDING->in([IntBackedEnum::ACCEPTED])// false
IntBackedEnum::PENDING->in([0, 1, 2]); // true
IntBackedEnum::PENDING->in([2, 3]); // false
IntBackedEnum::PENDING->in(['PENDING', 'ACCEPTED']); // true
IntBackedEnum::PENDING->in(['DISCARDED', 'ACCEPTED']); // false
StringBackedEnum::PENDING->in(['P', 'D']); // true
StringBackedEnum::PENDING->notIn(['A','D']); // true

名称

此辅助函数提供了 names()namesByValue() 方法。

names()

此方法返回枚举中案例名称的列表。

PureEnum::names(); // ['PENDING', 'ACCEPTED', 'DISCARDED', 'NO_RESPONSE']
PascalCasePureEnum::names(); // ['Pending', 'Accepted', 'Discarded', 'NoResponse']
StringBackedEnum::names(); // ['PENDING', 'ACCEPTED', 'DISCARDED', 'NO_RESPONSE']
// Subset
PureEnum::names([PureEnum::NO_RESPONSE, PureEnum::DISCARDED]); // ['NO_RESPONSE', 'DISCARDED']
PascalCasePureEnum::names([PascalCasePureEnum::Accepted, PascalCasePureEnum::Discarded]); // ['Accepted', 'Discarded']

namesByValue()

此方法返回 BackedEnum 上的 [值 => 名称] 关联数组,纯枚举上的 [名称 => 名称] 关联数组。

PureEnum::namesByValue(); // [ 'PENDING' => 'PENDING', 'ACCEPTED' => 'ACCEPTED', 'DISCARDED' => 'DISCARDED'...
StringBackedEnum::namesByValue(); // [ 'P' => 'PENDING', 'A' => 'ACCEPTED', 'D' => 'DISCARDED'...
IntBackedEnum::namesByValue(); // [ 0=>'PENDING', 1=>'ACCEPTED', 2=>'DISCARDED'...
// Subset
IntBackedEnum::namesByValue([IntBackedEnum::NO_RESPONSE, IntBackedEnum::DISCARDED]); // [ 3=>'NO_RESPONSE', 2=>'DISCARDED']

此辅助函数提供了 values()valuesByName() 方法。

values()

此方法返回 BackedEnum 的案例值列表或纯枚举的案例名称列表。

PureEnum::values(); // ['PENDING', 'ACCEPTED', 'DISCARDED', 'NO_RESPONSE']
StringBackedEnum::values(); // ['P', 'A', 'D', 'N']
IntBackedEnum::values(); // [0, 1, 2, 3]
// Subset
PureEnum::values([PureEnum::NO_RESPONSE, PureEnum::DISCARDED]); // ['NO_RESPONSE', 'DISCARDED']
StringBackedEnum::values([StringBackedEnum::NO_RESPONSE, StringBackedEnum::DISCARDED]); // ['N', 'D']
IntBackedEnum::values([IntBackedEnum::NO_RESPONSE, IntBackedEnum::DISCARDED]); // [3, 2]

valuesByName()

此方法返回 BackedEnum 上的 [名称 => 值] 关联数组,纯枚举上的 [名称 => 名称] 关联数组。

PureEnum::valuesByName(); // ['PENDING' => 'PENDING','ACCEPTED' => 'ACCEPTED','DISCARDED' => 'DISCARDED',...]
StringBackedEnum::valuesByName(); // ['PENDING' => 'P','ACCEPTED' => 'A','DISCARDED' => 'D','NO_RESPONSE' => 'N']
IntBackedEnum::valuesByName(); // ['PENDING' => 0,'ACCEPTED' => 1,'DISCARDED' => 2,'NO_RESPONSE' => 3]
// Subset
PureEnum::valuesByName([PureEnum::NO_RESPONSE, PureEnum::DISCARDED]); // ['NO_RESPONSE' => 'NO_RESPONSE', 'DISCARDED' => 'DISCARDED']
StringBackedEnum::valuesByName([StringBackedEnum::NO_RESPONSE, StringBackedEnum::DISCARDED]); // ['NO_RESPONSE' => 'N', 'DISCARDED' => 'D']
IntBackedEnum::valuesByName([IntBackedEnum::NO_RESPONSE, IntBackedEnum::DISCARDED]); // ['NO_RESPONSE' => 3, 'DISCARDED' => 2]

唯一标识符

此辅助函数允许从枚举或标识符获取唯一标识符。

此辅助函数不包括在基础 EnumHelper 特质中,也不依赖于它,因此如果您需要它,必须使用 EnumUniqueId

use Suleymanozev\EnumHelper\Traits\EnumUniqueId;

enum PureEnum
{
    use EnumUniqueId;
    
    ...

uniqueId()

此方法返回基于命名空间\类名.CASE_NAME 的枚举唯一标识符。您可以使用此标识符将多种类型的枚举保存到数据库的多态列中。

PureEnum::PENDING->uniqueId(); // Namespace\PureEnum.PENDING
$enum = StringBackedEnum::NO_RESPONSE;
$enum->uniqueId(); // Namespace\StringBackedEnum.NO_RESPONSE

fromUniqueId()

此方法从唯一标识符返回枚举实例。

PureEnum::fromUniqueId('Namespace\PureEnum.PENDING'); // PureEnum::PENDING
IntBackedEnum::fromUniqueId('Namespace\IntBackedEnum.PENDING'); // IntBackedEnum::PENDING
IntBackedEnum::fromUniqueId('NOT.valid.uniqueId'); // throw InvalidUniqueId Exception
IntBackedEnum::fromUniqueId('Wrong\Namespace\IntBackedEnum.PENDING'); // throw InvalidUniqueId Exception
IntBackedEnum::fromUniqueId('Namespace\IntBackedEnum.MISSING'); // throw InvalidUniqueId Exception

全局 getEnumFromUniqueId() 辅助函数

fromUniqueId() 方法使用较少,因为它仅与枚举类相关。更好的方法是创建一个全局辅助函数,可以像这样从 uniqueId 实例化任何枚举

use Suleymanozev\EnumHelper\Exceptions\InvalidUniqueId;

public function getEnumFromUniqueId(string $uniqueId): object
{
    if (
        !strpos($uniqueId, '.')
        || substr_count($uniqueId, '.') !== 1
    ) {
        throw InvalidUniqueId::uniqueIdFormatIsInvalid($uniqueId);
    }

    list($enumClass, $enumName) = explode('.', $uniqueId);

    foreach ($enumClass::cases() as $case){
        if( $case->name === $enumName){
                return $case;
            }
        }
    }
    
    throw InvalidUniqueId::caseNotPresent($case);
}

描述和翻译

此辅助工具允许为枚举的每个案例提供描述。适用于单语言和多语言应用程序。在需要更好的描述来表征案例或在多语言环境中时,这很有用。

此辅助工具不包括在基本EnumHelper特质中,也不依赖于它,因此如果您需要它,您必须使用EnumDescription并实现抽象的description()方法来定义描述。您可以在纯枚举和BackedEnum上使用它。

use Suleymanozev\EnumHelper\EnumHelper;
use Suleymanozev\EnumHelper\Traits\EnumDescription;

enum StringBackedEnum: string
{
    use EnumHelper;
    use EnumDescription;
    
    case PENDING = 'P';
    case ACCEPTED = 'A';
    case DISCARDED = 'D';
    case NO_RESPONSE = 'N';

    public function description(?string $lang = null): string
    {
        return match ($this) {
            self::PENDING => 'Await decision',
            self::ACCEPTED => 'Recognized valid',
            self::DISCARDED => 'No longer useful',
            self::NO_RESPONSE => 'No response',
        };
    }

在实现description()方法之后,您可以

PureEnum::PENDING->description(); // 'Await decision'

本地化

您可以使用自己的翻译方法/辅助工具替换description()方法来翻译描述。

public function description(?string $lang = null): string
    {
        // this is only an example of implementation... translate method not exist
        // if $lang is null you have to use the current locale
        return return translate('status.'$this->name, $lang);
        
        // or translate each case
        return match ($this) {
            self::PENDING => translate('Await decision'),
            self::ACCEPTED => translate('Recognized valid'),
            self::DISCARDED => translate('No longer useful'),
            self::NO_RESPONSE => translate('No response'),
        };
        
        //or use EnumUniqueId trait
        return translate($this->uniqueId(), $lang);
    }

在实现description方法之后,您可以

$enum = PureEnum::PENDING;
$enum->description(); // 'Await decision'
$enum->description('it'); // 🇮🇹 'In attesa'

descriptions()

此方法返回枚举案例描述的列表。

StringBackedEnum::descriptions(); // ['Await decision','Recognized valid','No longer useful','No response']
// Subset
StringBackedEnum::descriptions([StringBackedEnum::ACCEPTED, StringBackedEnum::NO_RESPONSE]); // ['Recognized valid','No response']

descriptionsByValue()

此方法返回一个关联数组,在BackedEnum上为[value => description],在纯枚举上为[name => description]。

StringBackedEnum::descriptionsByValue(); // ['P' => 'Await decision', 'A' => 'Recognized valid',...
PureEnum::descriptionsByValue(); // ['PENDING' => 'Await decision', 'ACCEPTED' => 'Recognized valid',...
// Subset
StringBackedEnum::descriptionsByValue([StringBackedEnum::DISCARDED, StringBackedEnum::ACCEPTED]); // ['D' => 'No longer useful', 'A' => 'Recognized valid']
PureEnum::descriptionsByValue([[PureEnum::PENDING, PureEnum::DISCARDED]); // ['PENDING' => 'Await decision', 'DISCARDED' => 'No longer useful']

nullableDescriptionsByValue()

此方法将默认值添加到descriptionsByValue()的开头,这在您需要在表单上执行可空选择时很有用。

StringBackedEnum::nullableDescriptionsByValue('Select value'); // [null => 'Select value', 'P' => 'Await decision', 'A' => 'Recognized valid',...

特别感谢

  • Datomatic 团队