refactor: susun semula struktur folder — Laravel source ke src/

This commit is contained in:
Saufi
2026-05-19 15:58:35 +08:00
parent f052251b94
commit bf53c71b45
10806 changed files with 1385379 additions and 121 deletions

View File

@@ -0,0 +1,95 @@
---
name: livewire-development
description: "Use for any task or question involving Livewire. Activate if user mentions Livewire, wire: directives, or Livewire-specific concepts like wire:model, wire:click, invoke this skill. Covers building new components, debugging reactivity issues, real-time form validation, loading states, migrating from Livewire 1 to 2, and performance optimization. Do not use for non-Livewire reactive UI (React, Vue, Alpine-only, Inertia.js) or standard Laravel forms without Livewire."
license: MIT
metadata:
author: laravel
---
@php
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
@endphp
# Livewire Development
## Documentation
Use `search-docs` for detailed Livewire 2 patterns and documentation.
## Basic Usage
### Creating Components
Use the `{{ $assist->artisanCommand('make:livewire [Posts\\CreatePost]') }}` Artisan command to create new components.
### Fundamental Concepts
- State should live on the server, with the UI reflecting it.
- All Livewire requests hit the Laravel backend; they're like regular HTTP requests. Always validate form data and run authorization checks in Livewire actions.
## Livewire 2 Specifics
- `wire:model` is live by default (real-time updates without modifier).
- Components typically exist in the `App\Http\Livewire` namespace.
- Use `emit()`, `emitTo()`, `emitSelf()`, and `dispatchBrowserEvent()` for events.
- Alpine is included separately from Livewire.
## Best Practices
### Component Structure
- Livewire components require a single root element.
- Use `wire:loading` and `wire:dirty` for delightful loading states.
### Using Keys in Loops
@boostsnippet("Wire Key in Loops", "blade")
@foreach ($items as $item)
<div wire:key="item-{{ $item->id }}">
{{ $item->name }}
</div>
@endforeach
@endboostsnippet
### Lifecycle Hooks
Prefer lifecycle hooks like `mount()`, `updatedFoo()` for initialization and reactive side effects:
@boostsnippet("Lifecycle Hook Examples", "php")
public function mount(User $user) { $this->user = $user; }
public function updatedSearch() { $this->resetPage(); }
@endboostsnippet
## JavaScript Hooks
You can listen for `livewire:load` to hook into Livewire initialization:
@boostsnippet("Livewire Load Hook Example", "js")
document.addEventListener('livewire:load', function () {
Livewire.onPageExpired(() => {
alert('Your session expired');
});
Livewire.onError(status => console.error(status));
});
@endboostsnippet
## Testing
@boostsnippet("Example Livewire Component Test", "php")
Livewire::test(Counter::class)
->assertSet('count', 0)
->call('increment')
->assertSet('count', 1)
->assertSee(1)
->assertStatus(200);
@endboostsnippet
@boostsnippet("Testing Livewire Component Exists on Page", "php")
$this->get('/posts/create')
->assertSeeLivewire(CreatePost::class);
@endboostsnippet
## Common Pitfalls
- Forgetting `wire:key` in loops causes unexpected behavior when items change
- Not validating/authorizing in Livewire actions (treat them like HTTP requests)
- Forgetting that `wire:model` is live by default in v2 (may cause performance issues)

View File

@@ -0,0 +1,111 @@
---
name: livewire-development
description: "Use for any task or question involving Livewire. Activate if user mentions Livewire, wire: directives, or Livewire-specific concepts like wire:model, wire:click, invoke this skill. Covers building new components, debugging reactivity issues, real-time form validation, loading states, migrating from Livewire 2 to 3, converting component formats (SFC/MFC/class-based), and performance optimization. Do not use for non-Livewire reactive UI (React, Vue, Alpine-only, Inertia.js) or standard Laravel forms without Livewire."
license: MIT
metadata:
author: laravel
---
@php
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
@endphp
# Livewire Development
## Documentation
Use `search-docs` for detailed Livewire 3 patterns and documentation.
## Basic Usage
### Creating Components
Use the `{{ $assist->artisanCommand('make:livewire [Posts\\CreatePost]') }}` Artisan command to create new components.
### Fundamental Concepts
- State should live on the server, with the UI reflecting it.
- All Livewire requests hit the Laravel backend; they're like regular HTTP requests. Always validate form data and run authorization checks in Livewire actions.
## Livewire 3 Specifics
### Key Changes From Livewire 2
These things changed in Livewire 3, but may not have been updated in this application. Verify this application's setup to ensure you follow existing conventions.
- Use `wire:model.live` for real-time updates, `wire:model` is now deferred by default.
- Components now use the `App\Livewire` namespace (not `App\Http\Livewire`).
- Use `$this->dispatch()` to dispatch events (not `emit` or `dispatchBrowserEvent`).
- Use the `components.layouts.app` view as the typical layout path (not `layouts.app`).
### New Directives
- `wire:show`, `wire:transition`, `wire:cloak`, `wire:offline`, `wire:target` are available for use.
### Alpine Integration
- Alpine is now included with Livewire; don't manually include Alpine.js.
- Plugins included with Alpine: persist, intersect, collapse, and focus.
## Best Practices
### Component Structure
- Livewire components require a single root element.
- Use `wire:loading` and `wire:dirty` for delightful loading states.
### Using Keys in Loops
@boostsnippet("Wire Key in Loops", "blade")
@foreach ($items as $item)
<div wire:key="item-{{ $item->id }}">
{{ $item->name }}
</div>
@endforeach
@endboostsnippet
### Lifecycle Hooks
Prefer lifecycle hooks like `mount()`, `updatedFoo()` for initialization and reactive side effects:
@boostsnippet("Lifecycle Hook Examples", "php")
public function mount(User $user) { $this->user = $user; }
public function updatedSearch() { $this->resetPage(); }
@endboostsnippet
## JavaScript Hooks
You can listen for `livewire:init` to hook into Livewire initialization:
@boostsnippet("Livewire Init Hook Example", "js")
document.addEventListener('livewire:init', function () {
Livewire.hook('request', ({ fail }) => {
if (fail && fail.status === 419) {
alert('Your session expired');
}
});
Livewire.hook('message.failed', (message, component) => {
console.error(message);
});
});
@endboostsnippet
## Testing
@boostsnippet("Example Livewire Component Test", "php")
Livewire::test(Counter::class)
->assertSet('count', 0)
->call('increment')
->assertSet('count', 1)
->assertSee(1)
->assertStatus(200);
@endboostsnippet
@boostsnippet("Testing Livewire Component Exists on Page", "php")
$this->get('/posts/create')
->assertSeeLivewire(CreatePost::class);
@endboostsnippet
## Common Pitfalls
- Forgetting `wire:key` in loops causes unexpected behavior when items change
- Using `wire:model` expecting real-time updates (use `wire:model.live` instead in v3)
- Not validating/authorizing in Livewire actions (treat them like HTTP requests)
- Including Alpine.js separately when it's already bundled with Livewire 3

View File

@@ -0,0 +1,164 @@
---
name: livewire-development
description: "Use for any task or question involving Livewire. Activate if user mentions Livewire, wire: directives, or Livewire-specific concepts like wire:model, wire:click, wire:sort, or islands, invoke this skill. Covers building new components, debugging reactivity issues, real-time form validation, drag-and-drop, loading states, migrating from Livewire 3 to 4, converting component formats (SFC/MFC/class-based), and performance optimization. Do not use for non-Livewire reactive UI (React, Vue, Alpine-only, Inertia.js) or standard Laravel forms without Livewire."
license: MIT
metadata:
author: laravel
---
@php
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
@endphp
# Livewire Development
## Documentation
Use `search-docs` for detailed Livewire 4 patterns and documentation.
## Basic Usage
### Creating Components
```bash
# Single-file component (SFC - default in v4)
# Creates: resources/views/components/⚡create-post.blade.php
{{ $assist->artisanCommand('make:livewire create-post') }}
# Page component (SFC - Full Page in v4)
# Creates: resources/views/pages/⚡create-post.blade.php
{{ $assist->artisanCommand('make:livewire pages::create-post') }}
# Multi-file component (MFC)
# Creates: resources/views/components/⚡create-post/create-post.php
# resources/views/components/⚡create-post/create-post.blade.php
{{ $assist->artisanCommand('make:livewire create-post --mfc') }}
# Class-based component (v3 style)
# Creates: app/Livewire/CreatePost.php AND resources/views/livewire/create-post.blade.php
{{ $assist->artisanCommand('make:livewire create-post --class') }}
# With namespace
{{ $assist->artisanCommand('make:livewire Posts/CreatePost') }}
```
### Converting Between Formats
Use `{{ $assist->artisanCommand('livewire:convert create-post') }}` to convert between single-file, multi-file, and class-based formats.
### Choosing a Component Format
> **Always follow the project's existing conventions first.** Before creating any component, inspect the project's existing Livewire components to determine the established format (SFC, MFC, or class-based) and directory structure. Check `{{ $assist->appPath('Livewire/') }}`, `resources/views/components/`, and `resources/views/livewire/` for existing components. If the project already uses a consistent format, **use that same format** even if it differs from the Livewire v4 defaults below. Only fall back to the v4 defaults (SFC in `resources/views/components/`) when no existing convention is established.
Also check `config/livewire.php` for `make_command.type`, `make_command.emoji`, `component_locations`, and `component_namespaces` overrides, which change the default format and where files are stored.
### Component Format Reference
| Format | Flag | Class Path | View Path |
|--------|------|------------|-----------|
| Single-file (SFC) | default | | `resources/views/components/⚡create-post.blade.php` (PHP + Blade in one file) |
| Full Page SFC | `pages::name` | | `resources/views/pages/⚡create-post.blade.php` |
| Multi-file (MFC) | `--mfc` | `resources/views/components/⚡create-post/create-post.php` | `resources/views/components/⚡create-post/create-post.blade.php` |
| Class-based | `--class` | `{{ $assist->appPath('Livewire/CreatePost.php') }}` | `resources/views/livewire/create-post.blade.php` |
| View-based | default (Blade-only) | | `resources/views/components/⚡create-post.blade.php` (Blade-only with functional state) |
> **Important:** The prefix shown above is the **default** behavior in Livewire v4 it is **configurable**. Check `config/livewire.php` for the `make_command.emoji` setting. When `true` (default), always include the prefix in filenames you create. When `false`, omit the prefix from all paths above.
Namespaced components map to subdirectories: `make:livewire Posts/CreatePost` creates `resources/views/components/posts/⚡create-post.blade.php` (single-file by default). Use `make:livewire Posts/CreatePost --mfc` for multi-file output at `resources/views/components/posts/⚡create-post/create-post.php` and `resources/views/components/posts/⚡create-post/create-post.blade.php`.
### Single-File Component Example
@boostsnippet("Single-File Component Example", "php")
<?php
use Livewire\Component;
new class extends Component {
public int $count = 0;
public function increment(): void
{
$this->count++;
}
};
?>
<div>
<button wire:click="increment">Count: @{{ $count }}</button>
</div>
@endboostsnippet
## Livewire 4 Specifics
### Key Changes From Livewire 3
These things changed in Livewire 4, but may not have been updated in this application. Verify this application's setup to ensure you follow existing conventions.
- Use `Route::livewire()` for full-page components (e.g., `Route::livewire('/posts/create', CreatePost::class)`); config keys renamed: `layout` → `component_layout`, `lazy_placeholder` → `component_placeholder`.
- `wire:model` now ignores child events by default (use `wire:model.deep` for old behavior); `wire:scroll` renamed to `wire:navigate:scroll`.
- Component tags must be properly closed; `wire:transition` now uses View Transitions API (modifiers removed).
- JavaScript: `$wire.$js('name', fn)` → `$wire.$js.name = fn`; `commit`/`request` hooks → `interceptMessage()`/`interceptRequest()`.
### New Features
- Component formats: single-file (SFC), multi-file (MFC), view-based components.
- Islands (`@island`) for isolated updates; async actions (`wire:click.async`, `#[Async]`) for parallel execution.
- Deferred/bundled loading: `defer`, `lazy.bundle` for optimized component loading.
| Feature | Usage | Purpose |
|---------|-------|---------|
| Islands | `@island(name: 'stats')` | Isolated update regions |
| Async | `wire:click.async` or `#[Async]` | Non-blocking actions |
| Deferred | `defer` attribute | Load after page render |
| Bundled | `lazy.bundle` | Load multiple together |
### New Directives
- `wire:sort`, `wire:intersect`, `wire:ref`, `.renderless`, `.preserve-scroll` are available for use.
- `data-loading` attribute automatically added to elements triggering network requests.
| Directive | Purpose |
|-----------|---------|
| `wire:sort` | Drag-and-drop sorting |
| `wire:intersect` | Viewport intersection detection |
| `wire:ref` | Element references for JS |
| `.renderless` | Component without rendering |
| `.preserve-scroll` | Preserve scroll position |
## Best Practices
- Always use `wire:key` in loops
- Use `wire:loading` for loading states
- Use `wire:model.live` for instant updates (default is debounced)
- Validate and authorize in actions (treat like HTTP requests)
## Configuration
- `smart_wire_keys` defaults to `true`; new configs: `component_locations`, `component_namespaces`, `make_command`, `csp_safe`.
## Alpine & JavaScript
- `wire:transition` uses browser View Transitions API; `$errors` and `$intercept` magic properties available.
- Non-blocking `wire:poll` and parallel `wire:model.live` updates improve performance.
For interceptors and hooks, see [reference/javascript-hooks.md](reference/javascript-hooks.md).
## Testing
@boostsnippet("Testing Example", "php")
Livewire::test(Counter::class)
->assertSet('count', 0)
->call('increment')
->assertSet('count', 1);
@endboostsnippet
## Verification
1. Browser console: Check for JS errors
2. Network tab: Verify Livewire requests return 200
3. Ensure `wire:key` on all `@foreach` loops
## Common Pitfalls
- Missing `wire:key` in loops → unexpected re-rendering
- Expecting `wire:model` real-time → use `wire:model.live`
- Unclosed component tags → syntax errors in v4
- Using deprecated config keys or JS hooks
- Including Alpine.js separately (already bundled in Livewire 4)

View File

@@ -0,0 +1,39 @@
# Livewire 4 JavaScript Integration
## Interceptor System (v4)
### Intercept Messages
```js
Livewire.interceptMessage(({ component, message, onFinish, onSuccess, onError }) => {
onFinish(() => { /* After response, before processing */ });
onSuccess(({ payload }) => { /* payload.snapshot, payload.effects */ });
onError(() => { /* Server errors */ });
});
```
### Intercept Requests
```js
Livewire.interceptRequest(({ request, onResponse, onSuccess, onError, onFailure }) => {
onResponse(({ response }) => { /* When received */ });
onSuccess(({ response, responseJson }) => { /* Success */ });
onError(({ response, responseBody, preventDefault }) => { /* 4xx/5xx */ });
onFailure(({ error }) => { /* Network failures */ });
});
```
### Component-Scoped Interceptors
```blade
<script>
this.$intercept('save', ({ component, onSuccess }) => {
onSuccess(() => console.log('Saved!'));
});
</script>
```
## Magic Properties
- `$errors` - Access validation errors from JavaScript
- `$intercept` - Component-scoped interceptors

View File

@@ -0,0 +1,5 @@
# Livewire
- Livewire allow to build dynamic, reactive interfaces in PHP without writing JavaScript.
- You can use Alpine.js for client-side interactions instead of JavaScript frameworks.
- Keep state server-side so the UI reflects it. Validate and authorize in actions as you would in HTTP requests.