greg0ire/enum

解决PHP中缺少枚举类型的问题

v4.3.1 2023-05-24 07:04 UTC

README

此包包含一个简单的类,可以作为您的枚举类基类使用。

Build Status

安装

composer require greg0ire/enum

使用方法

基本使用

扩展 Greg0ire\Enum\AbstractEnum,将您的枚举键值定义为常量,然后Bob就是你的叔叔。您可以使其类为抽象的或最终的,按需而定。

use Greg0ire\Enum\AbstractEnum;

final class DaysOfWeek extends AbstractEnum {
    const Sunday = 0;
    const Monday = 1;
    const Tuesday = 2;
    const Wednesday = 3;
    const Thursday = 4;
    const Friday = 5;
    const Saturday = 6;
}

然后,您可以使用 DaysOfWeek 类进行输入验证

DaysOfWeek::isValidName('Humpday');                  // false
DaysOfWeek::isValidName('Monday');                   // true
DaysOfWeek::isValidName('monday');                   // false
DaysOfWeek::isValidName(0);                          // false

DaysOfWeek::isValidValue(0);                         // true
DaysOfWeek::isValidValue(5);                         // true
DaysOfWeek::isValidValue(7);                         // false
DaysOfWeek::isValidValue('Friday');                  // false

这两种方法都有一个 assert* 对应方法,将抛出 Greg0ire\Enum\Exception\InvalidEnumValue 异常

DaysOfWeek::assertValidName(0);                      // InvalidEnumName
DaysOfWeek::assertValidValue('Friday');              // InvalidEnumValue

此外,您还可以将类中的所有常量作为哈希表获取

DaysOfWeek::getConstants();
DaysOfWeek::getConstants('strtolower'); // Will combine your values with `DaysOfWeek::getKeys($callback)`.
DaysOfWeek::getConstants('strtolower', true); // Values combine with `DaysOfWeek::getClassPrefixedKeys($callback)`.
DaysOfWeek::getConstants('strtolower', true, '.'); // Same with `DaysOfWeek::getClassPrefixedKeys($callback, $separator)`.

您还可以获取类中的所有键作为数组

DaysOfWeek::getKeys();
DaysOfWeek::getKeys('strtolower'); // Will call `array_map` with the given callback.

或带有枚举类前缀的键

DaysOfWeek::getClassPrefixedKeys();
DaysOfWeek::getClassPrefixedKeys('strtolower'); // Will call `array_map` with the given callback.
DaysOfWeek::getClassPrefixedKeys('strtolower', '.'); // Replace the namespace separator ('_' by default).

如果您想从值获取键

$key = DaysOfWeek::getKeysFromValue(1); // Monday will be assigned to $key

如果您有许多具有相同值的键,您将获得一个数组,否则将获得一个值。

高级使用

如果您需要从无法修改的类中获取常量,或从接口中获取,或从多个类/接口中获取,您可能需要覆盖 AbstractEnum::getEnumTypes()

例如,如果您有以下类和接口

namespace Vendor\Namespace;

class ClassFromAVendor
{
   const SOMETHING      = 'something';
   const SOMETHING_ELSE = 'something_else';
}
namespace My\Namespace;

interface SomeInterface
{
   const ANOTHER_CONST = 'another_const';
}

您可以通过创建此枚举来获取所有三个常量

use Greg0ire\Enum\AbstractEnum;
use My\Namespace\SomeInterface;
use Vendor\Namespace\ClassFromAVendor;

final class MyEnum extends AbstractEnum
{
    protected static function getEnumTypes()
    {
        return [ClassFromAVendor::class, SomeInterface::class];
    }
}

或者,您可以为每个类型指定一个前缀,以避免在哈希键中获取完全限定类名。

use Greg0ire\Enum\AbstractEnum;
use My\Namespace\SomeInterface;
use Vendor\Namespace\ClassFromAVendor;

final class MyEnum extends AbstractEnum
{
    protected static function getEnumTypes()
    {
        return [
            'prefix1' => ClassFromAVendor::class,
            'prefix2' => SomeInterface::class,
        ];
    }
}

从服务获取标签

如果您想获取枚举值背后的常量标签,可以实例化 GetLabel 类并调用它。

use Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel;

$label = new GetLabel();
$label(Your\Enum\Class::VALUE, Your\Enum\Class::class);

要启用翻译,需要 symfony/translation 组件,并在 GetLabel 构造函数中传递一个 Symfony\Contracts\Translation\TranslationInterface 实例。

use Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel;
use Symfony\Contracts\Translation\TranslationInterface;

$label = new GetLabel($translator);
$label(Your\Enum\Class::VALUE, Your\Enum\Class::class);

如果您使用 Symfony,别名服务并简单地注入它。如果启用了翻译,则将自动注入 TranslatorInterface

services:
    # ...
    Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel: "@greg0ire_enum.symfony.translator.get_label"
public function index(GetLabel $label)
{
    $label(Your\Enum\Class::VALUE, Your\Enum\Class::class);
    $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, 'another_domain'); // Change the translation domain
    $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, false); // Disable translation. In this case the class prefix wont be added
    $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, false, true); // Disable translation but keep class prefix
    $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, false, true, '.'); // Disable translation but keep class prefix with a custom separator
}

与其他库的集成

greg0ire/enum 与其他库集成。列表可在 Composer 依赖项清单的 suggest 部分找到。

Symfony 验证器

此包提供了一个“即用型”的 symfony 验证器。您需要安装 symfony/validator 包才能使用它。

use Greg0ire\Enum\Bridge\Symfony\Validator\Constraint\Enum;
use Symfony\Component\Validator\Validation;
use Your\Namespace\EnumClass;

$validator = Validation::createValidator();

$violations = $validator->validateValue(42, new Enum(EnumClass::class));
// You can also show the constants keys on the error message:
$violations = $validator->validateValue(42, new Enum(['class' => EnumClass::class, 'showKeys' => true]));
// Enum constraint inherits from Choice constraint. You can use inherited options too:
$violations = $validator->validateValue(42, new Enum(['class' => EnumClass::class, 'strict' => true]));

带有注解的另一个示例

use Doctrine\Common\Annotations\AnnotationRegistry;
use Greg0ire\Enum\Bridge\Symfony\Validator\Constraint\Enum as EnumAssert;
use Symfony\Component\Validator\Validation;

class MyClass
{
    /**
     * @EnumAssert("Your\Namespace\EnumClass")
     */
    private $dummy;

    public function __construct($dummy)
    {
        $this->dummy = $dummy
    }
}

AnnotationRegistry::registerLoader('class_exists');
$validator = Validation::createValidatorBuilder()
    ->enableAnnotationMapping()
    ->getValidator();

$object = new MyClass(42);

$violations = $validator->validate($object);

注意:您将需要获取 doctrine/annotationsdoctrine/cache 包才能使用它。

Symfony 表单

此包提供了一个“即用型”的 symfony 表单类型。您需要安装 symfony/form 包才能使用它。

use Greg0ire\Enum\Bridge\Symfony\Form\Type\EnumType;
use Symfony\Component\Form\Forms;
use Your\Namespace\EnumClass;

$formFactory = Forms::createFormFactory();

$view = $this->factory->create(EnumType::class, null, array(
    'class' => EnumClass::class,
))->createView();

Twig 扩展

此包附带一个 EnumExtension Twig 类。它包含一个过滤器和一些函数。您需要安装 twig/twig 包才能使用它。

过滤器

enum_label 过滤器将尝试返回对应于给定值的常量标签。

此过滤器依赖于 Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel 服务。

如果可能,它将尝试将其翻译。要启用翻译,需要 symfony/translation 组件,并在 GetLabel 构造函数中传递一个 Symfony\Contracts\Translation\TranslationInterface 实例。GetLabel 实例将在 EnumExtension 构造函数中注入。

如果翻译不可用,您将获得具有类前缀的默认标签。

使用方法

{{ value|enum_label('Your\\Enum\\Class') }}
{{ value|enum_label('Your\\Enum\\Class', 'another_domain') }} {# Change the translation domain #}
{{ value|enum_label('Your\\Enum\\Class', false) }} {# Disable translation. In this case the class prefix wont be added #}
{{ value|enum_label('Your\\Enum\\Class', false, true) }} {# Disable translation but keep class prefix #}
{{ value|enum_label('Your\\Enum\\Class', false, true, '.') }} {# Disable translation but keep class prefix with a custom separator #}
函数

3 个可用的 twig 函数是某些 AbstractEnum 方法,在 twig 模板中可能很有用

  • enum_get_constants <=> AbstractEnum::getConstants
  • enum_get_keys <=> AbstractEnum::getKeys
  • enum_get_class_prefixed_keys <=> AbstractEnum::getClassPrefixedKeys

参数完全相同,只是你需要先指定目标类(例如作为 enum_label 过滤器)。

以下是一个使用 enum_get_constants 函数的具体示例

{% for enum_key, enum_value in enum_get_constants('Your\\Enum\\Class') %}
    {{ enum_key }} -> {{ enum_value }}
{% endfor %}
作为服务的Twig扩展

在Symfony项目中,该扩展可以被自动加载。首先,你需要引入 symfony/framework-bundlesymfony/twig-bundle 包,或者使用Symfony全栈。

然后,在你的应用程序内核中注册该包

// app/AppKernel.php

public function registerBundles()
{
    $bundles = [
        // ...
        new Greg0ire\Enum\Bridge\Symfony\Bundle\Greg0ireEnumBundle(),
    ];

    // ...

    return $bundles
}

这就完了。现在你可以直接使用过滤器了。

贡献

请参阅 CONTRIBUTING.md

鸣谢

这是一个无耻的剽窃自 这个Stack Overflow答案,有一个修改:将 getConstants 方法设为公共,以便它可以用于构建选择小部件等。如果你想要感谢某个人,请感谢 Brian Cline