- 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.
765 lines
19 KiB
Markdown
765 lines
19 KiB
Markdown
# XComponentProcessor - Unified Component Syntax
|
|
|
|
**Einheitliche `<x-*>` Syntax für LiveComponents und HTML Components im Custom PHP Framework.**
|
|
|
|
## Übersicht
|
|
|
|
Der `XComponentProcessor` ist ein DOM-Processor, der eine einheitliche Syntax für **zwei verschiedene Component-Typen** bietet:
|
|
|
|
1. **LiveComponents** (Interaktiv/Stateful) - Komponenten mit #[LiveComponent] Attribut
|
|
2. **HTML Components** (Statisch) - Komponenten mit #[ComponentName] Attribut
|
|
|
|
**Unified Syntax**: `<x-componentname>`
|
|
|
|
```html
|
|
<!-- LiveComponent (Interactive) -->
|
|
<x-datatable id="users" page="1" pageSize="25" />
|
|
|
|
<!-- HTML Component (Static) -->
|
|
<x-button variant="primary">Click me</x-button>
|
|
```
|
|
|
|
## Architektur
|
|
|
|
### Component Auto-Detection Flow
|
|
|
|
```
|
|
<x-componentname> Syntax
|
|
↓
|
|
XComponentProcessor
|
|
↓
|
|
┌─────┴─────┐
|
|
↓ ↓
|
|
LiveComponent? HTML Component?
|
|
↓ ↓
|
|
YES NO
|
|
↓ ↓
|
|
Process as Process as
|
|
Interactive Static
|
|
```
|
|
|
|
**Detection Priority**:
|
|
1. **LiveComponent** prüfen (via `ComponentRegistry::isRegistered()`)
|
|
2. **HTML Component** prüfen (via `HtmlComponentRegistry::has()`)
|
|
3. **Not Found** → Helpful error message
|
|
|
|
### Klassen-Struktur
|
|
|
|
```php
|
|
final readonly class XComponentProcessor implements DomProcessor
|
|
{
|
|
public function __construct(
|
|
private ComponentRegistry $liveComponentRegistry,
|
|
private HtmlComponentRegistry $htmlComponentRegistry,
|
|
private ComponentMetadataCache $metadataCache,
|
|
private DomComponentService $componentService
|
|
) {}
|
|
|
|
public function process(DomWrapper $dom, RenderContext $context): DomWrapper
|
|
{
|
|
// Find all <x-*> elements
|
|
$xComponents = $this->findXComponents($dom);
|
|
|
|
foreach ($xComponents as $element) {
|
|
try {
|
|
$this->processXComponent($dom, $element, $context);
|
|
} catch (\Throwable $e) {
|
|
$this->handleProcessingError($dom, $element, $e);
|
|
}
|
|
}
|
|
|
|
return $dom;
|
|
}
|
|
}
|
|
```
|
|
|
|
## LiveComponents (Interactive)
|
|
|
|
### Verwendung
|
|
|
|
```html
|
|
<!-- LiveComponent mit Props -->
|
|
<x-datatable
|
|
id="users-table"
|
|
page="1"
|
|
pageSize="25"
|
|
sortBy="created_at"
|
|
sortOrder="desc"
|
|
/>
|
|
|
|
<!-- LiveComponent ohne explizite ID (auto-generiert) -->
|
|
<x-counter initialValue="0" />
|
|
|
|
<!-- Mit JSON Props -->
|
|
<x-chart
|
|
data='[{"x":1,"y":10},{"x":2,"y":20}]'
|
|
options='{"type":"line","legend":true}'
|
|
/>
|
|
```
|
|
|
|
### LiveComponent Definition
|
|
|
|
```php
|
|
use App\Framework\LiveComponents\Attributes\LiveComponent;
|
|
use App\Framework\LiveComponents\Attributes\LiveProp;
|
|
|
|
#[LiveComponent(name: 'datatable')]
|
|
final class DataTableComponent
|
|
{
|
|
#[LiveProp]
|
|
public int $page = 1;
|
|
|
|
#[LiveProp]
|
|
public int $pageSize = 25;
|
|
|
|
#[LiveProp]
|
|
public string $sortBy = 'id';
|
|
|
|
#[LiveProp]
|
|
public string $sortOrder = 'asc';
|
|
|
|
public function render(): RenderData
|
|
{
|
|
$data = $this->loadData();
|
|
|
|
return new RenderData(
|
|
template: 'components/datatable',
|
|
data: [
|
|
'rows' => $data,
|
|
'currentPage' => $this->page,
|
|
'totalPages' => $this->calculateTotalPages()
|
|
]
|
|
);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Processing Flow
|
|
|
|
```
|
|
<x-datatable id="users" page="1" pageSize="25" />
|
|
↓
|
|
1. Extract component name: "datatable"
|
|
2. Check ComponentRegistry: isRegistered("datatable") → true
|
|
3. Extract props: ["id" => "users", "page" => "1", "pageSize" => "25"]
|
|
4. Type coercion: ["id" => "users", "page" => 1, "pageSize" => 25]
|
|
5. Validate props gegen ComponentMetadata
|
|
6. Create ComponentId: ComponentId::create("datatable", "users")
|
|
7. Create ComponentData: ComponentData::fromArray(["page" => 1, "pageSize" => 25])
|
|
8. Resolve component: $registry->resolve($componentId, $initialState)
|
|
9. Render with wrapper: $registry->renderWithWrapper($component)
|
|
10. Replace <x-datatable> with rendered HTML
|
|
↓
|
|
<div data-component-id="datatable:users" data-component-state="...">
|
|
<!-- Rendered component HTML -->
|
|
</div>
|
|
```
|
|
|
|
### Type Coercion (LiveComponents only)
|
|
|
|
Der XComponentProcessor führt automatische **Type Coercion** für LiveComponent-Props durch:
|
|
|
|
```php
|
|
// Input: HTML Attributes (always strings)
|
|
<x-component
|
|
stringProp="text"
|
|
intProp="123"
|
|
floatProp="12.5"
|
|
boolTrue="true"
|
|
boolFalse="false"
|
|
nullProp="null"
|
|
arrayProp="[1,2,3]"
|
|
objectProp='{"key":"value"}'
|
|
/>
|
|
|
|
// Output: Coerced PHP types
|
|
[
|
|
'stringProp' => 'text', // string
|
|
'intProp' => 123, // int
|
|
'floatProp' => 12.5, // float
|
|
'boolTrue' => true, // bool
|
|
'boolFalse' => false, // bool
|
|
'nullProp' => null, // null
|
|
'arrayProp' => [1, 2, 3], // array
|
|
'objectProp' => ['key' => 'value'] // array
|
|
]
|
|
```
|
|
|
|
**Coercion Rules**:
|
|
- `"true"` / `"false"` → `bool`
|
|
- `"null"` → `null`
|
|
- `"123"` → `int`
|
|
- `"12.5"` → `float`
|
|
- `"[...]"` / `"{...}"` → JSON decode
|
|
- Alles andere → `string`
|
|
|
|
### Prop Validation
|
|
|
|
```php
|
|
// Component Definition
|
|
#[LiveComponent(name: 'counter')]
|
|
final class CounterComponent
|
|
{
|
|
#[LiveProp]
|
|
public int $initialValue = 0;
|
|
|
|
#[LiveProp]
|
|
public int $step = 1;
|
|
}
|
|
|
|
// ✅ Valid Usage
|
|
<x-counter initialValue="10" step="2" />
|
|
|
|
// ❌ Invalid: Unknown prop 'invalidProp'
|
|
<x-counter invalidProp="test" />
|
|
// → Development: Error message
|
|
// → Production: Silent removal
|
|
|
|
// ❌ Invalid: Wrong type (caught at component level)
|
|
<x-counter initialValue="not-a-number" />
|
|
// → Type coercion to string, component validation throws error
|
|
```
|
|
|
|
**Validation Error (Development Mode)**:
|
|
```html
|
|
<div style="border:2px solid red;padding:1rem;background:#fee;color:#c00;">
|
|
<strong>XComponentProcessor Error:</strong><br>
|
|
<pre>LiveComponent 'counter' has no property 'invalidProp'.
|
|
Available properties: initialValue, step</pre>
|
|
<small>Component: x-counter</small>
|
|
</div>
|
|
```
|
|
|
|
## HTML Components (Static)
|
|
|
|
### Verwendung
|
|
|
|
```html
|
|
<!-- Static Button Component -->
|
|
<x-button variant="primary">Click me</x-button>
|
|
|
|
<!-- Card Component with content -->
|
|
<x-card title="User Profile">
|
|
<p>User information goes here</p>
|
|
</x-card>
|
|
|
|
<!-- Badge Component -->
|
|
<x-badge color="blue">New</x-badge>
|
|
```
|
|
|
|
### HTML Component Definition
|
|
|
|
```php
|
|
use App\Framework\View\Attributes\ComponentName;
|
|
|
|
#[ComponentName('button')]
|
|
final readonly class ButtonComponent
|
|
{
|
|
public function render(string $content, array $attributes): string
|
|
{
|
|
$variant = $attributes['variant'] ?? 'default';
|
|
$class = "btn btn-{$variant}";
|
|
|
|
return "<button class=\"{$class}\">{$content}</button>";
|
|
}
|
|
}
|
|
```
|
|
|
|
### Processing Flow
|
|
|
|
```
|
|
<x-button variant="primary">Click me</x-button>
|
|
↓
|
|
1. Extract component name: "button"
|
|
2. Check ComponentRegistry: isRegistered("button") → false
|
|
3. Check HtmlComponentRegistry: has("button") → true
|
|
4. Extract content: "Click me"
|
|
5. Extract attributes: ["variant" => "primary"] (as strings)
|
|
6. Render: $registry->render("button", "Click me", ["variant" => "primary"])
|
|
7. Replace <x-button> with rendered HTML
|
|
↓
|
|
<button class="btn btn-primary">Click me</button>
|
|
```
|
|
|
|
### Unterschiede zu LiveComponents
|
|
|
|
**HTML Components**:
|
|
- ❌ **Keine Type Coercion** - alle Attribute bleiben strings
|
|
- ❌ **Keine Prop Validation** - Component entscheidet selbst
|
|
- ❌ **Kein State Management** - rein statisches Rendering
|
|
- ❌ **Keine ComponentId/ComponentData** - nur template rendering
|
|
- ✅ **Schneller** - keine Metadata-Prüfung, kein State-Handling
|
|
- ✅ **Einfacher** - nur `render(content, attributes)` Method
|
|
|
|
**LiveComponents**:
|
|
- ✅ **Type Coercion** für alle Props
|
|
- ✅ **Prop Validation** gegen Metadata
|
|
- ✅ **State Management** mit ComponentData
|
|
- ✅ **Interactive** mit AJAX/WebSocket updates
|
|
- ⚠️ **Komplexer** - mehr Overhead für Metadata/State
|
|
|
|
## Component Registration
|
|
|
|
### LiveComponent Registration
|
|
|
|
```php
|
|
// Automatisch via #[LiveComponent] Attribute Discovery
|
|
use App\Framework\LiveComponents\Attributes\LiveComponent;
|
|
|
|
#[LiveComponent(name: 'datatable')]
|
|
final class DataTableComponent implements LiveComponentContract
|
|
{
|
|
// Component implementation
|
|
}
|
|
|
|
// Framework registriert automatisch bei Bootstrap:
|
|
// ComponentRegistry::register('datatable', DataTableComponent::class)
|
|
```
|
|
|
|
### HTML Component Registration
|
|
|
|
```php
|
|
// Automatisch via #[ComponentName] Attribute Discovery
|
|
use App\Framework\View\Attributes\ComponentName;
|
|
|
|
#[ComponentName('button')]
|
|
final readonly class ButtonComponent
|
|
{
|
|
public function render(string $content, array $attributes): string
|
|
{
|
|
// Rendering logic
|
|
}
|
|
}
|
|
|
|
// Framework registriert automatisch:
|
|
// HtmlComponentRegistry::register('button', ButtonComponent::class)
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
### Component Not Found
|
|
|
|
```html
|
|
<x-unknown-component />
|
|
```
|
|
|
|
**Development Mode**:
|
|
```html
|
|
<div style="border:2px solid red;padding:1rem;background:#fee;color:#c00;">
|
|
<strong>XComponentProcessor Error:</strong><br>
|
|
<pre>Unknown component: <x-unknown-component>
|
|
|
|
Available LiveComponents: datatable, counter, chart
|
|
Available HTML Components: button, card, badge</pre>
|
|
<small>Component: x-unknown-component</small>
|
|
</div>
|
|
```
|
|
|
|
**Production Mode**:
|
|
- Element wird **silently removed**
|
|
- Kein Error-HTML im Output
|
|
- Optional: Logging via `error_log()`
|
|
|
|
### Invalid Props (LiveComponents)
|
|
|
|
```html
|
|
<x-counter invalidProp="test" />
|
|
```
|
|
|
|
**Development Mode**:
|
|
```html
|
|
<div style="border:2px solid red;">
|
|
<strong>XComponentProcessor Error:</strong><br>
|
|
<pre>LiveComponent 'counter' has no property 'invalidProp'.
|
|
Available properties: initialValue, step</pre>
|
|
<small>Component: x-counter</small>
|
|
</div>
|
|
```
|
|
|
|
**Production Mode**:
|
|
- Component wird **nicht gerendert**
|
|
- Silent removal
|
|
- Optional: Logging
|
|
|
|
### Rendering Errors
|
|
|
|
Wenn `ComponentRegistry::renderWithWrapper()` oder `HtmlComponentRegistry::render()` fehlschlägt:
|
|
|
|
**Development Mode**: Error message with stack trace
|
|
**Production Mode**: Silent removal + logging
|
|
|
|
## Auto-Detection Priority
|
|
|
|
**Wenn Component in BEIDEN Registries existiert**:
|
|
|
|
```
|
|
LiveComponent Priority > HTML Component
|
|
```
|
|
|
|
**Beispiel**:
|
|
```php
|
|
// BOTH registered:
|
|
#[LiveComponent(name: 'button')]
|
|
class ButtonLiveComponent { ... }
|
|
|
|
#[ComponentName('button')]
|
|
class ButtonHtmlComponent { ... }
|
|
|
|
// Usage:
|
|
<x-button id="my-button" />
|
|
// → Processed as LiveComponent (Priority!)
|
|
```
|
|
|
|
**Best Practice**: Verwende unterschiedliche Namen für LiveComponents und HTML Components, um Verwirrung zu vermeiden.
|
|
|
|
## Performance Characteristics
|
|
|
|
### LiveComponent Processing
|
|
|
|
- **Prop Extraction**: O(n) wo n = Anzahl Attribute
|
|
- **Type Coercion**: O(n) wo n = Anzahl Props
|
|
- **Prop Validation**: O(n) wo n = Anzahl Props (Metadata lookup cached)
|
|
- **Component Resolution**: Container DI Resolution
|
|
- **Rendering**: Depends on component logic
|
|
|
|
**Durchschnittliche Latenz**: ~5-15ms pro LiveComponent
|
|
|
|
### HTML Component Processing
|
|
|
|
- **Attribute Extraction**: O(n) wo n = Anzahl Attribute
|
|
- **Content Extraction**: O(1)
|
|
- **Rendering**: Component-spezifische Logic
|
|
|
|
**Durchschnittliche Latenz**: ~1-5ms pro HTML Component
|
|
|
|
### Optimization Strategies
|
|
|
|
```php
|
|
// 1. ComponentMetadataCache nutzen (automatisch)
|
|
$metadata = $this->metadataCache->get($className);
|
|
|
|
// 2. Registry Lookups cachen (automatisch)
|
|
if ($this->liveComponentRegistry->isRegistered($name)) { ... }
|
|
|
|
// 3. Batch-Processing für multiple Components
|
|
foreach ($xComponents as $element) {
|
|
$this->processXComponent($dom, $element, $context);
|
|
}
|
|
```
|
|
|
|
## Framework Integration
|
|
|
|
### DomProcessingPipeline Integration
|
|
|
|
```php
|
|
// In TemplateRendererInitializer
|
|
$domProcessors = [
|
|
ForProcessor::class, // Priority 100
|
|
IfProcessor::class, // Priority 200
|
|
XComponentProcessor::class, // Priority 300 ← Here
|
|
ComponentProcessor::class, // Priority 400 (legacy <component>)
|
|
IncludeProcessor::class, // Priority 500
|
|
// ...
|
|
];
|
|
```
|
|
|
|
**Processing Order**:
|
|
1. `<for>` loops werden zuerst verarbeitet
|
|
2. `<if>` conditionals danach
|
|
3. **`<x-*>` Components** werden resolved
|
|
4. Legacy `<component>` tags (fallback)
|
|
5. `<include>` templates einbinden
|
|
|
|
### RenderContext Integration
|
|
|
|
```php
|
|
$context = RenderContext::create([
|
|
'users' => $users,
|
|
'currentPage' => $page
|
|
]);
|
|
|
|
// XComponentProcessor hat Zugriff auf $context->data
|
|
// Aber: Props werden via Attribute übergeben, nicht via Context
|
|
```
|
|
|
|
## Testing
|
|
|
|
### Unit Tests
|
|
|
|
Der XComponentProcessor hat **umfassende Test-Coverage**:
|
|
|
|
```php
|
|
describe('XComponentProcessor', function () {
|
|
describe('LiveComponent Processing', function () {
|
|
it('processes LiveComponent with basic props');
|
|
it('coerces prop types correctly');
|
|
it('validates props against ComponentMetadata');
|
|
it('generates unique ID if not provided');
|
|
});
|
|
|
|
describe('HTML Component Processing', function () {
|
|
it('processes HTML Component');
|
|
it('extracts content from HTML Component');
|
|
});
|
|
|
|
describe('Error Handling', function () {
|
|
it('shows helpful error when component not found');
|
|
it('removes component silently in production');
|
|
});
|
|
|
|
describe('Auto-Detection Priority', function () {
|
|
it('prioritizes LiveComponent over HTML Component');
|
|
});
|
|
});
|
|
```
|
|
|
|
**Test File**: `tests/Unit/Framework/View/Processors/XComponentProcessorTest.php`
|
|
|
|
### Integration Testing
|
|
|
|
```php
|
|
// Full Template Processing mit XComponentProcessor
|
|
$template = <<<HTML
|
|
<html>
|
|
<body>
|
|
<h1>Dashboard</h1>
|
|
<x-datatable id="users" page="1" />
|
|
<x-button variant="primary">Add User</x-button>
|
|
</body>
|
|
</html>
|
|
HTML;
|
|
|
|
$context = RenderContext::create([]);
|
|
$rendered = $this->templateProcessor->render($context, $template);
|
|
|
|
// Assert: LiveComponent rendered with data-component-id
|
|
expect($rendered)->toContain('data-component-id="datatable:users"');
|
|
|
|
// Assert: HTML Component rendered as button
|
|
expect($rendered)->toContain('<button class="btn btn-primary">');
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### 1. Naming Conventions
|
|
|
|
```php
|
|
// ✅ Good: Clear, descriptive names
|
|
#[LiveComponent(name: 'user-datatable')]
|
|
#[LiveComponent(name: 'product-filter')]
|
|
#[ComponentName('primary-button')]
|
|
|
|
// ❌ Bad: Generic or conflicting names
|
|
#[LiveComponent(name: 'table')] // Too generic
|
|
#[ComponentName('button')] // Conflicts with LiveComponent 'button'
|
|
```
|
|
|
|
### 2. Prop Design (LiveComponents)
|
|
|
|
```php
|
|
// ✅ Good: Specific, typed props
|
|
#[LiveComponent(name: 'datatable')]
|
|
final class DataTableComponent
|
|
{
|
|
#[LiveProp]
|
|
public int $page = 1;
|
|
|
|
#[LiveProp]
|
|
public int $pageSize = 25;
|
|
|
|
#[LiveProp]
|
|
public string $sortBy = 'id';
|
|
}
|
|
|
|
// ❌ Bad: Generic array props (lose type safety)
|
|
#[LiveProp]
|
|
public array $config = []; // What keys? What types?
|
|
```
|
|
|
|
### 3. ID Management
|
|
|
|
```php
|
|
// ✅ Good: Explicit IDs for wichtige Components
|
|
<x-datatable id="users-table" page="1" />
|
|
<x-datatable id="products-table" page="1" />
|
|
|
|
// ⚠️ OK: Auto-generated IDs für einmalige Components
|
|
<x-counter initialValue="0" />
|
|
// → "counter-a3f2d8e1" (auto-generated)
|
|
|
|
// ❌ Bad: Duplicate IDs
|
|
<x-counter id="counter-1" />
|
|
<x-counter id="counter-1" /> // ID collision!
|
|
```
|
|
|
|
### 4. Content vs Props
|
|
|
|
```php
|
|
// ✅ HTML Component: Content via innerHTML
|
|
<x-button variant="primary">Click me</x-button>
|
|
|
|
// ✅ LiveComponent: Data via Props
|
|
<x-datatable data='[{"id":1,"name":"Alice"}]' />
|
|
|
|
// ❌ Bad: Mixing approaches
|
|
<x-datatable>
|
|
<!-- Content ignored for LiveComponents! -->
|
|
Some data here
|
|
</x-datatable>
|
|
```
|
|
|
|
### 5. Error Handling
|
|
|
|
```php
|
|
// ✅ Good: Graceful error handling in component
|
|
#[LiveComponent(name: 'chart')]
|
|
final class ChartComponent
|
|
{
|
|
#[LiveProp]
|
|
public array $data = [];
|
|
|
|
public function render(): RenderData
|
|
{
|
|
if (empty($this->data)) {
|
|
return new RenderData(
|
|
template: 'components/chart-empty',
|
|
data: ['message' => 'No data available']
|
|
);
|
|
}
|
|
|
|
return new RenderData(
|
|
template: 'components/chart',
|
|
data: ['chartData' => $this->processData()]
|
|
);
|
|
}
|
|
}
|
|
|
|
// ❌ Bad: Throwing exceptions ohne Fallback
|
|
public function render(): RenderData
|
|
{
|
|
if (empty($this->data)) {
|
|
throw new \Exception('No data!'); // User sieht error!
|
|
}
|
|
// ...
|
|
}
|
|
```
|
|
|
|
## Migration Guide
|
|
|
|
### Von Legacy `<component>` zu `<x-*>` Syntax
|
|
|
|
**Vorher** (Legacy Syntax):
|
|
```html
|
|
<component name="button" variant="primary">Click me</component>
|
|
```
|
|
|
|
**Nachher** (Unified Syntax):
|
|
```html
|
|
<x-button variant="primary">Click me</x-button>
|
|
```
|
|
|
|
**Migration Steps**:
|
|
|
|
1. **HTML Components**: Einfach Syntax ändern
|
|
```html
|
|
<!-- Old -->
|
|
<component name="card" title="Profile">Content</component>
|
|
|
|
<!-- New -->
|
|
<x-card title="Profile">Content</x-card>
|
|
```
|
|
|
|
2. **LiveComponents**: Prüfen ob #[LiveComponent] Attribute vorhanden
|
|
```php
|
|
// Ensure component is registered
|
|
#[LiveComponent(name: 'datatable')]
|
|
final class DataTableComponent { ... }
|
|
```
|
|
|
|
3. **Props**: Gleiche Attribute-Namen funktionieren
|
|
```html
|
|
<!-- Old -->
|
|
<component name="datatable" page="1" pageSize="25" />
|
|
|
|
<!-- New -->
|
|
<x-datatable page="1" pageSize="25" />
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Problem: Component wird nicht gefunden
|
|
|
|
```
|
|
Unknown component: <x-my-component>
|
|
Available LiveComponents: datatable, counter
|
|
Available HTML Components: button, card
|
|
```
|
|
|
|
**Lösung**:
|
|
1. Prüfe Attribute: `#[LiveComponent(name: 'my-component')]` oder `#[ComponentName('my-component')]`
|
|
2. Prüfe Auto-Discovery: `php console.php framework:discover`
|
|
3. Cache leeren: `php console.php cache:clear`
|
|
|
|
### Problem: Props werden nicht übergeben
|
|
|
|
```html
|
|
<x-counter initialValue="10" />
|
|
<!-- Component erhält initialValue nicht -->
|
|
```
|
|
|
|
**Lösung**:
|
|
1. Prüfe `#[LiveProp]` Attribute auf Property
|
|
2. Prüfe ComponentMetadata Cache
|
|
3. Development Mode aktivieren für Validation Errors
|
|
|
|
### Problem: Type Coercion funktioniert nicht
|
|
|
|
```html
|
|
<x-chart data="[1,2,3]" />
|
|
<!-- Component erhält string statt array -->
|
|
```
|
|
|
|
**Lösung**:
|
|
1. **HTML Component?** → Keine Type Coercion, nur LiveComponents!
|
|
2. JSON valid? → Teste mit `json_decode('[1,2,3]')`
|
|
3. Alternativ: Props als separate Attribute
|
|
|
|
### Problem: Component rendered nicht in Production
|
|
|
|
**Ursache**: Silent error handling in production mode
|
|
|
|
**Lösung**:
|
|
1. Development Mode aktivieren: `APP_ENV=development`
|
|
2. Error logs prüfen
|
|
3. Component-Validation durchführen
|
|
|
|
## Framework-Compliance
|
|
|
|
Der XComponentProcessor folgt **allen Framework-Patterns**:
|
|
|
|
✅ **readonly class** - Unveränderlichkeit
|
|
✅ **final** - Keine Vererbung
|
|
✅ **Composition** - Dependencies via Constructor Injection
|
|
✅ **Interface Implementation** - `DomProcessor` Interface
|
|
✅ **Value Objects** - `ComponentId`, `ComponentData`, `RenderData`
|
|
✅ **Attribute Discovery** - Automatische Component Registration
|
|
✅ **Error Handling** - Graceful degradation (dev/prod modes)
|
|
✅ **Type Safety** - Strict types via `declare(strict_types=1)`
|
|
|
|
## Zusammenfassung
|
|
|
|
**XComponentProcessor** bietet:
|
|
- ✅ **Unified Syntax** - `<x-*>` für alle Component-Typen
|
|
- ✅ **Auto-Detection** - LiveComponent vs HTML Component
|
|
- ✅ **Type Coercion** - Automatische Typ-Konvertierung (LiveComponents)
|
|
- ✅ **Prop Validation** - Gegen ComponentMetadata (LiveComponents)
|
|
- ✅ **Error Handling** - Development vs Production Modes
|
|
- ✅ **Performance** - Optimized mit Caching
|
|
- ✅ **Framework-Compliant** - Alle Patterns korrekt umgesetzt
|
|
|
|
**Use Cases**:
|
|
- **Interactive Components**: DataTables, Forms, Counters, Charts
|
|
- **Static Components**: Buttons, Cards, Badges, Icons
|
|
- **Unified Developer Experience**: Eine Syntax für beide Typen
|
|
- **Progressive Enhancement**: Start mit HTML Component, upgrade zu LiveComponent
|