Enum-Based Validation and Type Casting Pattern

📖 2 minutes read

When working with dynamic configuration or polymorphic data structures, PHP 8.1+ backed enums can do more than just define constants—they can encapsulate validation rules and type casting logic, creating a self-documenting, single-source-of-truth for field definitions.

The Pattern

enum ConfigType: string
{
    case DISCOUNT_RATE = 'discount_rate';
    case MAX_QUANTITY = 'max_quantity';
    case ENABLED = 'enabled';
    
    // Define the expected PHP type for each config
    public function castType(): string
    {
        return match($this) {
            self::DISCOUNT_RATE => 'float',
            self::MAX_QUANTITY => 'int',
            self::ENABLED => 'bool',
        };
    }
    
    // Generate validation rules based on type
    public function validationRules(): array
    {
        return match($this->castType()) {
            'float' => ['numeric', 'min:0', 'max:100'],
            'int' => ['integer', 'min:0'],
            'bool' => ['boolean'],
            default => ['string'],
        };
    }
    
    // Get all enum cases with their cast types
    public static function casts(): array
    {
        return collect(self::cases())
            ->mapWithKeys(fn($case) => [
                $case->value => $case->castType()
            ])
            ->toArray();
    }
}

Using in Models

class Config extends Model
{
    protected $casts = [
        'type' => ConfigType::class,
    ];
    
    // Cast the stored string value to the correct type
    public function getValue(): mixed
    {
        return match($this->type->castType()) {
            'float' => (float) $this->value,
            'int' => (int) $this->value,
            'bool' => filter_var($this->value, FILTER_VALIDATE_BOOLEAN),
            default => $this->value,
        };
    }
}

Using in Controllers

public function update(Request $request, $model)
{
    // Generate validation rules dynamically from enum
    $rules = collect(ConfigType::cases())
        ->mapWithKeys(fn($type) => [
            $type->value => [
                ...$type->validationRules(),
                'nullable'
            ]
        ])
        ->toArray();
    
    $validated = $request->validate($rules);
    
    // Save each config with proper type casting
    foreach (ConfigType::cases() as $type) {
        if (array_key_exists($type->value, $validated)) {
            $model->setConfig($type, $validated[$type->value]);
        }
    }
}

Why This Works

  • Single Source of Truth: Type definitions, validation, and casting logic live together
  • Self-Documenting: New developers can read the enum to understand all available configuration fields
  • Type-Safe: PHP 8.1+ backed enums with IDE autocomplete
  • Maintainable: Adding a new config type is just one new case
  • Testable: Easy to unit test each enum method independently

This pattern shines when building admin panels, user preferences, or any polymorphic configuration system where each field has different validation and type requirements.

Daryle De Silva

VP of Technology

11+ years building and scaling web applications. Writing about what I learn in the trenches.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *