# LiveComponents API Reference **Complete API Documentation for LiveComponents System** This reference covers all public classes, methods, attributes, and interfaces for building LiveComponents applications. --- ## Table of Contents 1. [LiveComponent Base Class](#livecomponent-base-class) 2. [Attributes](#attributes) 3. [HTTP Integration](#http-integration) 4. [JavaScript API](#javascript-api) 5. [Value Objects](#value-objects) 6. [Services](#services) 7. [Events](#events) --- ## LiveComponent Base Class ### `abstract class LiveComponent` Base class for all LiveComponents. Provides lifecycle hooks, rendering, and state management. #### Properties ```php /** * Unique component instance ID * Auto-generated on component creation */ protected readonly string $id; /** * Component name (class name without namespace) */ protected readonly string $name; ``` #### Lifecycle Methods ##### `public function mount(array $initialData = []): void` Called when component is first created. **Parameters**: - `$initialData` - Initial data passed to component **Usage**: ```php public function mount(array $initialData = []): void { $this->userId = $initialData['user_id'] ?? null; $this->loadUserData(); $this->enableSse(); // Enable real-time updates } ``` --- ##### `public function hydrate(array $state): void` Called on subsequent requests to restore component state. **Parameters**: - `$state` - Serialized component state from client **Usage**: ```php public function hydrate(array $state): void { // State automatically restored to #[LiveProp] properties // Override for custom hydration logic parent::hydrate($state); $this->recalculateDerivedState(); } ``` --- ##### `public function updated(string $property, mixed $oldValue, mixed $newValue): void` Called when a property is updated via two-way binding. **Parameters**: - `$property` - Property name that changed - `$oldValue` - Previous value - `$newValue` - New value **Usage**: ```php public function updated(string $property, mixed $oldValue, mixed $newValue): void { if ($property === 'searchTerm' && strlen($newValue) >= 3) { $this->performSearch(); } } ``` --- ##### `public function unmount(): void` Called when component is destroyed. **Usage**: ```php public function unmount(): void { // Clean up resources $this->closeConnections(); $this->clearCache(); } ``` --- #### Rendering ##### `abstract public function render(): string` Render component to HTML. **Returns**: HTML string **Usage**: ```php public function render(): string { return $this->view('components/user-profile', [ 'user' => $this->user, 'posts' => $this->posts ]); } ``` --- ##### `protected function view(string $template, array $data = []): string` Render a template with data. **Parameters**: - `$template` - Template path (relative to views directory) - `$data` - Data to pass to template **Returns**: Rendered HTML **Usage**: ```php protected function render(): string { return $this->view('components/product-list', [ 'products' => $this->products, 'totalCount' => $this->totalCount ]); } ``` --- #### State Management ##### `protected function getState(): array` Get component state for serialization. **Returns**: Associative array of state **Usage**: ```php protected function getState(): array { return [ 'userId' => $this->userId, 'filters' => $this->filters, 'page' => $this->page ]; } ``` --- ##### `protected function setState(array $state): void` Restore component state. **Parameters**: - `$state` - State array to restore --- #### Real-Time Updates ##### `protected function enableSse(array $channels = []): void` Enable Server-Sent Events for real-time updates. **Parameters**: - `$channels` - Channels to subscribe to (default: component-specific channel) **Usage**: ```php public function mount(): void { $this->enableSse([ 'user.updates', "team.{$this->teamId}.activity" ]); } ``` --- ##### `protected function broadcast(string $channel, array $data): void` Broadcast update to all clients listening on channel. **Parameters**: - `$channel` - Channel name - `$data` - Data to broadcast **Usage**: ```php #[LiveAction] public function updateStatus(string $status): void { $this->status = $status; // Broadcast to all team members $this->broadcast("team.{$this->teamId}.updates", [ 'type' => 'status_changed', 'user' => $this->user->name, 'status' => $status ]); } ``` --- #### Static Factory ##### `public static function mount(string $componentClass, array $initialData = []): string` Create and render component. **Parameters**: - `$componentClass` - Fully qualified class name - `$initialData` - Initial data for component **Returns**: Rendered component HTML **Usage**: ```php // In controller public function dashboard(): ViewResult { return new ViewResult('dashboard', [ 'statsWidget' => LiveComponent::mount(StatsWidget::class, [ 'user_id' => $currentUser->id ]) ]); } ``` --- ## Attributes ### `#[LiveProp]` Marks a property for automatic state synchronization. **Usage**: ```php use App\Framework\LiveComponents\Attributes\LiveProp; final class SearchComponent extends LiveComponent { #[LiveProp] public string $searchTerm = ''; #[LiveProp] public array $filters = []; #[LiveProp] public int $page = 1; } ``` **Options**: - Properties are automatically serialized/deserialized - Two-way binding with `data-lc-model` - State preserved across requests --- ### `#[LiveAction]` Marks a method as a LiveComponent action. **Usage**: ```php use App\Framework\LiveComponents\Attributes\LiveAction; #[LiveAction] public function search(): void { $this->results = $this->searchService->search($this->searchTerm); } #[LiveAction] public function addToCart(string $productId): void { $this->cart->add($productId); } ``` **Callable From Client**: ```html ``` --- ### `#[Fragment(string|array $names)]` Marks action to only update specific fragments. **Usage**: ```php use App\Framework\LiveComponents\Attributes\Fragment; #[LiveAction] #[Fragment('search-results')] public function search(): void { $this->results = $this->searchService->search($this->searchTerm); // Only search-results fragment re-renders } #[LiveAction] #[Fragment(['user-stats', 'activity-feed'])] public function refreshDashboard(): void { $this->updateStats(); $this->loadActivity(); // Both fragments re-render } ``` --- ### `#[RateLimit(int $requests, int $window)]` Apply rate limiting to component or action. **Usage**: ```php use App\Framework\LiveComponents\Attributes\RateLimit; #[RateLimit(requests: 10, window: 60)] final class SearchComponent extends LiveComponent { #[LiveAction] #[RateLimit(requests: 5, window: 60)] public function expensiveSearch(): void { // Limited to 5 requests per minute } } ``` --- ### `#[Authorize(array $roles = [], array $permissions = [])]` Require authorization for action. **Usage**: ```php use App\Framework\LiveComponents\Attributes\Authorize; #[LiveAction] #[Authorize(roles: ['admin'])] public function deleteUser(string $userId): void { $this->userService->delete($userId); } #[LiveAction] #[Authorize(permissions: ['users.edit'])] public function updateUserRole(string $userId, string $role): void { $this->userService->updateRole($userId, $role); } ``` --- ### `#[Idempotent]` Ensure action executes exactly once (for critical operations). **Usage**: ```php use App\Framework\LiveComponents\Attributes\Idempotent; #[LiveAction] #[Idempotent] public function processPayment(): void { $this->paymentGateway->charge($this->amount); $this->orderService->createOrder($this->cartItems); // Guaranteed to execute only once even if request duplicated } ``` --- ### `#[Encrypted]` Encrypt property value in client state. **Usage**: ```php use App\Framework\LiveComponents\Attributes\Encrypted; final class PaymentForm extends LiveComponent { #[LiveProp] #[Encrypted] public string $creditCardNumber = ''; #[LiveProp] #[Encrypted] public string $cvv = ''; #[LiveProp] // Not encrypted - safe to expose public string $cardholderName = ''; } ``` --- ### `#[Optimistic(string $property, string $operation)]` Enable optimistic UI updates for action. **Usage**: ```php use App\Framework\LiveComponents\Attributes\Optimistic; #[LiveAction] #[Optimistic(property: 'isLiked', operation: 'toggle')] #[Optimistic(property: 'likeCount', operation: 'increment')] public function toggleLike(): void { $this->isLiked = !$this->isLiked; $this->likeCount += $this->isLiked ? 1 : -1; } ``` **Operations**: - `toggle` - Boolean toggle - `increment` - Increment number - `decrement` - Decrement number - `set` - Set to specific value --- ## HTTP Integration ### LiveComponent HTTP Endpoint **POST** `/livecomponent/{componentId}/{action}` Execute a LiveComponent action. **Request Headers**: ```http Content-Type: application/json X-CSRF-Token: {token} X-Component-Checksum: {checksum} ``` **Request Body**: ```json { "state": { "searchTerm": "laptop", "filters": ["electronics"], "page": 1 }, "params": { "productId": "123" } } ``` **Response**: ```json { "success": true, "html": "
...
", "fragments": { "search-results": "
...
" }, "redirect": null, "events": [] } ``` --- ### Batch Endpoint **POST** `/livecomponent/{componentId}/batch` Execute multiple actions in single request. **Request Body**: ```json { "batch": [ { "action": "incrementCounter", "params": {} }, { "action": "updateName", "params": {"name": "John"} }, { "action": "save", "params": {} } ], "state": { "count": 0, "name": "" } } ``` **Response**: ```json { "success": true, "results": [ { "success": true, "html": "..." }, { "success": true, "html": "..." }, { "success": true, "html": "..." } ] } ``` --- ### Upload Endpoint **POST** `/livecomponent/{componentId}/upload` Upload file chunks. **Request Headers**: ```http Content-Type: multipart/form-data X-Upload-ID: {unique-upload-id} X-Chunk-Index: {chunk-number} X-Total-Chunks: {total-chunks} ``` **Response**: ```json { "success": true, "chunk_index": 0, "received_bytes": 1048576, "complete": false } ``` --- ### SSE Endpoint **GET** `/livecomponent/sse/{componentId}` Subscribe to Server-Sent Events. **Response** (text/event-stream): ``` event: component-update data: {"type":"fragment","fragment":"user-stats","html":"
...
"} event: component-update data: {"type":"full","html":"
...
"} event: heartbeat data: {"timestamp":1634567890} ``` --- ## JavaScript API ### `LiveComponent` Global Object Main JavaScript interface for LiveComponents. #### Methods ##### `LiveComponent.mount(element: HTMLElement): void` Initialize component on element. **Usage**: ```javascript const componentEl = document.querySelector('[data-component-id="abc123"]'); LiveComponent.mount(componentEl); ``` --- ##### `LiveComponent.unmount(componentId: string): void` Destroy component and clean up resources. **Usage**: ```javascript LiveComponent.unmount('abc123'); ``` --- ##### `LiveComponent.executeAction(componentId: string, action: string, params: object): Promise` Manually execute component action. **Usage**: ```javascript await LiveComponent.executeAction('abc123', 'search', { query: 'laptops' }); ``` --- ##### `LiveComponent.getComponent(componentId: string): Component | null` Get component instance. **Returns**: Component object or null **Usage**: ```javascript const component = LiveComponent.getComponent('abc123'); console.log(component.state); console.log(component.name); ``` --- ##### `LiveComponent.getState(componentId: string): object | null` Get component state. **Returns**: State object or null **Usage**: ```javascript const state = LiveComponent.getState('abc123'); console.log(state.searchTerm); console.log(state.filters); ``` --- ##### `LiveComponent.setState(componentId: string, state: object): void` Update component state (triggers server sync). **Usage**: ```javascript LiveComponent.setState('abc123', { searchTerm: 'new query', page: 2 }); ``` --- ##### `LiveComponent.refresh(componentId: string): Promise` Force component refresh from server. **Usage**: ```javascript await LiveComponent.refresh('abc123'); ``` --- ##### `LiveComponent.configure(options: object): void` Configure global LiveComponent settings. **Options**: - `batchSize` - Max actions per batch (default: 10) - `batchDebounce` - Debounce delay in ms (default: 50) - `optimisticUI` - Enable optimistic updates (default: true) - `csrfProtection` - Enable CSRF protection (default: true) - `enableSSE` - Enable Server-Sent Events (default: true) **Usage**: ```javascript LiveComponent.configure({ batchSize: 15, batchDebounce: 100, optimisticUI: true }); ``` --- #### Properties ##### `LiveComponent.components: Map` All active components. **Usage**: ```javascript console.log(`Active components: ${LiveComponent.components.size}`); LiveComponent.components.forEach((component, id) => { console.log(`${id}: ${component.name}`); }); ``` --- ### Component Object Individual component instance (returned by `getComponent()`). #### Properties ```typescript interface Component { id: string; // Unique component ID name: string; // Component class name state: object; // Current state element: HTMLElement; // DOM element isConnected: boolean; // SSE connection status isPending: boolean; // Has pending actions } ``` --- ### Data Attributes > **Note**: All data attributes are centrally managed through PHP Enums and JavaScript Constants. See [Data Attributes Reference](../../framework/data-attributes-reference.md) for complete documentation. #### `data-component-id` Identifies component root element. **Required** on component container. **PHP Enum**: `LiveComponentCoreAttribute::COMPONENT_ID` **JavaScript Constant**: `LiveComponentCoreAttributes.COMPONENT_ID` ```html
``` --- #### `data-lc-model` Two-way data binding for inputs. **PHP Enum**: `LiveComponentFeatureAttribute::LC_MODEL` **JavaScript Constant**: `LiveComponentFeatureAttributes.LC_MODEL` ```html ``` **Modifiers**: - `data-lc-model.debounce.500="searchTerm"` - Debounce 500ms - `data-lc-model.lazy="bio"` - Update on blur instead of input --- #### `data-lc-action` Trigger action on click. **PHP Enum**: `LiveComponentCoreAttribute::LIVE_ACTION` **JavaScript Constant**: `LiveComponentCoreAttributes.LIVE_ACTION` ```html ``` --- #### `data-lc-fragment` Mark fragment for partial updates. ```html
{result.title}
``` --- #### `data-lc-submit` Handle form submission. ```html
``` --- #### `data-optimistic` Enable optimistic update for action. ```html ``` --- #### `data-lc-upload` Enable chunked file upload. ```html
{uploadProgress}%
``` --- ## Value Objects ### `ComponentId` Unique component identifier. ```php final readonly class ComponentId { public function __construct(public string $value) {} public static function generate(): self { return new self(bin2hex(random_bytes(16))); } public function toString(): string { return $this->value; } } ``` --- ### `ComponentState` Component state snapshot. ```php final readonly class ComponentState { public function __construct( public array $properties, public string $checksum ) {} public function toArray(): array { return $this->properties; } public function verify(string $checksum): bool { return hash_equals($this->checksum, $checksum); } } ``` --- ### `FragmentName` Fragment identifier. ```php final readonly class FragmentName { public function __construct(public string $value) {} public static function fromString(string $name): self { return new self($name); } } ``` --- ## Services ### `LiveComponentManager` Core service for component lifecycle management. ```php final readonly class LiveComponentManager { public function create( string $componentClass, array $initialData = [] ): LiveComponent; public function hydrate( string $componentClass, ComponentId $id, ComponentState $state ): LiveComponent; public function executeAction( LiveComponent $component, string $actionName, array $params = [] ): ActionResult; public function render(LiveComponent $component): string; public function renderFragment( LiveComponent $component, FragmentName $fragment ): string; } ``` --- ### `LiveComponentState` State management service. ```php final readonly class LiveComponentState { public function serialize(LiveComponent $component): ComponentState; public function deserialize( string $componentClass, array $state ): LiveComponent; public function generateChecksum(array $state): string; public function verifyChecksum(array $state, string $checksum): bool; } ``` --- ## Events ### Client-Side Events #### `livecomponent:mount` Fired when component is mounted. ```javascript window.addEventListener('livecomponent:mount', (e) => { console.log('Component mounted:', e.detail); // { componentId, name, element } }); ``` --- #### `livecomponent:action-start` Fired when action starts. ```javascript window.addEventListener('livecomponent:action-start', (e) => { console.log('Action starting:', e.detail); // { componentId, action, params, optimistic } }); ``` --- #### `livecomponent:action-executed` Fired when action completes. ```javascript window.addEventListener('livecomponent:action-executed', (e) => { console.log('Action completed:', e.detail); // { componentId, action, duration, success } }); ``` --- #### `livecomponent:action-error` Fired when action fails. ```javascript window.addEventListener('livecomponent:action-error', (e) => { console.error('Action failed:', e.detail); // { componentId, action, error, response } }); ``` --- #### `livecomponent:fragment-updated` Fired when fragment updates. ```javascript window.addEventListener('livecomponent:fragment-updated', (e) => { console.log('Fragment updated:', e.detail); // { componentId, fragmentName, duration, nodesChanged } }); ``` --- #### `livecomponent:batch-sent` Fired when batch request is sent. ```javascript window.addEventListener('livecomponent:batch-sent', (e) => { console.log('Batch sent:', e.detail); // { componentId, actionsCount, payloadSize } }); ``` --- #### `livecomponent:optimistic-applied` Fired when optimistic update is applied. ```javascript window.addEventListener('livecomponent:optimistic-applied', (e) => { console.log('Optimistic update:', e.detail); // { componentId, action, stateBeforeOptimistic } }); ``` --- #### `livecomponent:optimistic-rollback` Fired when optimistic update is rolled back. ```javascript window.addEventListener('livecomponent:optimistic-rollback', (e) => { console.warn('Optimistic rollback:', e.detail); // { componentId, action, error, stateBeforeOptimistic } }); ``` --- #### `livecomponent:sse-connected` Fired when SSE connection established. ```javascript window.addEventListener('livecomponent:sse-connected', (e) => { console.log('SSE connected:', e.detail); // { componentId, channels } }); ``` --- #### `livecomponent:sse-disconnected` Fired when SSE connection lost. ```javascript window.addEventListener('livecomponent:sse-disconnected', (e) => { console.warn('SSE disconnected:', e.detail); // { componentId, reason } }); ``` --- #### `livecomponent:error` Fired on any LiveComponent error. ```javascript window.addEventListener('livecomponent:error', (e) => { console.error('LiveComponent error:', e.detail); // { componentId, type, message, code } if (e.detail.code === 'CSRF_TOKEN_EXPIRED') { LiveComponent.refreshCsrfToken(); } }); ``` --- ### Server-Side Events Events dispatched during server-side processing (integrate with framework Event System). #### `ComponentMountedEvent` ```php final readonly class ComponentMountedEvent { public function __construct( public string $componentClass, public ComponentId $componentId, public array $initialData ) {} } ``` --- #### `ComponentActionExecutedEvent` ```php final readonly class ComponentActionExecutedEvent { public function __construct( public string $componentClass, public ComponentId $componentId, public string $actionName, public array $params, public float $duration, public bool $success ) {} } ``` --- #### `ComponentUnmountedEvent` ```php final readonly class ComponentUnmountedEvent { public function __construct( public string $componentClass, public ComponentId $componentId ) {} } ``` --- ## Error Handling ### Common Errors #### `ComponentNotFoundException` Component class not found. ```php throw ComponentNotFoundException::forClass('App\\Components\\NonExistent'); ``` --- #### `ActionNotFoundException` Action method not found on component. ```php throw ActionNotFoundException::forAction('nonExistentAction', $componentClass); ``` --- #### `InvalidStateException` Component state invalid or corrupted. ```php throw InvalidStateException::checksumMismatch($expectedChecksum, $actualChecksum); ``` --- #### `RateLimitExceededException` Rate limit exceeded for component/action. ```php throw RateLimitExceededException::forAction($actionName, $retryAfter); ``` --- #### `UnauthorizedException` User not authorized for action. ```php throw UnauthorizedException::missingRole($requiredRole); throw UnauthorizedException::missingPermission($requiredPermission); ``` --- ## Type Definitions ### TypeScript Definitions For TypeScript projects: ```typescript // livecomponents.d.ts declare global { interface Window { LiveComponent: { mount(element: HTMLElement): void; unmount(componentId: string): void; executeAction( componentId: string, action: string, params?: object ): Promise; getComponent(componentId: string): Component | null; getState(componentId: string): object | null; setState(componentId: string, state: object): void; refresh(componentId: string): Promise; configure(options: LiveComponentConfig): void; components: Map; }; } interface Component { id: string; name: string; state: object; element: HTMLElement; isConnected: boolean; isPending: boolean; } interface LiveComponentConfig { batchSize?: number; batchDebounce?: number; optimisticUI?: boolean; csrfProtection?: boolean; enableSSE?: boolean; } } export {}; ``` --- **Next**: [Advanced Features](advanced-features.md) →