annotated icon indicating copy to clipboard operation
annotated copied to clipboard

💡 Specify enum values in different ways

Open puzzledpolymath opened this issue 3 months ago • 0 comments

I have an idea!

When defining enum values, currently you must define the Column type with parenthesis, wrapping the set of values. Having to manually type these values isn't great from a maintenance perspective, also code editors will flag this as an issue because type does not match ExpectedValues having enum(one,two) instead of expected enum.

    public const string STATUS_ACTIVE    = 'active';
    public const string STATUS_PENDING   = 'pending';
    public const string STATUS_SUSPENDED = 'suspended';

    #[Column(
        type: 'enum(active,pending,suspended)',
        name: 'status',
        default: self::STATUS_ACTIVE,
    )]
    private string $status = self::STATUS_ACTIVE;

It would be nice to have the ability to specify enum values using code, an array of constants, array of enum cases, a backed enum class name etc. Below are a few modifications to the Column annotation class providing such behavior, just as an example.

class Column
{
    public function __construct(
        protected string $type,
        protected ?string $name = null,
        protected ?string $property = null,
        protected bool $primary = false,
        protected bool $nullable = false,
        protected mixed $default = null,
        protected mixed $typecast = null,
        protected bool $castDefault = false,
        protected bool $readonlySchema = false,
        mixed ...$attributes,
    ) {
        if ($type === 'enum' && isset($attributes['values'])) {
            $values = $attributes['values'];
            if (is_string($values) && enum_exists($values)) {
                $values = array_column($values::cases(), 'value');
            } elseif ($values instanceof BackedEnum) {
                $values = array_column($values::cases(), 'value');
            } elseif (is_array($values)) {
                $values = array_map(function ($value) {
                    if ($value instanceof BackedEnum) {
                        return $value->value;
                    }
                    if (is_object($value) && property_exists($value, 'value')) {
                        return $value->value;
                    }
                    return $value;
                }, $values);
            }
            $this->type = 'enum(' . implode(',', array_map('strval', (array) $values)) . ')';
            unset($attributes['values']);
        }

        if ($default !== null) {
            $this->hasDefault = true;
        }

        $this->attributes = $attributes;
    }
}

Giving developers more options.

    public const string STATUS_ACTIVE    = 'active';
    public const string STATUS_PENDING   = 'pending';
    public const string STATUS_SUSPENDED = 'suspended';

    #[Column(
        type: 'enum',
        name: 'status',
        default: self::STATUS_ACTIVE,
        values: [self::STATUS_ACTIVE, self::STATUS_PENDING, self::STATUS_SUSPENDED],
    )]
    private string $status = self::STATUS_ACTIVE;
    #[Column(
        type: 'enum',
        name: 'status',
        default: AccountStatus::ACTIVE->value,
        values: AccountStatus::class,
    )]
    private string $status = AccountStatus::ACTIVE->value;
    #[Column(
        type: 'enum',
        name: 'status',
        default: AccountStatus::ACTIVE->value,
        typecast: AccountStatus::class,
        values: AccountStatus::class,
    )]
    private AccountStatus $status = AccountStatus::ACTIVE;

Related

  • https://github.com/cycle/orm/issues/484

puzzledpolymath avatar Jul 30 '25 06:07 puzzledpolymath