# Admin Form Fields - Composition-basiertes Design

## Framework-Prinzip: No Inheritance!

❌ **NICHT**: Abstract Base Classes mit `extends`
✅ **STATTDESSEN**: Composition mit readonly Classes

## Design-Pattern: Composition über Vererbung

### Core Interface

```php
interface FormField
{
    public function render(FormBuilder $form): FormBuilder;
    public function getName(): string;
    public function getValue(): mixed;
    public function getLabel(): string;
}
```

### Shared Components (für Composition)

```php
// Gemeinsame Funktionalität als Value Objects

final readonly class FieldAttributes
{
    public function __construct(
        public string $name,
        public string $id,
        public string $class = 'form-control',
        public bool $required = false,
        public ?string $placeholder = null,
        public array $additional = []
    ) {}

    public function toArray(): array
    {
        $attrs = [
            'name' => $this->name,
            'id' => $this->id,
            'class' => $this->class,
            ...$this->additional
        ];

        if ($this->required) {
            $attrs['required'] = 'required';
        }

        if ($this->placeholder !== null) {
            $attrs['placeholder'] = $this->placeholder;
        }

        return $attrs;
    }
}

final readonly class FieldMetadata
{
    public function __construct(
        public string $name,
        public string $label,
        public ?string $help = null
    ) {}
}

final readonly class FieldWrapper
{
    public function wrap(string $content, FieldMetadata $metadata): string
    {
        $label = FormElement::create('label', ['for' => $metadata->name], $metadata->label);

        $html = FormElement::create('div', ['class' => 'form-group'], $label . $content);

        if ($metadata->help !== null) {
            $help = FormElement::create('small', ['class' => 'form-text text-muted'], $metadata->help);
            $html .= $help;
        }

        return $html;
    }
}
```

### Field Implementations (Keine Vererbung!)

```php
// TextField - Composition statt Inheritance
final readonly class TextField implements FormField
{
    public function __construct(
        private FieldMetadata $metadata,
        private FieldAttributes $attributes,
        private FieldWrapper $wrapper,
        private mixed $value = null
    ) {}

    public static function create(
        string $name,
        string $label,
        mixed $value = null,
        bool $required = false,
        ?string $placeholder = null,
        ?string $help = null
    ): self {
        return new self(
            metadata: new FieldMetadata($name, $label, $help),
            attributes: new FieldAttributes(
                name: $name,
                id: $name,
                required: $required,
                placeholder: $placeholder
            ),
            wrapper: new FieldWrapper(),
            value: $value
        );
    }

    public function render(FormBuilder $form): FormBuilder
    {
        $attrs = [...$this->attributes->toArray(), 'type' => 'text'];

        if ($this->value !== null) {
            $attrs['value'] = (string) $this->value;
        }

        $input = FormElement::create('input', $attrs);
        $wrapped = $this->wrapper->wrap($input, $this->metadata);

        return $form->addElement($wrapped);
    }

    public function getName(): string
    {
        return $this->metadata->name;
    }

    public function getValue(): mixed
    {
        return $this->value;
    }

    public function getLabel(): string
    {
        return $this->metadata->label;
    }
}
```

### SelectField mit Composition

```php
final readonly class SelectField implements FormField
{
    public function __construct(
        private FieldMetadata $metadata,
        private FieldAttributes $attributes,
        private FieldWrapper $wrapper,
        private FieldOptions $options,
        private mixed $value = null
    ) {}

    public static function create(
        string $name,
        string $label,
        array $options,
        mixed $value = null,
        bool $required = false,
        ?string $placeholder = null,
        ?string $help = null
    ): self {
        return new self(
            metadata: new FieldMetadata($name, $label, $help),
            attributes: new FieldAttributes(
                name: $name,
                id: $name,
                required: $required,
                placeholder: $placeholder
            ),
            wrapper: new FieldWrapper(),
            options: new FieldOptions($options, $placeholder),
            value: $value
        );
    }

    public function render(FormBuilder $form): FormBuilder
    {
        $select = FormElement::create(
            'select',
            $this->attributes->toArray(),
            $this->options->renderOptions($this->value)
        );

        $wrapped = $this->wrapper->wrap($select, $this->metadata);

        return $form->addElement($wrapped);
    }

    public function getName(): string
    {
        return $this->metadata->name;
    }

    public function getValue(): mixed
    {
        return $this->value;
    }

    public function getLabel(): string
    {
        return $this->metadata->label;
    }
}

// Helper Value Object
final readonly class FieldOptions
{
    public function __construct(
        private array $options,
        private ?string $placeholderText = null
    ) {}

    public function renderOptions(mixed $selectedValue): string
    {
        $html = '';

        if ($this->placeholderText !== null) {
            $html .= '<option value="">' . htmlspecialchars($this->placeholderText) . '</option>';
        }

        foreach ($this->options as $value => $label) {
            $selected = $value === $selectedValue ? ' selected' : '';
            $html .= sprintf(
                '<option value="%s"%s>%s</option>',
                htmlspecialchars((string) $value),
                $selected,
                htmlspecialchars((string) $label)
            );
        }

        return $html;
    }
}
```

### DateTimeField mit spezifischen Komponenten

```php
final readonly class DateTimeField implements FormField
{
    public function __construct(
        private FieldMetadata $metadata,
        private FieldAttributes $attributes,
        private FieldWrapper $wrapper,
        private DateTimeFormatter $formatter,
        private mixed $value = null
    ) {}

    public static function create(
        string $name,
        string $label,
        mixed $value = null,
        bool $required = false,
        ?string $placeholder = null,
        ?string $help = null
    ): self {
        return new self(
            metadata: new FieldMetadata($name, $label, $help),
            attributes: new FieldAttributes(
                name: $name,
                id: $name,
                required: $required,
                placeholder: $placeholder ?? 'YYYY-MM-DD HH:MM'
            ),
            wrapper: new FieldWrapper(),
            formatter: new DateTimeFormatter(),
            value: $value
        );
    }

    public function render(FormBuilder $form): FormBuilder
    {
        $attrs = [...$this->attributes->toArray(), 'type' => 'datetime-local'];

        if ($this->value !== null) {
            $attrs['value'] = $this->formatter->formatForInput($this->value);
        }

        $input = FormElement::create('input', $attrs);
        $wrapped = $this->wrapper->wrap($input, $this->metadata);

        return $form->addElement($wrapped);
    }

    public function getName(): string
    {
        return $this->metadata->name;
    }

    public function getValue(): mixed
    {
        return $this->value;
    }

    public function getLabel(): string
    {
        return $this->metadata->label;
    }
}

// Helper für DateTime-Formatierung
final readonly class DateTimeFormatter
{
    public function formatForInput(mixed $value): string
    {
        if ($value instanceof \DateTimeInterface) {
            return $value->format('Y-m-d\TH:i');
        }

        if (is_string($value)) {
            return (new \DateTimeImmutable($value))->format('Y-m-d\TH:i');
        }

        return '';
    }
}
```

## Vorteile dieses Designs

### ✅ Framework-Konform
- Keine Vererbung (`extends`)
- Composition over Inheritance
- Readonly Classes
- Value Objects für Datenstrukturen

### ✅ Wiederverwendbare Komponenten
- `FieldMetadata` - für alle Fields gleich
- `FieldAttributes` - wiederverwendbar
- `FieldWrapper` - zentrale Wrapper-Logik
- `FieldOptions` - für Select/Radio/Checkbox

### ✅ Type-Safe & Testbar
- Jede Komponente isoliert testbar
- Klare Verantwortlichkeiten
- Dependency Injection freundlich

### ✅ Flexibel & Erweiterbar
- Neue Field-Types durch Composition
- Custom Components durch Interface
- Keine Vererbungshierarchien

## FormFieldFactory mit Composition

```php
final readonly class FormFieldFactory
{
    public function __construct(
        private FieldWrapper $wrapper
    ) {}

    public function createFromConfig(string $name, array $config, mixed $value = null): FormField
    {
        $type = $config['type'];
        $label = $config['label'] ?? ucfirst($name);
        $required = $config['required'] ?? false;
        $placeholder = $config['placeholder'] ?? null;
        $help = $config['help'] ?? null;

        return match ($type) {
            'text' => TextField::create($name, $label, $value, $required, $placeholder, $help),
            'email' => EmailField::create($name, $label, $value, $required, $placeholder, $help),
            'password' => PasswordField::create($name, $label, $required, $placeholder, $help),
            'textarea' => TextareaField::create($name, $label, $value, $required, $placeholder, $help),
            'select' => SelectField::create(
                $name,
                $label,
                $config['options'] ?? [],
                $value,
                $required,
                $placeholder,
                $help
            ),
            'checkbox' => CheckboxField::create($name, $label, (bool) $value, $help),
            'date' => DateField::create($name, $label, $value, $required, $placeholder, $help),
            'datetime' => DateTimeField::create($name, $label, $value, $required, $placeholder, $help),
            'number' => NumberField::create(
                $name,
                $label,
                $value,
                $required,
                $placeholder,
                $help,
                $config['min'] ?? null,
                $config['max'] ?? null,
                $config['step'] ?? null
            ),
            'url' => UrlField::create($name, $label, $value, $required, $placeholder, $help),
            'file' => FileField::create($name, $label, $required, $help),
            'hidden' => HiddenField::create($name, $value),
            default => throw new \InvalidArgumentException("Unknown field type: {$type}"),
        };
    }
}
```

## Struktur

```
src/Framework/Admin/FormFields/
├── FormField.php                    # Interface
├── ValueObjects/
│   ├── FieldMetadata.php           # Name, Label, Help
│   ├── FieldAttributes.php         # HTML Attributes
│   ├── FieldOptions.php            # Select Options
│   └── DateTimeFormatter.php       # DateTime Formatting
├── Components/
│   └── FieldWrapper.php            # Wrapper Logic
└── Fields/
    ├── TextField.php
    ├── TextareaField.php
    ├── EmailField.php
    ├── PasswordField.php
    ├── SelectField.php
    ├── CheckboxField.php
    ├── DateField.php
    ├── DateTimeField.php
    ├── NumberField.php
    ├── UrlField.php
    ├── FileField.php
    └── HiddenField.php
```

## Nächste Schritte

Soll ich:
1. Die Basis-Komponenten (FieldMetadata, FieldAttributes, FieldWrapper) erstellen?
2. Dann die wichtigsten Field-Klassen implementieren?
3. FormFieldFactory anpassen?
