Files
michaelschiemer/docs/livecomponents/livecomponent-slot-system.md
Michael Schiemer 36ef2a1e2c
Some checks failed
🚀 Build & Deploy Image / Determine Build Necessity (push) Failing after 10m14s
🚀 Build & Deploy Image / Build Runtime Base Image (push) Has been skipped
🚀 Build & Deploy Image / Build Docker Image (push) Has been skipped
🚀 Build & Deploy Image / Run Tests & Quality Checks (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Staging (push) Has been skipped
🚀 Build & Deploy Image / Auto-deploy to Production (push) Has been skipped
Security Vulnerability Scan / Check for Dependency Changes (push) Failing after 11m25s
Security Vulnerability Scan / Composer Security Audit (push) Has been cancelled
fix: Gitea Traefik routing and connection pool optimization
- Remove middleware reference from Gitea Traefik labels (caused routing issues)
- Optimize Gitea connection pool settings (MAX_IDLE_CONNS=30, authentication_timeout=180s)
- Add explicit service reference in Traefik labels
- Fix intermittent 504 timeouts by improving PostgreSQL connection handling

Fixes Gitea unreachability via git.michaelschiemer.de
2025-11-09 14:46:15 +01:00

1128 lines
28 KiB
Markdown

# LiveComponents Slot System
Umfassende Dokumentation des Slot Systems für flexible Component-Komposition im Custom PHP Framework.
## Übersicht
Das Slot System ermöglicht es Components, flexible Bereiche zu definieren, in die Parent Components ihren eigenen Content einfügen können - ähnlich wie **Vue's Slots** oder **React's children/render props** Pattern.
**Key Features:**
-**Named Slots** - Spezifische Placement Points (header, footer, sidebar)
-**Default Slots** - Unnamed slot für allgemeinen Content
-**Scoped Slots** - Slots mit Zugriff auf Component-Daten via Context
-**Required Slots** - Validation dass notwendige Slots gefüllt sind
-**Default Content** - Fallback-Content wenn Slot nicht gefüllt wird
-**Content Processing** - Automatische Wrapper und Transformationen
-**Custom Validation** - Component-spezifische Slot-Validierung
-**XSS Protection** - Automatisches HTML-Escaping in Context-Values
## Architektur
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ SlotDefinition │───▶│ SlotManager │───▶│ SlotProcessor │
│ (What exists) │ │ (Resolution) │ │ (Rendering) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
SlotContent SupportsSlots DomProcessor
(Provided) (Component) (Template)
│ │ │
SlotContext ──────────────────────────────────────────┘
(Scoped Data)
```
## Kern-Komponenten
### 1. SlotDefinition - Slot-Definition Value Object
Definiert welche Slots in einer Component verfügbar sind.
```php
final readonly class SlotDefinition
{
public function __construct(
public string $name, // Slot name (z.B. 'header', 'footer')
public string $defaultContent = '', // Fallback content
public array $props = [], // Props für scoped slots
public bool $required = false // Pflicht-Slot?
) {}
}
```
**Factory Methods:**
```php
// Default (unnamed) Slot
SlotDefinition::default('<p>Default content</p>');
// Named Slot
SlotDefinition::named('header', '<h1>Default Header</h1>');
// Scoped Slot mit Props
SlotDefinition::scoped('content', ['userId', 'userName'], '<p>Default</p>');
// Required Slot
SlotDefinition::named('body')->withRequired(true);
```
**Beispiel:**
```php
public function getSlotDefinitions(): array
{
return [
SlotDefinition::named('header', '<h2>Default Header</h2>'),
SlotDefinition::named('body')->withRequired(true),
SlotDefinition::scoped('footer', ['closeFunction'], '<button onclick="{closeFunction}">Close</button>'),
];
}
```
---
### 2. SlotContent - Slot-Content Value Object
Repräsentiert den Content, der einen Slot füllt.
```php
final readonly class SlotContent
{
public function __construct(
public string $slotName, // Name des Slots
public string $content, // HTML/Text content
public array $data = [], // Data für scoped slots
public ?ComponentId $componentId = null // Optional: Component reference
) {}
}
```
**Factory Methods:**
```php
// Default slot content
SlotContent::default('<p>Main content</p>');
// Named slot content
SlotContent::named('header', '<h1>Custom Header</h1>');
// Named slot with data (for scoped slots)
SlotContent::named('content', '<p>{userName}</p>', ['userName' => 'John']);
// Content from component
SlotContent::fromComponent('header', $componentId, '<h1>Header</h1>');
```
**Hilfsmethoden:**
```php
$content = SlotContent::named('header', '<h1>Header</h1>');
$content->isDefault(); // false
$content->isEmpty(); // false
$content->hasData(); // false
$content->isFromComponent(); // false
// Content transformieren
$updated = $content->withContent('<h2>New Header</h2>');
$withData = $content->withData(['key' => 'value']);
```
---
### 3. SlotContext - Scoped Slot Context
Provides context/data to scoped slots, ermöglicht Parent Zugriff auf Child-Component-Daten.
```php
final readonly class SlotContext
{
public function __construct(
public array $data = [], // Context data
public array $metadata = [] // Additional metadata
) {}
}
```
**Factory Methods:**
```php
// Empty context
SlotContext::empty();
// Context with data
SlotContext::create([
'userId' => 123,
'userName' => 'John Doe',
'isAdmin' => true,
]);
// Context with metadata
SlotContext::create(
data: ['userId' => 123],
metadata: ['timestamp' => time()]
);
```
**Fluent API:**
```php
$context = SlotContext::empty()
->with('userId', 123)
->with('userName', 'John Doe')
->withData(['role' => 'admin'])
->withMetadata(['version' => '1.0']);
// Zugriff
$userId = $context->get('userId'); // 123
$role = $context->get('role', 'guest'); // 'admin'
$hasUser = $context->has('userId'); // true
// Manipulation
$updated = $context->without('role');
$merged = $context1->merge($context2);
```
---
### 4. SupportsSlots Interface
Components die Slots unterstützen müssen dieses Interface implementieren.
```php
interface SupportsSlots
{
/**
* Get slot definitions for this component
* @return array<SlotDefinition>
*/
public function getSlotDefinitions(): array;
/**
* Get context data for a specific slot (scoped slots)
*/
public function getSlotContext(string $slotName): SlotContext;
/**
* Process slot content before rendering
*/
public function processSlotContent(SlotContent $content): SlotContent;
/**
* Validate that required slots are filled
* @return array<string> Validation errors (empty if valid)
*/
public function validateSlots(array $providedSlots): array;
}
```
**Implementierung:**
```php
final readonly class CardComponent implements LiveComponent, SupportsSlots
{
public function getSlotDefinitions(): array
{
return [
SlotDefinition::named('header', '<div class="card-header-default">Card Header</div>'),
SlotDefinition::named('body')->withRequired(true),
SlotDefinition::named('footer', ''),
];
}
public function getSlotContext(string $slotName): SlotContext
{
// Card doesn't need scoped slots
return SlotContext::empty();
}
public function processSlotContent(SlotContent $content): SlotContent
{
// Apply card-specific CSS classes
$wrappedContent = match ($content->slotName) {
'header' => '<div class="card-header">' . $content->content . '</div>',
'body' => '<div class="card-body">' . $content->content . '</div>',
'footer' => '<div class="card-footer">' . $content->content . '</div>',
default => $content->content
};
return $content->withContent($wrappedContent);
}
public function validateSlots(array $providedSlots): array
{
$errors = [];
// Custom validation: warn if footer without header
$hasHeader = false;
$hasFooter = false;
foreach ($providedSlots as $slot) {
if ($slot->slotName === 'header') $hasHeader = true;
if ($slot->slotName === 'footer') $hasFooter = true;
}
if ($hasFooter && !$hasHeader) {
$errors[] = 'Card footer provided without header - consider adding a header';
}
return $errors;
}
}
```
---
### 5. SlotManager - Core Slot Management
Zentrale Verwaltung von Slot-Resolution, Validation und Rendering.
```php
final class SlotManager
{
/**
* Register slot contents for a component
*/
public function registerSlotContents(ComponentId $componentId, array $contents): void;
/**
* Get registered slot contents for a component
* @return array<SlotContent>
*/
public function getSlotContents(ComponentId $componentId): array;
/**
* Resolve slot content for rendering
* Priority: Provided content > Default content
*/
public function resolveSlotContent(
SupportsSlots $component,
SlotDefinition $definition,
array $providedContents
): string;
/**
* Validate slots for a component
* @return array<string> Validation errors (empty if valid)
*/
public function validateSlots(SupportsSlots $component, array $providedContents): array;
/**
* Check if component has specific slot
*/
public function hasSlot(SupportsSlots $component, string $slotName): bool;
/**
* Get slot definition by name
*/
public function getSlotDefinition(SupportsSlots $component, string $slotName): ?SlotDefinition;
/**
* Clear all registered slot contents
*/
public function clear(): void;
/**
* Get statistics about registered slots
*/
public function getStats(): array;
}
```
**Resolution-Logik:**
1. **Find Provided Content** - Suche SlotContent für Slot-Name
2. **Process Content** - Führe `processSlotContent()` aus
3. **Inject Context** (bei scoped slots) - Ersetze `{context.key}` Placeholders
4. **Return Content** - Oder Default Content wenn nichts provided
**Context Injection:**
```php
// Scoped slot content
$content = '<p>User: {context.userName} (ID: {context.userId})</p>';
// Context data
$context = SlotContext::create([
'userId' => 123,
'userName' => 'John Doe',
]);
// After injection
// Result: '<p>User: John Doe (ID: 123)</p>'
```
---
### 6. SlotProcessor - Template Integration
DomProcessor für Template-Rendering mit Slot-Unterstützung.
```php
final readonly class SlotProcessor implements DomProcessor
{
public function __construct(
private SlotManager $slotManager
) {}
public function process(DomWrapper $dom, RenderContext $context): DomWrapper
{
// Check if component supports slots
if ($component instanceof SupportsSlots) {
return $this->processWithSlotSystem($dom, $context, $component);
}
// Fallback to legacy slot processing
return $this->processLegacySlots($dom, $context);
}
}
```
**Features:**
- ✅ Integration mit SlotManager
- ✅ Backward compatibility (legacy slots)
- ✅ Automatic slot validation
- ✅ Error handling (development vs production)
- ✅ Context injection für scoped slots
---
## Slot-Patterns
### Pattern 1: Named Slots - Basic Layout Composition
**Use Case:** Component mit mehreren spezifischen Bereichen (Header, Body, Footer).
**Component Definition:**
```php
final readonly class CardComponent implements SupportsSlots
{
public function getSlotDefinitions(): array
{
return [
SlotDefinition::named('header', '<div class="card-header-default">Default Header</div>'),
SlotDefinition::named('body')->withRequired(true),
SlotDefinition::named('footer', ''),
];
}
}
```
**Template:**
```html
<div class="card">
<slot name="header">
<div class="card-header-default">Default Header</div>
</slot>
<slot name="body"></slot>
<slot name="footer"></slot>
</div>
```
**Usage:**
```html
<component name="card" id="user-card">
<slot name="header">
<h2>User Profile</h2>
<p>John Doe</p>
</slot>
<slot name="body">
<p><strong>Email:</strong> john@example.com</p>
<p><strong>Role:</strong> Administrator</p>
</slot>
<slot name="footer">
<button>Edit Profile</button>
<button>View Activity</button>
</slot>
</component>
```
**Result:**
- ✅ Custom header mit User-Info
- ✅ Custom body mit Details
- ✅ Custom footer mit Actions
- ✅ Alle Slots mit CSS-Wrappern via `processSlotContent()`
---
### Pattern 2: Default Slot - Unnamed Content
**Use Case:** Container-Component die beliebigen Content aufnimmt.
**Component Definition:**
```php
final readonly class ContainerComponent implements SupportsSlots
{
public function getSlotDefinitions(): array
{
return [
SlotDefinition::default('<div class="empty-container">No content</div>'),
SlotDefinition::named('title'),
SlotDefinition::named('actions', ''),
];
}
}
```
**Template:**
```html
<div class="container">
<slot name="title">
<h2>Container</h2>
</slot>
<!-- Default slot: all unnamed content goes here -->
<slot>
<div class="empty-container">No content provided</div>
</slot>
<slot name="actions"></slot>
</div>
```
**Usage:**
```html
<component name="container" id="wrapper">
<slot name="title">
<h2>Welcome Container</h2>
</slot>
<!-- This goes into the default slot -->
<h1>Welcome to the Platform</h1>
<p>This is the main content.</p>
<p>All unnamed content appears in the default slot.</p>
<slot name="actions">
<button>Get Started</button>
<button>Learn More</button>
</slot>
</component>
```
**Result:**
- ✅ Named slots (title, actions) für spezifischen Content
- ✅ Default slot für flexiblen, unstrukturierten Content
- ✅ Fallback Content wenn Default Slot leer
---
### Pattern 3: Scoped Slots - Component Data Access
**Use Case:** Parent braucht Zugriff auf Child-Component-Daten (z.B. Modal ID, Close Function).
**Component Definition:**
```php
final readonly class ModalComponent implements SupportsSlots
{
public function getSlotDefinitions(): array
{
return [
SlotDefinition::named('title', '<h3>Modal Title</h3>'),
SlotDefinition::scoped('content', ['modalId', 'isOpen', 'closeFunction'])->withRequired(true),
SlotDefinition::scoped('actions', ['closeFunction', 'modalId']),
];
}
public function getSlotContext(string $slotName): SlotContext
{
$modalId = $this->id->toString();
$isOpen = $this->state->get('isOpen', false);
return match ($slotName) {
'content', 'actions' => SlotContext::create([
'modalId' => $modalId,
'isOpen' => $isOpen,
'closeFunction' => "closeModal('{$modalId}')",
]),
default => SlotContext::empty()
};
}
}
```
**Template:**
```html
<div class="modal" data-modal-id="{component.id}">
<slot name="title">
<h3>Modal Title</h3>
</slot>
<!-- Scoped slot: parent can access context -->
<slot name="content"></slot>
<!-- Scoped slot: parent can use closeFunction -->
<slot name="actions">
<button onclick="{context.closeFunction}">Close</button>
</slot>
</div>
```
**Usage:**
```html
<component name="modal" id="confirm-delete-modal">
<slot name="title">
<h3>Confirm Delete</h3>
</slot>
<slot name="content">
<p>Are you sure you want to delete this item?</p>
<p>This action cannot be undone.</p>
<!-- Access component data via {context.key} -->
<p><small>Modal ID: {context.modalId}</small></p>
</slot>
<slot name="actions">
<!-- Use component's close function -->
<button onclick="{context.closeFunction}">Cancel</button>
<button class="btn-danger">Delete</button>
</slot>
</component>
```
**Result:**
- ✅ Parent-Slot-Content hat Zugriff auf Child-Component-Daten
-`{context.modalId}` wird durch echte Modal-ID ersetzt
-`{context.closeFunction}` wird durch `closeModal('modal-id')` ersetzt
- ✅ XSS-Protection: Alle Context-Values werden HTML-escaped
---
### Pattern 4: Complex Layout - Multi-Slot Composition
**Use Case:** Page-Layout mit mehreren Bereichen (Header, Sidebar, Main, Footer).
**Component Definition:**
```php
final readonly class LayoutComponent implements SupportsSlots
{
public function getSlotDefinitions(): array
{
return [
SlotDefinition::scoped('sidebar', ['sidebarWidth', 'isCollapsed'], '<aside>Default Sidebar</aside>'),
SlotDefinition::named('main')->withRequired(true),
SlotDefinition::named('footer', '<footer>Default Footer</footer>'),
SlotDefinition::named('header', ''),
];
}
public function getSlotContext(string $slotName): SlotContext
{
$sidebarWidth = $this->state->get('sidebarWidth', '250px');
$isCollapsed = $this->state->get('sidebarCollapsed', false);
return match ($slotName) {
'sidebar' => SlotContext::create([
'sidebarWidth' => $sidebarWidth,
'isCollapsed' => $isCollapsed,
]),
default => SlotContext::empty()
};
}
}
```
**Usage:**
```html
<component name="layout" id="dashboard-layout">
<slot name="header">
<header class="app-header">
<h1>My Application</h1>
<nav>...</nav>
</header>
</slot>
<slot name="sidebar">
<!-- Scoped: access layout dimensions -->
<aside style="width: {context.sidebarWidth}">
<nav class="sidebar-nav">
<ul>
<li><a href="/dashboard">Dashboard</a></li>
<li><a href="/users">Users</a></li>
<li><a href="/settings">Settings</a></li>
</ul>
</nav>
</aside>
</slot>
<slot name="main">
<main class="main-content">
<h2>Dashboard</h2>
<p>Welcome to your dashboard!</p>
</main>
</slot>
<slot name="footer">
<footer class="app-footer">
<p>&copy; 2025 My Application</p>
</footer>
</slot>
</component>
```
---
## Slot Validation
### Required Slots
```php
SlotDefinition::named('body')->withRequired(true);
```
**Validation:**
- ✅ Automatische Prüfung via `SlotManager::validateSlots()`
- ✅ Fehler wenn Required Slot nicht gefüllt oder leer
- ✅ Validation-Errors als Array zurückgegeben
**Error Handling:**
```php
$errors = $slotManager->validateSlots($component, $providedSlots);
if (!empty($errors)) {
// Development: Show errors
// Production: Log errors
foreach ($errors as $error) {
error_log("Slot validation error: $error");
}
}
```
### Custom Validation
Components können eigene Validation-Logik in `validateSlots()` implementieren:
```php
public function validateSlots(array $providedSlots): array
{
$errors = [];
// Custom logic: warn if footer without header
$hasHeader = false;
$hasFooter = false;
foreach ($providedSlots as $slot) {
if ($slot->slotName === 'header') $hasHeader = true;
if ($slot->slotName === 'footer') $hasFooter = true;
}
if ($hasFooter && !$hasHeader) {
$errors[] = 'Card footer provided without header - visual consistency issue';
}
return $errors;
}
```
---
## Content Processing
### Automatic Wrapping
Components können Slot-Content automatisch wrappen via `processSlotContent()`:
```php
public function processSlotContent(SlotContent $content): SlotContent
{
$wrappedContent = match ($content->slotName) {
'header' => '<div class="card-header">' . $content->content . '</div>',
'body' => '<div class="card-body">' . $content->content . '</div>',
'footer' => '<div class="card-footer">' . $content->content . '</div>',
default => $content->content
};
return $content->withContent($wrappedContent);
}
```
**Result:**
- ✅ Automatische CSS-Wrapper für jeden Slot
- ✅ Konsistentes Styling ohne manuelle Wrapper im Parent
- ✅ Component-spezifische Transformationen
### State-Based Processing
```php
public function processSlotContent(SlotContent $content): SlotContent
{
// Get padding from state
$padding = $this->state->get('padding', 'medium');
$paddingClass = 'container-padding-' . $padding;
$wrappedContent = match ($content->slotName) {
'default' => "<div class=\"container-content {$paddingClass}\">" . $content->content . '</div>',
default => $content->content
};
return $content->withContent($wrappedContent);
}
```
---
## Security
### XSS Protection
**Automatic HTML Escaping** in scoped context values:
```php
private function formatValue(mixed $value): string
{
if (is_scalar($value)) {
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
if (is_array($value)) {
return htmlspecialchars(json_encode($value), ENT_QUOTES, 'UTF-8');
}
return '';
}
```
**Result:**
- ✅ Alle Context-Values werden automatisch escaped
-`<script>``&lt;script&gt;`
- ✅ Schutz vor XSS-Attacken via Scoped Slots
**Beispiel:**
```php
// Malicious context value
$context = SlotContext::create([
'userInput' => '<script>alert("XSS")</script>',
]);
// In slot content
'<p>{context.userInput}</p>'
// After injection (safe!)
'<p>&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;</p>'
```
---
## Best Practices
### 1. Slot Design
**✅ DO:**
- Klare Slot-Namen verwenden (`header`, `body`, `footer` statt `slot1`, `slot2`)
- Default Content für alle Optional Slots definieren
- Required Slots nur für essentiellen Content
- Scoped Slots für Component-Daten die Parent braucht
**❌ DON'T:**
- Zu viele Slots definieren (max. 5-6 für Übersichtlichkeit)
- Alle Slots als Required markieren
- Sensitive Daten in Scoped Context legen
- Komplexe Logik in `processSlotContent()`
### 2. Content Processing
**✅ DO:**
- Automatische Wrapper für konsistentes Styling
- State-basierte Transformationen (padding, themes)
- Slot-spezifische CSS-Klassen
- Immutable Content-Transformationen (`withContent()`)
**❌ DON'T:**
- Content manipulieren ohne `withContent()`
- Seiteneffekte in `processSlotContent()`
- Zu aggressive Transformationen
- Parent-Content überschreiben
### 3. Validation
**✅ DO:**
- Required Slots für kritischen Content
- Custom Validation für logische Konsistenz
- Hilfreiche Error-Messages
- Validation in Development zeigen, in Production loggen
**❌ DON'T:**
- Validation-Errors swallowing
- Generische Error-Messages
- Validation-Logic in Templates
- Performance-intensive Validation
### 4. Scoped Slots
**✅ DO:**
- Nur notwendige Daten in Context legen
- HTML-Escaping ist automatisch (vertrauen)
- Dokumentierte Context-Props in SlotDefinition
- Klare Naming Convention (`{context.key}`)
**❌ DON'T:**
- Sensitive Daten in Context (Passwords, Tokens)
- Zu viele Context-Props (max. 5-6)
- Komplexe Objekte in Context
- Manual HTML-Escaping (ist redundant)
---
## Testing
### Unit Tests - SlotManager
```php
it('resolves provided content over default content', function () {
$component = new TestComponent();
$definition = SlotDefinition::named('header', '<h2>Default</h2>');
$providedContent = [
SlotContent::named('header', '<h1>Custom</h1>'),
];
$result = $this->slotManager->resolveSlotContent(
$component,
$definition,
$providedContent
);
expect($result)->toBe('<h1>Custom</h1>');
});
```
### Integration Tests - Components
```php
it('renders CardComponent with custom slots', function () {
$component = new CardComponent(
id: ComponentId::generate(),
state: ComponentState::fromArray([])
);
$providedSlots = [
SlotContent::named('header', '<h2>User Profile</h2>'),
SlotContent::named('body', '<p>User details...</p>'),
];
$errors = $this->slotManager->validateSlots($component, $providedSlots);
expect($errors)->toBeEmpty();
});
```
### Template Tests
```php
it('processes slots in template', function () {
$html = '<component name="card">
<slot name="header"><h1>Header</h1></slot>
<slot name="body"><p>Body</p></slot>
</component>';
$result = $this->templateRenderer->render($html, [
'component' => new CardComponent(...),
]);
expect($result)->toContain('card-header');
expect($result)->toContain('card-body');
});
```
---
## Troubleshooting
### Problem: Slot Content wird nicht angezeigt
**Ursache:** Slot-Name stimmt nicht überein.
**Lösung:**
```php
// Check slot definitions
$slotNames = $this->slotManager->getSlotNames($component);
var_dump($slotNames); // ['header', 'body', 'footer']
// Verify provided content
foreach ($providedSlots as $slot) {
echo $slot->slotName; // Muss mit Definition übereinstimmen
}
```
### Problem: Scoped Context wird nicht injiziert
**Ursache:** Slot nicht als Scoped definiert.
**Lösung:**
```php
// ❌ WRONG: Named slot (no context injection)
SlotDefinition::named('content');
// ✅ CORRECT: Scoped slot with props
SlotDefinition::scoped('content', ['modalId', 'closeFunction']);
```
### Problem: XSS in Context Values
**Ursache:** Custom formatValue() ohne Escaping.
**Lösung:**
```php
// ❌ WRONG: No escaping
private function formatValue(mixed $value): string
{
return (string) $value; // XSS vulnerability!
}
// ✅ CORRECT: Automatic escaping
private function formatValue(mixed $value): string
{
if (is_scalar($value)) {
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
return '';
}
```
### Problem: Required Slot Validation schlägt fehl
**Ursache:** Slot ist empty oder nur Whitespace.
**Lösung:**
```php
// Check if slot is truly empty
$content = SlotContent::named('body', ' ');
if ($content->isEmpty()) {
// Will fail required validation
}
// ✅ Provide actual content
SlotContent::named('body', '<p>Content</p>');
```
---
## Performance
### Slot Resolution Performance
**Typical Performance:**
- Slot Resolution: < 1ms per slot
- Context Injection: < 0.5ms per placeholder
- Validation: < 2ms für 5-6 slots
- Total Overhead: < 5ms per component
**Optimizations:**
- ✅ Slot definitions gecached in Component
- ✅ Keine Reflection für Slot-Discovery
- ✅ Simple string replacement für Context
- ✅ Minimal regex usage
### Best Practices für Performance
**✅ DO:**
- Slot definitions im Constructor definieren
- Simple Context-Values (strings, ints)
- Cached Component-Instances
- Minimal Validation-Logic
**❌ DON'T:**
- Dynamic Slot-Definitions
- Complex Objects in Context
- Database-Queries in `getSlotContext()`
- Expensive Transformations in `processSlotContent()`
---
## Migration Guide
### Von Legacy Slots zu Slot System
**Before (Legacy):**
```php
final class OldCard
{
public function render(array $slots): string
{
$header = $slots['header'] ?? '<h2>Default</h2>';
$body = $slots['body'] ?? '';
return "<div class='card'>$header $body</div>";
}
}
```
**After (Slot System):**
```php
final readonly class NewCard implements SupportsSlots
{
public function getSlotDefinitions(): array
{
return [
SlotDefinition::named('header', '<h2>Default</h2>'),
SlotDefinition::named('body')->withRequired(true),
];
}
public function processSlotContent(SlotContent $content): SlotContent
{
// Automatic wrapping
$wrapped = "<div class='card-{$content->slotName}'>{$content->content}</div>";
return $content->withContent($wrapped);
}
}
```
**Benefits:**
- ✅ Type-safe Slot-Definitionen
- ✅ Automatische Validation
- ✅ Scoped Slots Support
- ✅ Content Processing Hooks
- ✅ Better Developer Experience
---
## Zusammenfassung
Das **Slot System** bietet eine flexible, typsichere Lösung für Component-Komposition:
**✅ Features:**
- Named, Default und Scoped Slots
- Required Slots mit Validation
- Default Content Fallbacks
- Automatic Content Processing
- XSS Protection
- Custom Validation Hooks
- Template Integration
- Backward Compatibility
**✅ Architecture:**
- Value Objects für Type Safety
- SlotManager für Orchestration
- SupportsSlots Interface für Components
- SlotProcessor für Template-Rendering
- Framework-compliant (readonly, final, composition)
**✅ Use Cases:**
- Layout Components (Header/Sidebar/Main/Footer)
- Modal/Dialog Components
- Card/Container Components
- Complex Nested Compositions
- Dynamic Content Injection
**Nächste Schritte:**
- Weitere Example Components erstellen
- Performance Optimizations
- Advanced Caching für Slot-Resolution
- Visual Slot Editor (optional)