# LiveComponent FormBuilder Integration Elegante Integration zwischen LiveComponent System und bestehendem FormBuilder. ## Architektur ``` ┌─────────────────────────┐ │ MultiStepFormDefinition │ ← Value Object mit Steps └────────────┬────────────┘ │ ├─► FormStepDefinition ← Step-Info + Fields │ └─► FormFieldDefinition ← Field-Config (text, email, etc.) │ ├─► FieldType (enum) ├─► FieldCondition (conditional rendering) └─► StepValidator (validation logic) ┌─────────────────────────┐ │ MultiStepFormComponent │ ← Generic LiveComponent └────────────┬────────────┘ │ └─► LiveFormBuilder ← Erweitert bestehenden FormBuilder │ └─► FormBuilder (bestehend, wiederverwendet!) ``` ## Verwendungsbeispiel ### 1. Form Definition erstellen (Deklarativ, Type-Safe) ```php use App\Framework\LiveComponents\FormBuilder\MultiStepFormDefinition; use App\Framework\LiveComponents\FormBuilder\FormStepDefinition; use App\Framework\LiveComponents\FormBuilder\FormFieldDefinition; use App\Framework\LiveComponents\FormBuilder\FieldCondition; // Deklarative Form-Definition - kein Code-Duplikat! $userRegistrationForm = new MultiStepFormDefinition( steps: [ // Step 1: Personal Information new FormStepDefinition( title: 'Persönliche Informationen', description: 'Bitte geben Sie Ihre persönlichen Daten ein', fields: [ FormFieldDefinition::text( name: 'first_name', label: 'Vorname', required: true ), FormFieldDefinition::text( name: 'last_name', label: 'Nachname', required: true ), FormFieldDefinition::email( name: 'email', label: 'E-Mail Adresse', required: true ) ], validator: new PersonalInfoValidator() ), // Step 2: Account Type new FormStepDefinition( title: 'Konto-Typ', description: 'Wählen Sie Ihren Konto-Typ', fields: [ FormFieldDefinition::radio( name: 'account_type', label: 'Account Type', options: [ 'personal' => 'Privatkonto', 'business' => 'Geschäftskonto' ], required: true ), // Conditional Field - nur bei Business FormFieldDefinition::text( name: 'company_name', label: 'Firmenname', required: true )->showWhen( FieldCondition::equals('account_type', 'business') ), FormFieldDefinition::text( name: 'vat_number', label: 'USt-IdNr.', placeholder: 'DE123456789' )->showWhen( FieldCondition::equals('account_type', 'business') ) ], validator: new AccountTypeValidator() ), // Step 3: Preferences new FormStepDefinition( title: 'Präferenzen', description: 'Passen Sie Ihre Einstellungen an', fields: [ FormFieldDefinition::checkbox( name: 'newsletter', label: 'Newsletter abonnieren' ), FormFieldDefinition::select( name: 'language', label: 'Bevorzugte Sprache', options: [ 'en' => 'English', 'de' => 'Deutsch', 'fr' => 'Français' ], defaultValue: 'de' ) ] ) ], submitHandler: new UserRegistrationSubmitHandler() ); ``` ### 2. Validator implementieren ```php use App\Framework\LiveComponents\FormBuilder\StepValidator; final readonly class PersonalInfoValidator implements StepValidator { public function validate(array $formData): array { $errors = []; if (empty($formData['first_name'] ?? '')) { $errors['first_name'] = 'Vorname ist erforderlich'; } if (empty($formData['last_name'] ?? '')) { $errors['last_name'] = 'Nachname ist erforderlich'; } if (empty($formData['email'] ?? '')) { $errors['email'] = 'E-Mail ist erforderlich'; } elseif (!filter_var($formData['email'], FILTER_VALIDATE_EMAIL)) { $errors['email'] = 'Ungültige E-Mail Adresse'; } return $errors; } } final readonly class AccountTypeValidator implements StepValidator { public function validate(array $formData): array { $errors = []; if (empty($formData['account_type'] ?? '')) { $errors['account_type'] = 'Bitte wählen Sie einen Konto-Typ'; } // Conditional validation für Business if (($formData['account_type'] ?? '') === 'business') { if (empty($formData['company_name'] ?? '')) { $errors['company_name'] = 'Firmenname ist erforderlich'; } } return $errors; } } ``` ### 3. Submit Handler implementieren ```php use App\Framework\LiveComponents\FormBuilder\FormSubmitHandler; use App\Framework\LiveComponents\FormBuilder\SubmitResult; final readonly class UserRegistrationSubmitHandler implements FormSubmitHandler { public function __construct( private UserService $userService ) {} public function handle(array $formData): SubmitResult { try { $user = $this->userService->registerUser( firstName: $formData['first_name'], lastName: $formData['last_name'], email: $formData['email'], accountType: $formData['account_type'], companyName: $formData['company_name'] ?? null, newsletter: ($formData['newsletter'] ?? 'no') === 'yes', language: $formData['language'] ?? 'de' ); return SubmitResult::success( message: 'Registrierung erfolgreich!', redirectUrl: '/dashboard', data: ['user_id' => $user->id] ); } catch (\Exception $e) { return SubmitResult::failure( message: 'Registrierung fehlgeschlagen: ' . $e->getMessage() ); } } } ``` ### 4. Controller Setup ```php use App\Framework\Http\Attributes\Route; use App\Framework\Http\Method; use App\Framework\Http\ViewResult; use App\Framework\LiveComponents\FormBuilder\MultiStepFormComponent; use App\Framework\LiveComponents\ValueObjects\ComponentId; final readonly class UserRegistrationController { #[Route('/register', method: Method::GET)] public function showRegistrationForm(): ViewResult { // Form Definition (könnte auch aus Container kommen) $formDefinition = $this->createUserRegistrationForm(); // Component erstellen $component = new MultiStepFormComponent( id: ComponentId::generate('user-registration'), formDefinition: $formDefinition ); return new ViewResult( template: 'pages/register', data: [ 'registration_form' => $component ] ); } private function createUserRegistrationForm(): MultiStepFormDefinition { return new MultiStepFormDefinition( steps: [ // ... (wie oben) ], submitHandler: new UserRegistrationSubmitHandler($this->userService) ); } } ``` ### 5. Template Usage ```html

Benutzerregistrierung

``` ## Vorteile dieser Lösung ### ✅ Kein Code-Duplikat - Nutzt bestehenden `FormBuilder` aus View-Modul - `LiveFormBuilder` erweitert nur mit LiveComponent-Features - Keine doppelte Field-Rendering-Logik ### ✅ Type-Safe & Framework-Compliant - Alle Value Objects: `FormFieldDefinition`, `FormStepDefinition`, `MultiStepFormDefinition` - Readonly Classes überall - Enums für `FieldType` ### ✅ Deklarativ statt Imperativ - Form-Definition rein deklarativ (kein Code für Rendering) - Klare Trennung: Definition vs. Rendering vs. Validation vs. Submission ### ✅ Conditional Fields eingebaut ```php FormFieldDefinition::text('company_name', 'Firma', required: true) ->showWhen(FieldCondition::equals('account_type', 'business')) ``` ### ✅ Wiederverwendbare Validators ```php final readonly class EmailValidator implements StepValidator { public function validate(array $formData): array { // Wiederverwendbare Validation-Logic } } ``` ### ✅ Testbar ```php // Unit Test für Validator it('validates email format', function () { $validator = new PersonalInfoValidator(); $errors = $validator->validate(['email' => 'invalid']); expect($errors)->toHaveKey('email'); }); // Integration Test für Component it('moves to next step after validation', function () { $component = new MultiStepFormComponent( id: ComponentId::generate('test'), formDefinition: $this->testFormDef ); $result = $component->nextStep([ 'first_name' => 'John', 'last_name' => 'Doe', 'email' => 'john@example.com' ]); expect($result->get('current_step'))->toBe(2); }); ``` ## Erweiterungsmöglichkeiten ### Custom Field Types ```php // Neuen FieldType hinzufügen enum FieldType: string { case TEXT = 'text'; case EMAIL = 'email'; // ... existing types case DATE = 'date'; case PHONE = 'phone'; case CURRENCY = 'currency'; } // LiveFormBuilder erweitern final readonly class LiveFormBuilder { public function addLiveDateInput( string $name, string $label, ?string $value = null ): self { // Implementation } } ``` ### Multi-Field Conditions ```php final readonly class AndCondition implements FieldConditionContract { public function __construct( private FieldCondition $left, private FieldCondition $right ) {} public function matches(array $formData): bool { return $this->left->matches($formData) && $this->right->matches($formData); } } // Usage FormFieldDefinition::text('special_field', 'Special') ->showWhen( new AndCondition( FieldCondition::equals('account_type', 'business'), FieldCondition::equals('country', 'DE') ) ); ``` ### Custom Renderers ```php interface FieldRenderer { public function render(FormFieldDefinition $field, mixed $value): string; } final readonly class CustomTextRenderer implements FieldRenderer { public function render(FormFieldDefinition $field, mixed $value): string { // Custom rendering logic } } ``` ## Vergleich: Vorher vs. Nachher ### ❌ Vorher (Code-Duplikat) ```php // Hardcoded Form in Component public function getRenderData(): ComponentRenderData { return new ComponentRenderData( templatePath: 'livecomponent-dynamic-form', data: [ 'form_first_name' => $formData['first_name'] ?? '', 'form_last_name' => $formData['last_name'] ?? '', // ... 50 weitere Zeilen hardcoded mappings ] ); } ``` ### ✅ Nachher (Wiederverwendbar) ```php // Deklarative Definition $formDef = new MultiStepFormDefinition( steps: [ new FormStepDefinition( title: 'Personal Info', fields: [ FormFieldDefinition::text('first_name', 'First Name'), FormFieldDefinition::text('last_name', 'Last Name') ] ) ] ); // Generic Component - keine Anpassungen nötig! $component = new MultiStepFormComponent( id: ComponentId::generate('my-form'), formDefinition: $formDef ); ``` ## Zusammenfassung Diese Integration: - ✅ Nutzt bestehenden `FormBuilder` (keine Code-Duplizierung) - ✅ Erweitert ihn minimal für LiveComponent-Features - ✅ Vollständig type-safe mit Value Objects - ✅ Framework-compliant (readonly, final, composition) - ✅ Deklarative Form-Definitionen - ✅ Conditional Fields eingebaut - ✅ Wiederverwendbare Validators - ✅ Generische Component (kein Custom-Code pro Form) - ✅ Einfach testbar - ✅ Leicht erweiterbar