- 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.
521 lines
12 KiB
Markdown
521 lines
12 KiB
Markdown
# Getting Started with LiveComponents
|
|
|
|
This guide will help you set up and create your first LiveComponent.
|
|
|
|
## Prerequisites
|
|
|
|
- PHP 8.4+ (8.5 recommended)
|
|
- Custom PHP Framework installed
|
|
- Node.js 18+ for frontend assets
|
|
- Docker (optional, for development environment)
|
|
|
|
## Installation
|
|
|
|
LiveComponents is built into the Custom PHP Framework. No additional installation is required.
|
|
|
|
### Verify Installation
|
|
|
|
```bash
|
|
# Check if LiveComponents are available
|
|
docker exec php php console.php list | grep livecomponent
|
|
|
|
# Should show LiveComponent-related commands
|
|
```
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
src/
|
|
├── Application/
|
|
│ └── LiveComponents/ # Your LiveComponent classes
|
|
├── Framework/
|
|
│ └── LiveComponents/ # Framework LiveComponents code
|
|
│ ├── LiveComponent.php # Base class
|
|
│ ├── Attributes/ # Attributes (#[LiveProp], etc.)
|
|
│ └── Services/ # Internal services
|
|
|
|
resources/
|
|
├── js/
|
|
│ └── modules/
|
|
│ └── LiveComponent.js # Client-side JavaScript
|
|
└── views/
|
|
└── components/ # Component templates
|
|
|
|
public/
|
|
└── assets/
|
|
└── js/
|
|
└── livecomponent.js # Compiled JavaScript
|
|
```
|
|
|
|
## Configuration
|
|
|
|
### Environment Variables
|
|
|
|
Add to your `.env` file:
|
|
|
|
```env
|
|
# LiveComponents Configuration
|
|
LIVECOMPONENT_ENABLED=true
|
|
LIVECOMPONENT_CSRF_PROTECTION=true
|
|
LIVECOMPONENT_RATE_LIMIT=60 # Requests per minute
|
|
LIVECOMPONENT_BATCH_SIZE=10 # Max actions per batch
|
|
LIVECOMPONENT_BATCH_DEBOUNCE=50 # Debounce delay in ms
|
|
LIVECOMPONENT_SSE_ENABLED=true
|
|
LIVECOMPONENT_SSE_HEARTBEAT=15 # Heartbeat interval in seconds
|
|
LIVECOMPONENT_DEVTOOLS_ENABLED=false # Enable in development only
|
|
```
|
|
|
|
### Frontend Assets
|
|
|
|
Ensure LiveComponent JavaScript is included in your base layout:
|
|
|
|
```html
|
|
<!-- resources/views/layouts/app.view.php -->
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="csrf-token" content="{csrf_token}">
|
|
<title>{title}</title>
|
|
</head>
|
|
<body>
|
|
<slot>Main content</slot>
|
|
|
|
<!-- LiveComponent JavaScript (built by Vite) -->
|
|
<script type="module" src="/assets/js/main.js"></script>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
The LiveComponent module is automatically loaded via the main JavaScript entry point.
|
|
|
|
## Creating Your First Component
|
|
|
|
### Step 1: Create Component Class
|
|
|
|
Create a new file: `src/Application/LiveComponents/HelloWorld.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Application\LiveComponents;
|
|
|
|
use App\Framework\LiveComponents\LiveComponent;
|
|
use App\Framework\LiveComponents\Attributes\LiveAction;
|
|
use App\Framework\LiveComponents\Attributes\LiveProp;
|
|
|
|
final class HelloWorld extends LiveComponent
|
|
{
|
|
#[LiveProp]
|
|
public string $name = 'World';
|
|
|
|
#[LiveProp]
|
|
public int $clickCount = 0;
|
|
|
|
#[LiveAction]
|
|
public function updateName(string $newName): void
|
|
{
|
|
$this->name = $newName;
|
|
}
|
|
|
|
#[LiveAction]
|
|
public function incrementClicks(): void
|
|
{
|
|
$this->clickCount++;
|
|
}
|
|
|
|
public function render(): string
|
|
{
|
|
return $this->view('components/hello-world', [
|
|
'name' => $this->name,
|
|
'clickCount' => $this->clickCount,
|
|
'greeting' => $this->getGreeting()
|
|
]);
|
|
}
|
|
|
|
private function getGreeting(): string
|
|
{
|
|
return "Hello, {$this->name}!";
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 2: Create Component Template
|
|
|
|
Create template: `resources/views/components/hello-world.view.php`
|
|
|
|
```html
|
|
<div
|
|
data-component-id="{component_id}"
|
|
data-component-name="HelloWorld"
|
|
class="hello-world-component"
|
|
>
|
|
<h2>{greeting}</h2>
|
|
|
|
<div class="input-group">
|
|
<label for="name-input">Your Name:</label>
|
|
<input
|
|
type="text"
|
|
id="name-input"
|
|
data-lc-model="name"
|
|
value="{name}"
|
|
placeholder="Enter your name"
|
|
/>
|
|
</div>
|
|
|
|
<div class="click-counter">
|
|
<p>Button clicked: <strong>{clickCount}</strong> times</p>
|
|
<button data-lc-action="incrementClicks" class="btn btn-primary">
|
|
Click Me!
|
|
</button>
|
|
</div>
|
|
|
|
<style>
|
|
.hello-world-component {
|
|
padding: 20px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 8px;
|
|
max-width: 400px;
|
|
}
|
|
|
|
.input-group {
|
|
margin: 15px 0;
|
|
}
|
|
|
|
.input-group label {
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.input-group input {
|
|
width: 100%;
|
|
padding: 8px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.click-counter {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.btn {
|
|
padding: 10px 20px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.btn-primary {
|
|
background: #007bff;
|
|
color: white;
|
|
}
|
|
|
|
.btn-primary:hover {
|
|
background: #0056b3;
|
|
}
|
|
</style>
|
|
</div>
|
|
```
|
|
|
|
### Step 3: Use Component in Controller
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Application;
|
|
|
|
use App\Framework\Attributes\Route;
|
|
use App\Framework\Http\Method;
|
|
use App\Framework\Http\ViewResult;
|
|
use App\Framework\LiveComponents\LiveComponent;
|
|
use App\Application\LiveComponents\HelloWorld;
|
|
|
|
final readonly class HomeController
|
|
{
|
|
#[Route(path: '/demo', method: Method::GET)]
|
|
public function demo(): ViewResult
|
|
{
|
|
return new ViewResult('demo', [
|
|
'title' => 'LiveComponent Demo',
|
|
'helloComponent' => LiveComponent::mount(HelloWorld::class)
|
|
]);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Step 4: Create View Template
|
|
|
|
Create: `resources/views/demo.view.php`
|
|
|
|
```html
|
|
<include template="layouts/app" data="{title: title}">
|
|
<div class="container">
|
|
<h1>LiveComponent Demo</h1>
|
|
<p>This component is fully interactive without writing any JavaScript!</p>
|
|
|
|
{helloComponent}
|
|
</div>
|
|
</include>
|
|
```
|
|
|
|
### Step 5: Test Your Component
|
|
|
|
1. **Start the development server**:
|
|
```bash
|
|
make up
|
|
npm run dev
|
|
```
|
|
|
|
2. **Visit the demo page**:
|
|
```
|
|
https://localhost/demo
|
|
```
|
|
|
|
3. **Try the interactions**:
|
|
- Type in the name input → Greeting updates automatically
|
|
- Click the button → Counter increments
|
|
- Open DevTools (F12) → Check Network tab for LiveComponent requests
|
|
|
|
## How It Works
|
|
|
|
### 1. Component Mounting
|
|
|
|
When the page loads:
|
|
```php
|
|
LiveComponent::mount(HelloWorld::class)
|
|
```
|
|
|
|
This creates a component instance and renders the initial HTML with a unique `component_id`.
|
|
|
|
### 2. Client-Side Initialization
|
|
|
|
The JavaScript module detects components by `data-component-id`:
|
|
|
|
```javascript
|
|
// Automatic initialization
|
|
document.querySelectorAll('[data-component-id]').forEach(element => {
|
|
LiveComponent.initializeComponent(element);
|
|
});
|
|
```
|
|
|
|
### 3. Two-Way Data Binding
|
|
|
|
Input with `data-lc-model="name"`:
|
|
- User types → `input` event → Send to server → Update `$name` property → Re-render → Update DOM
|
|
|
|
### 4. Action Execution
|
|
|
|
Button with `data-lc-action="incrementClicks"`:
|
|
- User clicks → Send action to server → Execute `incrementClicks()` → Re-render → Update DOM
|
|
|
|
### 5. Request Flow
|
|
|
|
```
|
|
Client Server
|
|
│ │
|
|
├─ Click Button ──────────────>│
|
|
│ ├─ Find Component
|
|
│ ├─ Execute Action
|
|
│ ├─ Render Template
|
|
│<─ JSON Response ─────────────┤
|
|
├─ Update DOM │
|
|
│ │
|
|
```
|
|
|
|
**Response Format**:
|
|
```json
|
|
{
|
|
"success": true,
|
|
"html": "<div data-component-id=\"abc123\">...</div>",
|
|
"fragments": {
|
|
"results": "<div>Updated fragment HTML</div>"
|
|
},
|
|
"redirect": null
|
|
}
|
|
```
|
|
|
|
## Common Data Binding Patterns
|
|
|
|
### Input Fields
|
|
|
|
```html
|
|
<!-- Text input -->
|
|
<input type="text" data-lc-model="title" value="{title}" />
|
|
|
|
<!-- Textarea -->
|
|
<textarea data-lc-model="description">{description}</textarea>
|
|
|
|
<!-- Checkbox -->
|
|
<input type="checkbox" data-lc-model="agreed" {agreed ? 'checked' : ''} />
|
|
|
|
<!-- Select -->
|
|
<select data-lc-model="category">
|
|
<option value="tech" {category === 'tech' ? 'selected' : ''}>Tech</option>
|
|
<option value="news" {category === 'news' ? 'selected' : ''}>News</option>
|
|
</select>
|
|
```
|
|
|
|
### Actions with Parameters
|
|
|
|
```html
|
|
<!-- Simple action -->
|
|
<button data-lc-action="save">Save</button>
|
|
|
|
<!-- Action with inline parameters -->
|
|
<button
|
|
data-lc-action="updateStatus"
|
|
data-lc-params='{"status": "published"}'
|
|
>
|
|
Publish
|
|
</button>
|
|
|
|
<!-- Action from event handler -->
|
|
<form data-lc-submit="handleSubmit">
|
|
<input type="text" name="email" />
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
```
|
|
|
|
### Optimistic Updates
|
|
|
|
```html
|
|
<!-- Instant UI feedback -->
|
|
<button
|
|
data-lc-action="toggleLike"
|
|
data-optimistic="true"
|
|
>
|
|
{isLiked ? '❤️' : '🤍'} Like ({likeCount})
|
|
</button>
|
|
```
|
|
|
|
## Debugging
|
|
|
|
### Enable DevTools Overlay
|
|
|
|
Set in `.env`:
|
|
```env
|
|
LIVECOMPONENT_DEVTOOLS_ENABLED=true
|
|
```
|
|
|
|
Or activate via localStorage:
|
|
```javascript
|
|
localStorage.setItem('livecomponent_devtools', 'true');
|
|
location.reload();
|
|
```
|
|
|
|
### DevTools Features
|
|
|
|
- **Component Tree**: Inspect all components on the page
|
|
- **Action Log**: See all executed actions with timing
|
|
- **Event Log**: Track client and server events
|
|
- **Performance**: Flamegraphs, timeline, memory profiling
|
|
- **Network**: Monitor LiveComponent HTTP requests
|
|
- **DOM Badges**: Visual component boundaries with activity
|
|
|
|
### Console Debugging
|
|
|
|
```javascript
|
|
// Get component instance
|
|
const component = LiveComponent.getComponent('component-id-here');
|
|
|
|
// Inspect component state
|
|
console.log(component.state);
|
|
|
|
// Manually trigger action
|
|
LiveComponent.executeAction('component-id', 'actionName', { param: 'value' });
|
|
|
|
// Monitor events
|
|
window.addEventListener('livecomponent:action-executed', (e) => {
|
|
console.log('Action executed:', e.detail);
|
|
});
|
|
```
|
|
|
|
## Next Steps
|
|
|
|
- [Your First Component](02-first-component.md) - Detailed walkthrough
|
|
- [Core Concepts](03-core-concepts.md) - Deep dive into architecture
|
|
- [Security Guide](security-guide.md) - CSRF, validation, rate limiting
|
|
- [Performance Guide](performance-guide.md) - Optimization techniques
|
|
|
|
## Troubleshooting
|
|
|
|
### Component Not Responding
|
|
|
|
**Check**:
|
|
1. Is `data-component-id` present in the rendered HTML?
|
|
2. Is the JavaScript bundle loaded? (Check browser console)
|
|
3. Are there any JavaScript errors? (F12 → Console)
|
|
4. Is HTTPS enabled? (Required for some features)
|
|
|
|
**Solution**:
|
|
```bash
|
|
# Rebuild JavaScript assets
|
|
npm run build
|
|
|
|
# Check server logs
|
|
docker logs php
|
|
|
|
# Verify component registration
|
|
docker exec php php console.php livecomponent:list
|
|
```
|
|
|
|
### CSRF Token Errors
|
|
|
|
**Error**: "CSRF token mismatch"
|
|
|
|
**Solution**:
|
|
```html
|
|
<!-- Ensure CSRF meta tag is present -->
|
|
<meta name="csrf-token" content="{csrf_token}">
|
|
```
|
|
|
|
### Rate Limiting
|
|
|
|
**Error**: "Too many requests"
|
|
|
|
**Solution**: Increase rate limit in `.env`:
|
|
```env
|
|
LIVECOMPONENT_RATE_LIMIT=120 # Increase to 120 per minute
|
|
```
|
|
|
|
## Common Pitfalls
|
|
|
|
❌ **Forgetting `data-component-id`**
|
|
```html
|
|
<!-- Wrong -->
|
|
<div class="component">...</div>
|
|
|
|
<!-- Correct -->
|
|
<div data-component-id="{component_id}">...</div>
|
|
```
|
|
|
|
❌ **Not using `#[LiveProp]` attribute**
|
|
```php
|
|
// Wrong - won't sync with client
|
|
public string $name = '';
|
|
|
|
// Correct
|
|
#[LiveProp]
|
|
public string $name = '';
|
|
```
|
|
|
|
❌ **Mutating props directly in template**
|
|
```html
|
|
<!-- Wrong - props are readonly on client -->
|
|
<button onclick="name = 'new value'">Change</button>
|
|
|
|
<!-- Correct - use actions -->
|
|
<button data-lc-action="updateName" data-lc-params='{"name": "new value"}'>
|
|
Change
|
|
</button>
|
|
```
|
|
|
|
---
|
|
|
|
**Next**: [Your First Component Tutorial](02-first-component.md) →
|