使用php8的注解,方便地定义枚举及其描述

参考了博客:laravel的枚举助手——基于php8.1的枚举特性 ,这篇文章中使用的enum类型,因为enum不能包含属性,我希望能保存map,所以用class+const实现

定义

<?php

namespace App\Enums;

/**
 * 方便ide提示,可省略
 * @method static string Yes()
 * @method static string No()
 */
class YesNo {
    use Enum;
    #[Description('是')]
    const Yes = 1;

    #[Description('否')]
    const No = 0;
}

使用

class Controller extends BaseController {
    public function test() {
        dump(YesNo::map());
        dump(YesNo::arrayMap());

        dump(YesNo::names());
        dump(YesNo::values());
        dump(YesNo::descriptions());

        dump(YesNo::Yes());


        dump(YesNo::description(YesNo::No));
        dump(YesNo::value('否'));

        dump(ConsumableType::make([
                                       'name'   => 'test',
                                       'status' => YesNo::Yes,
                                   ])->toArray());

    }
}
class ConsumableType extends Model {
    protected $appends=['status_name'];

    protected function statusName(): Attribute {
        return Attribute::make(
            fn ($value,$attributes) => YesNo::description($attributes['status']),
        );
    }
}

实现

<?php

namespace App\Enums;
use Attribute;

#[Attribute]
class Description {
    public function __construct(string $description) {}
}
<?php

namespace App\Enums;

use Exception;
use Illuminate\Support\Collection;
use ReflectionClass;

/**
 * @method static string|null description(int|string $value) 根据值获取描述
 * @method static string|null value(string $description) 根据描述获取值
 * @method static Collection map() 获取常量值与描述的映射集合
 * @method static array arrayMap() 获取常量值与描述的映射数组
 * @method static Collection names() 获取常量名集合
 * @method static Collection values() 获取常量值集合
 * @method static Collection descriptions() 获取描述集合
 */
trait Enum {
    public static ?Collection $valueMap = null;        // [常量值=>描述]
    public static ?Collection $nameMap = null;         // [常量名=>描述]
    public static ?Collection $descriptionMap = null;  // [描述=>常量值]

    public static function init(): void {
        // 仅执行一次
        if (!is_null(static::$valueMap)) {
            return;
        }
        // 获取类的常量反射
        $reflection = new ReflectionClass(static::class);
        $consts = $reflection->getReflectionConstants();

        static::$valueMap = new Collection();
        static::$nameMap = new Collection();

        foreach ($consts as $const) {
            // 获取常量的Description注解
            $annotations = $const->getAttributes(Description::class);
            if (!empty($annotations)) {
                $description = $annotations[0]->getArguments()[0];
                // 常量值=>描述 和 常量名=>描述
                static::$valueMap->put($const->getValue(), $description);
                static::$nameMap->put($const->getName(), $description);
            }
        }
    }

    /**
     * 根据常量名获取描述
     */
    public static function descriptionByName(string $name): ?string {
        static::init();

        if (static::$nameMap->offsetExists($name)) {
            return static::$nameMap->get($name);
        }
        return null;
    }

    private static function validateArguments(string $name, array $arguments): void {
        switch ($name) {
            case 'description':
            case 'value':
                count($arguments) === 0 && throw new Exception($name . '方法必须传入1个参数');
        }
    }

    /**
     * @throws Exception
     */
    public static function __callStatic(string $name, array $arguments) {
        static::validateArguments($name, $arguments);
        static::init();

        $name === 'value' && is_null(static::$descriptionMap) && static::$descriptionMap = static::$valueMap->flip();

        return match ($name) {
            'description' => static::$valueMap->get($arguments[0]),
            'value' => static::$descriptionMap->get($arguments[0]),
            'map' => static::$valueMap,
            'arrayMap' => static::$valueMap->toArray(),
            'names' => static::$nameMap->keys(),
            'values' => static::$valueMap->keys(),
            'descriptions' => static::$valueMap->values(),
            default => static::descriptionByName($name),   // 常量名作为静态方法调用,返回描述
        };
    }
}

只做了枚举的简单功能,后续可以完善一下,结合响应状态码,请求验证等,用的时候use Enum就可以了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2

这个抽象类可以使用 Trait 实现,PHP 自带的 enum 可以使用 trait 可以参考 hyperf 中的相关实现:github.com/hyperf/hyperf/blob/mast...

1个月前 评论
DogLoML (楼主) 1个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!