- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
13 KiB
13 KiB
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)
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
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
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
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
<!-- pages/register.view.php -->
<div class="registration-page">
<h1>Benutzerregistrierung</h1>
<!-- LiveComponent einbinden -->
<livecomponent name="multi-step-form" data="registration_form" />
</div>
Vorteile dieser Lösung
✅ Kein Code-Duplikat
- Nutzt bestehenden
FormBuilderaus View-Modul LiveFormBuildererweitert 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
FormFieldDefinition::text('company_name', 'Firma', required: true)
->showWhen(FieldCondition::equals('account_type', 'business'))
✅ Wiederverwendbare Validators
final readonly class EmailValidator implements StepValidator
{
public function validate(array $formData): array
{
// Wiederverwendbare Validation-Logic
}
}
✅ Testbar
// 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
// 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
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
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)
// 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)
// 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