refactor: susun semula struktur folder — Laravel source ke src/
This commit is contained in:
34
vendor/laravel/boost/.ai/boost/core.blade.php
vendored
Normal file
34
vendor/laravel/boost/.ai/boost/core.blade.php
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
# Laravel Boost
|
||||
@if($assist->hasMcpEnabled())
|
||||
|
||||
## Tools
|
||||
- Laravel Boost is an MCP server with tools designed specifically for this application. Prefer Boost tools over manual alternatives like shell commands or file reads.
|
||||
- Use `database-query` to run read-only queries against the database instead of writing raw SQL in tinker.
|
||||
- Use `database-schema` to inspect table structure before writing migrations or models.
|
||||
- Use `get-absolute-url` to resolve the correct scheme, domain, and port for project URLs. Always use this before sharing a URL with the user.
|
||||
@if (config('boost.browser_logs', false) !== false || config('boost.browser_logs_watcher', true) !== false)
|
||||
- Use `browser-logs` to read browser logs, errors, and exceptions. Only recent logs are useful, ignore old entries.
|
||||
@endif
|
||||
|
||||
## Searching Documentation (IMPORTANT)
|
||||
- Always use `search-docs` before making code changes. Do not skip this step. It returns version-specific docs based on installed packages automatically.
|
||||
- Pass a `packages` array to scope results when you know which packages are relevant.
|
||||
- Use multiple broad, topic-based queries: `['rate limiting', 'routing rate limiting', 'routing']`. Expect the most relevant results first.
|
||||
- Do not add package names to queries because package info is already shared. Use `test resource table`, not `filament 4 test resource table`.
|
||||
### Search Syntax
|
||||
1. Use words for auto-stemmed AND logic: `rate limit` matches both "rate" AND "limit".
|
||||
2. Use `"quoted phrases"` for exact position matching: `"infinite scroll"` requires adjacent words in order.
|
||||
3. Combine words and phrases for mixed queries: `middleware "rate limit"`.
|
||||
4. Use multiple queries for OR logic: `queries=["authentication", "middleware"]`.
|
||||
@endif
|
||||
|
||||
## Artisan
|
||||
- Run Artisan commands directly via the command line (e.g., `{{ $assist->artisanCommand('route:list') }}`). Use `{{ $assist->artisanCommand('list') }}` to discover available commands and `{{ $assist->artisanCommand('[command] --help') }}` to check parameters.
|
||||
- Inspect routes with `{{ $assist->artisanCommand('route:list') }}`. Filter with: `--method=GET`, `--name=users`, `--path=api`, `--except-vendor`, `--only-vendor`.
|
||||
- Read configuration values using dot notation: `{{ $assist->artisanCommand('config:show app.name') }}`, `{{ $assist->artisanCommand('config:show database.default') }}`. Or read config files directly from the `config/` directory.
|
||||
- To check environment variables, read the `.env` file directly.
|
||||
|
||||
## Tinker
|
||||
- Execute PHP in app context for debugging and testing code. Do not create models without user approval, prefer tests with factories instead. Prefer existing Artisan commands over custom tinker code.
|
||||
- Always use single quotes to prevent shell expansion: `{{ $assist->artisanCommand("tinker --execute 'Your::code();'") }}`
|
||||
- Double quotes for PHP strings inside: `{{ $assist->artisanCommand("tinker --execute 'User::where(\"active\", true)->count();'") }}`
|
||||
3
vendor/laravel/boost/.ai/deployments/core.blade.php
vendored
Normal file
3
vendor/laravel/boost/.ai/deployments/core.blade.php
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Deployment
|
||||
|
||||
- Laravel can be deployed using [Laravel Cloud](https://cloud.laravel.com/), which is the fastest way to deploy and scale production Laravel applications.
|
||||
7
vendor/laravel/boost/.ai/enforce-tests.blade.php
vendored
Normal file
7
vendor/laravel/boost/.ai/enforce-tests.blade.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Test Enforcement
|
||||
|
||||
- Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
|
||||
- Run the minimum number of tests needed to ensure code quality and speed. Use `{{ $assist->artisanCommand('test --compact') }}` with a specific filename or filter.
|
||||
79
vendor/laravel/boost/.ai/fluxui-free/skill/fluxui-development/SKILL.blade.php
vendored
Normal file
79
vendor/laravel/boost/.ai/fluxui-free/skill/fluxui-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
---
|
||||
name: fluxui-development
|
||||
description: "Use this skill for Flux UI development in Livewire applications only. Trigger when working with <flux:*> components, building or customizing Livewire component UIs, creating forms, modals, tables, or other interactive elements. Covers: flux: components (buttons, inputs, modals, forms, tables, date-pickers, kanban, badges, tooltips, etc.), component composition, Tailwind CSS styling, Heroicons/Lucide icon integration, validation patterns, responsive design, and theming. Do not use for non-Livewire frameworks or non-component styling."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Flux UI Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Flux UI patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
This project uses the free edition of Flux UI, which includes all free components and variants but not Pro components.
|
||||
|
||||
Flux UI is a component library for Livewire built with Tailwind CSS. It provides components that are easy to use and customize.
|
||||
|
||||
Use Flux UI components when available. Fall back to standard Blade components when no Flux component exists for your needs.
|
||||
|
||||
@boostsnippet("Basic Button", "blade")
|
||||
<flux:button variant="primary">Click me</flux:button>
|
||||
@endboostsnippet
|
||||
|
||||
## Available Components (Free Edition)
|
||||
|
||||
Available: avatar, badge, brand, breadcrumbs, button, callout, checkbox, dropdown, field, heading, icon, input, modal, navbar, otp-input, profile, radio, select, separator, skeleton, switch, text, textarea, tooltip
|
||||
|
||||
## Icons
|
||||
|
||||
Flux includes [Heroicons](https://heroicons.com/) as its default icon set. Search for exact icon names on the Heroicons site - do not guess or invent icon names.
|
||||
|
||||
@boostsnippet("Icon Button", "blade")
|
||||
<flux:button icon="arrow-down-tray">Export</flux:button>
|
||||
@endboostsnippet
|
||||
|
||||
For icons not available in Heroicons, use [Lucide](https://lucide.dev/). Import the icons you need with the Artisan command:
|
||||
|
||||
```bash
|
||||
{{ $assist->artisanCommand('flux:icon crown grip-vertical github') }}
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Form Fields
|
||||
|
||||
@boostsnippet("Form Field", "blade")
|
||||
<flux:field>
|
||||
<flux:label>Email</flux:label>
|
||||
<flux:input type="email" wire:model="email" />
|
||||
<flux:error name="email" />
|
||||
</flux:field>
|
||||
@endboostsnippet
|
||||
|
||||
### Modals
|
||||
|
||||
@boostsnippet("Modal", "blade")
|
||||
<flux:modal wire:model="showModal">
|
||||
<flux:heading>Title</flux:heading>
|
||||
<p>Content</p>
|
||||
</flux:modal>
|
||||
@endboostsnippet
|
||||
|
||||
## Verification
|
||||
|
||||
1. Check component renders correctly
|
||||
2. Test interactive states
|
||||
3. Verify mobile responsiveness
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Trying to use Pro-only components in the free edition
|
||||
- Not checking if a Flux component exists before creating custom implementations
|
||||
- Forgetting to use the `search-docs` tool for component-specific documentation
|
||||
- Not following existing project patterns for Flux usage
|
||||
82
vendor/laravel/boost/.ai/fluxui-pro/skill/fluxui-development/SKILL.blade.php
vendored
Normal file
82
vendor/laravel/boost/.ai/fluxui-pro/skill/fluxui-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
---
|
||||
name: fluxui-development
|
||||
description: "Use this skill for Flux UI development in Livewire applications only. Trigger when working with <flux:*> components, building or customizing Livewire component UIs, creating forms, modals, tables, or other interactive elements. Covers: flux: components (buttons, inputs, modals, forms, tables, date-pickers, kanban, badges, tooltips, etc.), component composition, Tailwind CSS styling, Heroicons/Lucide icon integration, validation patterns, responsive design, and theming. Do not use for non-Livewire frameworks or non-component styling."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Flux UI Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Flux UI patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
This project uses the Pro version of Flux UI, which includes all free and Pro components and variants.
|
||||
|
||||
Flux UI is a component library for Livewire built with Tailwind CSS. It provides components that are easy to use and customize.
|
||||
|
||||
Use Flux UI components when available. Fall back to standard Blade components when no Flux component exists for your needs.
|
||||
|
||||
@boostsnippet("Basic Button", "blade")
|
||||
<flux:button variant="primary">Click me</flux:button>
|
||||
@endboostsnippet
|
||||
|
||||
## Available Components (Pro Edition)
|
||||
|
||||
Available: accordion, autocomplete, avatar, badge, brand, breadcrumbs, button, calendar, callout, card, chart, checkbox, command, composer, context, date-picker, dropdown, editor, field, file-upload, heading, icon, input, kanban, modal, navbar, otp-input, pagination, pillbox, popover, profile, radio, select, separator, skeleton, slider, switch, table, tabs, text, textarea, time-picker, toast, tooltip
|
||||
|
||||
## Icons
|
||||
|
||||
Flux includes [Heroicons](https://heroicons.com/) as its default icon set. Search for exact icon names on the Heroicons site - do not guess or invent icon names.
|
||||
|
||||
@boostsnippet("Icon Button", "blade")
|
||||
<flux:button icon="arrow-down-tray">Export</flux:button>
|
||||
@endboostsnippet
|
||||
|
||||
For icons not available in Heroicons, use [Lucide](https://lucide.dev/). Import the icons you need with the Artisan command:
|
||||
|
||||
```bash
|
||||
{{ $assist->artisanCommand('flux:icon crown grip-vertical github') }}
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Form Fields
|
||||
|
||||
@boostsnippet("Form Field", "blade")
|
||||
<flux:field>
|
||||
<flux:label>Email</flux:label>
|
||||
<flux:input type="email" wire:model="email" />
|
||||
<flux:error name="email" />
|
||||
</flux:field>
|
||||
@endboostsnippet
|
||||
|
||||
### Tables
|
||||
|
||||
@boostsnippet("Table", "blade")
|
||||
<flux:table>
|
||||
<flux:table.columns>
|
||||
<flux:table.cell>Column Name</flux:table.cell>
|
||||
</flux:table.columns>
|
||||
<flux:table.row>
|
||||
<flux:table.cell>Value</flux:table.cell>
|
||||
</flux:table.row>
|
||||
</flux:table>
|
||||
@endboostsnippet
|
||||
|
||||
## Verification
|
||||
|
||||
1. Check component renders correctly
|
||||
2. Test interactive states
|
||||
3. Verify mobile responsiveness
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Not checking if a Flux component exists before creating custom implementations
|
||||
- Forgetting to use the `search-docs` tool for component-specific documentation
|
||||
- Not following existing project patterns for Flux usage
|
||||
132
vendor/laravel/boost/.ai/folio/skill/folio-routing/SKILL.blade.php
vendored
Normal file
132
vendor/laravel/boost/.ai/folio/skill/folio-routing/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
---
|
||||
name: folio-routing
|
||||
description: "Use this skill for Laravel Folio file-based routing tasks: creating pages with `folio:page`, setting up route parameters or model binding in filenames like `[User].blade.php`, defining named routes with `name()`, applying middleware, debugging Folio 404s, or running `folio:list`. Also trigger when a user is choosing between Folio and web.php for a new page, or wants to add a new URL or page in a Folio-enabled project (`resources/views/pages`). Folio automatically maps Blade templates to routes. Do not trigger for Livewire component, Normal Routing, Standard controller routes, or API endpoints"
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Folio Routing
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Folio patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Laravel Folio is a file-based router that creates a new route for every Blade file within the configured directory.
|
||||
|
||||
Pages are usually in `resources/views/pages/` and the file structure determines routes:
|
||||
|
||||
- `pages/index.blade.php` → `/`
|
||||
- `pages/profile/index.blade.php` → `/profile`
|
||||
- `pages/auth/login.blade.php` → `/auth/login`
|
||||
|
||||
### Listing Routes
|
||||
|
||||
You may list available Folio routes using `{{ $assist->artisanCommand('folio:list') }}`.
|
||||
|
||||
### Creating Pages
|
||||
|
||||
Always create new `folio` pages and routes using `{{ $assist->artisanCommand('folio:page [name]') }}` following existing naming conventions.
|
||||
|
||||
@boostsnippet("Example folio:page Commands for Automatic Routing", "shell")
|
||||
// Creates: resources/views/pages/products.blade.php → /products
|
||||
{{ $assist->artisanCommand('folio:page "products"') }}
|
||||
|
||||
// Creates: resources/views/pages/products/[id].blade.php → /products/{id}
|
||||
{{ $assist->artisanCommand('folio:page "products/[id]"') }}
|
||||
|
||||
// Creates: resources/views/pages/users/[User].blade.php → /users/{user} (implicit model binding)
|
||||
{{ $assist->artisanCommand('folio:page "users/[User]"') }}
|
||||
@endboostsnippet
|
||||
|
||||
## Route Parameters vs. Model Binding
|
||||
|
||||
Use the correct filename token based on intent:
|
||||
|
||||
- `[id]` (lowercase) captures a plain route parameter string
|
||||
- `[User]` (capitalized model class) enables implicit Eloquent model binding
|
||||
- `[Post:slug]` binds by a custom key instead of `id`
|
||||
|
||||
Model binding is case-sensitive in the filename. Avoid `[user]` when you expect a `User` model instance.
|
||||
|
||||
@boostsnippet("Route Parameter vs Model Binding Example", "blade")
|
||||
{{-- pages/users/[id].blade.php --}}
|
||||
<div>User ID: {{ $id }}</div>
|
||||
|
||||
{{-- pages/users/[User].blade.php --}}
|
||||
<div>User ID: {{ $user->id }}</div>
|
||||
@endboostsnippet
|
||||
|
||||
## Named Routes
|
||||
|
||||
Add a `name` at the top of each new Folio page to create a named route that other parts of the codebase can reference.
|
||||
|
||||
@boostsnippet("Named Routes Example", "php")
|
||||
use function Laravel\Folio\name;
|
||||
|
||||
name('products.index');
|
||||
@endboostsnippet
|
||||
|
||||
## Middleware
|
||||
|
||||
@boostsnippet("Middleware Example", "php")
|
||||
use function Laravel\Folio\{name, middleware};
|
||||
|
||||
name('admin.products');
|
||||
middleware(['auth', 'verified']);
|
||||
@endboostsnippet
|
||||
|
||||
## Page Content Patterns
|
||||
|
||||
Folio pages are normal Blade files. Include practical data-loading code when creating or editing pages.
|
||||
|
||||
@boostsnippet("Inline Query Example in a Folio Page", "blade")
|
||||
@php
|
||||
use App\Models\Post;
|
||||
|
||||
$posts = Post::query()
|
||||
->whereNotNull('published_at')
|
||||
->latest('published_at')
|
||||
->get();
|
||||
@endphp
|
||||
|
||||
<ul>
|
||||
@foreach ($posts as $post)
|
||||
<li>{{ $post->title }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endboostsnippet
|
||||
|
||||
@boostsnippet("Render Hook Example for View Data", "php")
|
||||
<?php
|
||||
use App\Models\Post;
|
||||
use Illuminate\View\View;
|
||||
use function Laravel\Folio\render;
|
||||
|
||||
render(function (View $view, Post $post) {
|
||||
return $view->with('photos', $post->author->photos);
|
||||
});
|
||||
?>
|
||||
@endboostsnippet
|
||||
|
||||
## Verification
|
||||
|
||||
1. Run `{{ $assist->artisanCommand('folio:list') }}` to verify route registration
|
||||
2. Test page loads at expected URL
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Forgetting to add named routes to new Folio pages
|
||||
- Using `[id]` or `[user]` when model binding requires `[User]`
|
||||
- Not following existing naming conventions when creating pages
|
||||
- Creating routes manually in `routes/web.php` instead of using Folio's file-based routing
|
||||
|
||||
### Folio 404 Debug Checklist
|
||||
|
||||
1. Run `{{ $assist->artisanCommand('folio:list') }}`
|
||||
2. If routes are missing, confirm Folio is mounted (`folio:install` + provider registration, or `Folio::path(...)`) and pages are under the mounted path
|
||||
3. Verify filename-to-route mapping (`index.blade.php`, nested paths, `[id]` vs `[Model]`), then rerun `{{ $assist->artisanCommand('folio:list') }}`
|
||||
45
vendor/laravel/boost/.ai/foundation.blade.php
vendored
Normal file
45
vendor/laravel/boost/.ai/foundation.blade.php
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Laravel Boost Guidelines
|
||||
|
||||
The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to ensure the best experience when building Laravel applications.
|
||||
|
||||
## Foundational Context
|
||||
This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions.
|
||||
|
||||
- php - {{ PHP_MAJOR_VERSION }}.{{ PHP_MINOR_VERSION }}
|
||||
@foreach (app(\Laravel\Roster\Roster::class)->packages()->unique(fn ($package) => $package->rawName()) as $package)
|
||||
- {{ $package->rawName() }} ({{ $package->name() }}) - v{{ $package->majorVersion() }}
|
||||
@endforeach
|
||||
|
||||
@if (! empty(config('boost.purpose')))
|
||||
Application purpose: {!! config('boost.purpose') !!}
|
||||
|
||||
@endif
|
||||
|
||||
@if($assist->hasSkillsEnabled())
|
||||
## Skills Activation
|
||||
This project has domain-specific skills available in `**/skills/**`. You MUST activate the relevant skill whenever you work in that domain—don't wait until you're stuck.
|
||||
@endif
|
||||
|
||||
## Conventions
|
||||
- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, and naming.
|
||||
- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`.
|
||||
- Check for existing components to reuse before writing a new one.
|
||||
|
||||
## Verification Scripts
|
||||
- Do not create verification scripts or tinker when tests cover that functionality and prove they work. Unit and feature tests are more important.
|
||||
|
||||
## Application Structure & Architecture
|
||||
- Stick to existing directory structure; don't create new base folders without approval.
|
||||
- Do not change the application's dependencies without approval.
|
||||
|
||||
## Frontend Bundling
|
||||
- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `{{ $assist->nodePackageManagerCommand('run build') }}`, `{{ $assist->nodePackageManagerCommand('run dev') }}`, or `{{ $assist->composerCommand('run dev') }}`. Ask them.
|
||||
|
||||
## Documentation Files
|
||||
- You must only create documentation files if explicitly requested by the user.
|
||||
|
||||
## Replies
|
||||
- Be concise in your explanations - focus on what's important rather than explaining obvious details.
|
||||
4
vendor/laravel/boost/.ai/herd/core.blade.php
vendored
Normal file
4
vendor/laravel/boost/.ai/herd/core.blade.php
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Laravel Herd
|
||||
|
||||
- The application is served by Laravel Herd at `https?://[kebab-case-project-dir].test`. Use the `get-absolute-url` tool to generate valid URLs. Never run commands to serve the site. It is always available.
|
||||
- Use the `herd` CLI to manage services, PHP versions, and sites (e.g. `herd sites`, `herd services:start <service>`, `herd php:list`). Run `herd list` to discover all available commands.
|
||||
24
vendor/laravel/boost/.ai/inertia-laravel/core.blade.php
vendored
Normal file
24
vendor/laravel/boost/.ai/inertia-laravel/core.blade.php
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Inertia
|
||||
|
||||
- Inertia creates fully client-side rendered SPAs without modern SPA complexity, leveraging existing server-side patterns.
|
||||
- Components live in `{{ $assist->inertia()->pagesDirectory() }}` (unless specified in `vite.config.js`). Use `Inertia::render()` for server-side routing instead of Blade views.
|
||||
- ALWAYS use `search-docs` tool for version-specific Inertia documentation and updated code examples.
|
||||
@if($assist->hasPackage(\Laravel\Roster\Enums\Packages::INERTIA_REACT))
|
||||
- IMPORTANT: Activate `inertia-react-development` when working with Inertia client-side patterns.
|
||||
@elseif($assist->hasPackage(\Laravel\Roster\Enums\Packages::INERTIA_VUE))
|
||||
- IMPORTANT: Activate `inertia-vue-development` when working with Inertia Vue client-side patterns.
|
||||
@elseif($assist->hasPackage(\Laravel\Roster\Enums\Packages::INERTIA_SVELTE))
|
||||
- IMPORTANT: Activate `inertia-svelte-development` when working with Inertia Svelte client-side patterns.
|
||||
@endif
|
||||
|
||||
@if($assist->inertia()->gte('2.0.0'))
|
||||
# Inertia v2
|
||||
|
||||
- Use all Inertia features from v1 and v2. Check the documentation before making changes to ensure the correct approach.
|
||||
- New features: deferred props, infinite scrolling (merging props + `WhenVisible`), lazy loading on scroll, polling, prefetching.
|
||||
- When using deferred props, add an empty state with a pulsing or animated skeleton.
|
||||
@else
|
||||
# Inertia v1
|
||||
|
||||
- Inertia v1 does not support the following v2 features: deferred props, infinite scrolling (merging props + `WhenVisible`), lazy loading on scroll, polling, or prefetching. Do not use these.
|
||||
@endif
|
||||
181
vendor/laravel/boost/.ai/inertia-react/1/skill/inertia-react-development/SKILL.blade.php
vendored
Normal file
181
vendor/laravel/boost/.ai/inertia-react/1/skill/inertia-react-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
---
|
||||
name: inertia-react-development
|
||||
description: "Develops Inertia.js v1 React client-side applications. Activates when creating React pages, forms, or navigation; using Link or router; or when user mentions React with Inertia, React pages, React forms, or React navigation."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Inertia React Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Inertia v1 React patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Page Components Location
|
||||
|
||||
React page components should be placed in the `{{ $assist->inertia()->pagesDirectory() }}` directory.
|
||||
|
||||
### Page Component Structure
|
||||
|
||||
@boostsnippet("Basic React Page Component", "react")
|
||||
export default function UsersIndex({ users }) {
|
||||
return (
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
<ul>
|
||||
{users.map(user => <li key={user.id}>{user.name}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
### Client-Side Navigation
|
||||
|
||||
Use `<Link>` for client-side navigation (not `<a>` tags):
|
||||
|
||||
@boostsnippet("Inertia React Navigation", "react")
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/users">Users</Link>
|
||||
<Link href={`/users/${user.id}`}>View User</Link>
|
||||
@endboostsnippet
|
||||
|
||||
### Link With Method
|
||||
|
||||
@boostsnippet("Link with POST Method", "react")
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
<Link href="/logout" method="post" as="button">
|
||||
Logout
|
||||
</Link>
|
||||
@endboostsnippet
|
||||
|
||||
### Programmatic Navigation
|
||||
|
||||
@boostsnippet("Router Visit", "react")
|
||||
import { router } from '@inertiajs/react'
|
||||
|
||||
function handleClick() {
|
||||
router.visit('/users')
|
||||
}
|
||||
|
||||
// Or with options
|
||||
router.visit('/users', {
|
||||
method: 'post',
|
||||
data: { name: 'John' },
|
||||
onSuccess: () => console.log('Success!'),
|
||||
})
|
||||
@endboostsnippet
|
||||
|
||||
## Form Handling
|
||||
|
||||
### Using `router.post`
|
||||
|
||||
@boostsnippet("Form With router.post", "react")
|
||||
import { router } from '@inertiajs/react'
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function CreateUser() {
|
||||
const [values, setValues] = useState({
|
||||
name: '',
|
||||
email: '',
|
||||
})
|
||||
const [processing, setProcessing] = useState(false)
|
||||
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault()
|
||||
setProcessing(true)
|
||||
|
||||
router.post('/users', values, {
|
||||
onFinish: () => setProcessing(false),
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input
|
||||
type="text"
|
||||
value={values.name}
|
||||
onChange={e => setValues({ ...values, name: e.target.value })}
|
||||
/>
|
||||
<input
|
||||
type="email"
|
||||
value={values.email}
|
||||
onChange={e => setValues({ ...values, email: e.target.value })}
|
||||
/>
|
||||
<button type="submit" disabled={processing}>
|
||||
Create User
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
### Using `useForm` Hook (If Available)
|
||||
|
||||
Check the Inertia documentation to confirm if `useForm` is available in your version:
|
||||
|
||||
@boostsnippet("useForm Hook Example", "react")
|
||||
import { useForm } from '@inertiajs/react'
|
||||
|
||||
export default function CreateUser() {
|
||||
const { data, setData, post, processing, errors } = useForm({
|
||||
name: '',
|
||||
email: '',
|
||||
})
|
||||
|
||||
function submit(e) {
|
||||
e.preventDefault()
|
||||
post('/users')
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={submit}>
|
||||
<input
|
||||
type="text"
|
||||
value={data.name}
|
||||
onChange={e => setData('name', e.target.value)}
|
||||
/>
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<input
|
||||
type="email"
|
||||
value={data.email}
|
||||
onChange={e => setData('email', e.target.value)}
|
||||
/>
|
||||
{errors.email && <div>{errors.email}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
Create User
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
## Inertia v1 Limitations
|
||||
|
||||
Inertia v1 does not support these v2 features:
|
||||
|
||||
- `<Form>` component
|
||||
- Deferred props
|
||||
- Prefetching
|
||||
- Polling
|
||||
- Infinite scrolling with `WhenVisible`
|
||||
- Merging props
|
||||
|
||||
Do not use these features in v1 projects.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
|
||||
- Trying to use Inertia v2 features (deferred props, `<Form>` component, etc.) in v1 projects
|
||||
- Using `<form>` without preventing default submission (use `e.preventDefault()`)
|
||||
- Not handling loading states during form submission
|
||||
371
vendor/laravel/boost/.ai/inertia-react/2/skill/inertia-react-development/SKILL.blade.php
vendored
Normal file
371
vendor/laravel/boost/.ai/inertia-react/2/skill/inertia-react-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,371 @@
|
||||
---
|
||||
name: inertia-react-development
|
||||
description: "Develops Inertia.js v2 React client-side applications. Activates when creating React pages, forms, or navigation; using <Link>, <Form>, useForm, or router; working with deferred props, prefetching, or polling; or when user mentions React with Inertia, React pages, React forms, or React navigation."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Inertia React Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Inertia v2 React patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Page Components Location
|
||||
|
||||
React page components should be placed in the `{{ $assist->inertia()->pagesDirectory() }}` directory.
|
||||
|
||||
### Page Component Structure
|
||||
|
||||
@boostsnippet("Basic React Page Component", "react")
|
||||
export default function UsersIndex({ users }) {
|
||||
return (
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
<ul>
|
||||
{users.map(user => <li key={user.id}>{user.name}</li>)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
## Client-Side Navigation
|
||||
|
||||
### Basic Link Component
|
||||
|
||||
Use `<Link>` for client-side navigation instead of traditional `<a>` tags:
|
||||
|
||||
@boostsnippet("Inertia React Navigation", "react")
|
||||
import { Link, router } from '@inertiajs/react'
|
||||
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/users">Users</Link>
|
||||
<Link href={`/users/${user.id}`}>View User</Link>
|
||||
@endboostsnippet
|
||||
|
||||
### Link with Method
|
||||
|
||||
@boostsnippet("Link with POST Method", "react")
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
<Link href="/logout" method="post" as="button">
|
||||
Logout
|
||||
</Link>
|
||||
@endboostsnippet
|
||||
|
||||
### Prefetching
|
||||
|
||||
Prefetch pages to improve perceived performance:
|
||||
|
||||
@boostsnippet("Prefetch on Hover", "react")
|
||||
import { Link } from '@inertiajs/react'
|
||||
|
||||
<Link href="/users" prefetch>
|
||||
Users
|
||||
</Link>
|
||||
@endboostsnippet
|
||||
|
||||
### Programmatic Navigation
|
||||
|
||||
@boostsnippet("Router Visit", "react")
|
||||
import { router } from '@inertiajs/react'
|
||||
|
||||
function handleClick() {
|
||||
router.visit('/users')
|
||||
}
|
||||
|
||||
// Or with options
|
||||
router.visit('/users', {
|
||||
method: 'post',
|
||||
data: { name: 'John' },
|
||||
onSuccess: () => console.log('Success!'),
|
||||
})
|
||||
@endboostsnippet
|
||||
|
||||
## Form Handling
|
||||
|
||||
@if($assist->inertia()->hasFormComponent())
|
||||
### Form Component (Recommended)
|
||||
|
||||
The recommended way to build forms is with the `<Form>` component:
|
||||
|
||||
@boostsnippet("Form Component Example", "react")
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
export default function CreateUser() {
|
||||
return (
|
||||
<Form action="/users" method="post">
|
||||
{({ errors, processing, wasSuccessful }) => (
|
||||
<>
|
||||
<input type="text" name="name" />
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<input type="email" name="email" />
|
||||
{errors.email && <div>{errors.email}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
{processing ? 'Creating...' : 'Create User'}
|
||||
</button>
|
||||
|
||||
{wasSuccessful && <div>User created!</div>}
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
### Form Component With All Props
|
||||
|
||||
@boostsnippet("Form Component Full Example", "react")
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
<Form action="/users" method="post">
|
||||
{({
|
||||
errors,
|
||||
hasErrors,
|
||||
processing,
|
||||
progress,
|
||||
wasSuccessful,
|
||||
recentlySuccessful,
|
||||
clearErrors,
|
||||
resetAndClearErrors,
|
||||
defaults,
|
||||
isDirty,
|
||||
reset,
|
||||
submit
|
||||
}) => (
|
||||
<>
|
||||
<input type="text" name="name" defaultValue={defaults.name} />
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
{processing ? 'Saving...' : 'Save'}
|
||||
</button>
|
||||
|
||||
{progress && (
|
||||
<progress value={progress.percentage} max="100">
|
||||
{progress.percentage}%
|
||||
</progress>
|
||||
)}
|
||||
|
||||
{wasSuccessful && <div>Saved!</div>}
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
@endboostsnippet
|
||||
|
||||
@if($assist->inertia()->hasFormComponentResets())
|
||||
### Form Component Reset Props
|
||||
|
||||
The `<Form>` component supports automatic resetting:
|
||||
|
||||
- `resetOnError` - Reset form data when the request fails
|
||||
- `resetOnSuccess` - Reset form data when the request succeeds
|
||||
- `setDefaultsOnSuccess` - Update default values on success
|
||||
|
||||
Use the `search-docs` tool with a query of `form component resetting` for detailed guidance.
|
||||
|
||||
@boostsnippet("Form with Reset Props", "react")
|
||||
import { Form } from '@inertiajs/react'
|
||||
|
||||
<Form
|
||||
action="/users"
|
||||
method="post"
|
||||
resetOnSuccess
|
||||
setDefaultsOnSuccess
|
||||
>
|
||||
{({ errors, processing, wasSuccessful }) => (
|
||||
<>
|
||||
<input type="text" name="name" />
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
Submit
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
@endboostsnippet
|
||||
@else
|
||||
Note: This version of Inertia does not support `resetOnError`, `resetOnSuccess`, or `setDefaultsOnSuccess` on the `<Form>` component. Using these props will cause errors. Upgrade to Inertia v2.2.0+ to use these features.
|
||||
@endif
|
||||
|
||||
Forms can also be built using the `useForm` helper for more programmatic control. Use the `search-docs` tool with a query of `useForm helper` for guidance.
|
||||
|
||||
@endif
|
||||
|
||||
### `useForm` Hook
|
||||
|
||||
@if($assist->inertia()->hasFormComponent() === false)
|
||||
For Inertia v2.0.x: Build forms using the `useForm` helper as the `<Form>` component is not available until v2.1.0+.
|
||||
@else
|
||||
For more programmatic control or to follow existing conventions, use the `useForm` hook:
|
||||
@endif
|
||||
|
||||
@boostsnippet("useForm Hook Example", "react")
|
||||
import { useForm } from '@inertiajs/react'
|
||||
|
||||
export default function CreateUser() {
|
||||
const { data, setData, post, processing, errors, reset } = useForm({
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
})
|
||||
|
||||
function submit(e) {
|
||||
e.preventDefault()
|
||||
post('/users', {
|
||||
onSuccess: () => reset('password'),
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={submit}>
|
||||
<input
|
||||
type="text"
|
||||
value={data.name}
|
||||
onChange={e => setData('name', e.target.value)}
|
||||
/>
|
||||
{errors.name && <div>{errors.name}</div>}
|
||||
|
||||
<input
|
||||
type="email"
|
||||
value={data.email}
|
||||
onChange={e => setData('email', e.target.value)}
|
||||
/>
|
||||
{errors.email && <div>{errors.email}</div>}
|
||||
|
||||
<input
|
||||
type="password"
|
||||
value={data.password}
|
||||
onChange={e => setData('password', e.target.value)}
|
||||
/>
|
||||
{errors.password && <div>{errors.password}</div>}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
Create User
|
||||
</button>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
## Inertia v2 Features
|
||||
|
||||
### Deferred Props
|
||||
|
||||
Use deferred props to load data after initial page render:
|
||||
|
||||
@boostsnippet("Deferred Props with Empty State", "react")
|
||||
export default function UsersIndex({ users }) {
|
||||
// users will be undefined initially, then populated
|
||||
return (
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
{!users ? (
|
||||
<div className="animate-pulse">
|
||||
<div className="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
|
||||
<div className="h-4 bg-gray-200 rounded w-1/2"></div>
|
||||
</div>
|
||||
) : (
|
||||
<ul>
|
||||
{users.map(user => (
|
||||
<li key={user.id}>{user.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
### Polling
|
||||
|
||||
Use the `usePoll` hook to automatically refresh data at intervals. It handles cleanup on unmount and throttles polling when the tab is inactive.
|
||||
|
||||
@boostsnippet("Basic Polling", "react")
|
||||
import { usePoll } from '@inertiajs/react'
|
||||
|
||||
export default function Dashboard({ stats }) {
|
||||
usePoll(5000)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {stats.activeUsers}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
@boostsnippet("Polling With Request Options and Manual Control", "react")
|
||||
import { usePoll } from '@inertiajs/react'
|
||||
|
||||
export default function Dashboard({ stats }) {
|
||||
const { start, stop } = usePoll(5000, {
|
||||
only: ['stats'],
|
||||
onStart() {
|
||||
console.log('Polling request started')
|
||||
},
|
||||
onFinish() {
|
||||
console.log('Polling request finished')
|
||||
},
|
||||
}, {
|
||||
autoStart: false,
|
||||
keepAlive: true,
|
||||
})
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {stats.activeUsers}</div>
|
||||
<button onClick={start}>Start Polling</button>
|
||||
<button onClick={stop}>Stop Polling</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
- `autoStart` (default `true`) — set to `false` to start polling manually via the returned `start()` function
|
||||
- `keepAlive` (default `false`) — set to `true` to prevent throttling when the browser tab is inactive
|
||||
|
||||
### WhenVisible (Infinite Scroll)
|
||||
|
||||
Load more data when user scrolls to a specific element:
|
||||
|
||||
@boostsnippet("Infinite Scroll with WhenVisible", "react")
|
||||
import { WhenVisible } from '@inertiajs/react'
|
||||
|
||||
export default function UsersList({ users }) {
|
||||
return (
|
||||
<div>
|
||||
{users.data.map(user => (
|
||||
<div key={user.id}>{user.name}</div>
|
||||
))}
|
||||
|
||||
{users.next_page_url && (
|
||||
<WhenVisible
|
||||
data="users"
|
||||
params={{ page: users.current_page + 1 }}
|
||||
fallback={<div>Loading more...</div>}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
|
||||
- Forgetting to add loading states (skeleton screens) when using deferred props
|
||||
- Not handling the `undefined` state of deferred props before data loads
|
||||
- Using `<form>` without preventing default submission (use `<Form>` component or `e.preventDefault()`)
|
||||
- Forgetting to check if `<Form>` component is available in your Inertia version
|
||||
3
vendor/laravel/boost/.ai/inertia-react/core.blade.php
vendored
Normal file
3
vendor/laravel/boost/.ai/inertia-react/core.blade.php
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Inertia + React
|
||||
|
||||
- IMPORTANT: Activate `inertia-react-development` when working with Inertia React client-side patterns.
|
||||
140
vendor/laravel/boost/.ai/inertia-svelte/1/skill/inertia-svelte-development/SKILL.blade.php
vendored
Normal file
140
vendor/laravel/boost/.ai/inertia-svelte/1/skill/inertia-svelte-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
---
|
||||
name: inertia-svelte-development
|
||||
description: "Develops Inertia.js v1 Svelte client-side applications. Activates when creating Svelte pages, forms, or navigation; using Link or router; or when user mentions Svelte with Inertia, Svelte pages, Svelte forms, or Svelte navigation."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Inertia Svelte Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Inertia v1 Svelte patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Page Components Location
|
||||
|
||||
Svelte page components should be placed in the `{{ $assist->inertia()->pagesDirectory() }}` directory.
|
||||
|
||||
### Page Component Structure
|
||||
|
||||
@boostsnippet("Basic Svelte Page Component", "svelte")
|
||||
<script>
|
||||
export let users
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
<ul>
|
||||
{#each users as user (user.id)}
|
||||
<li>{user.name}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
@endboostsnippet
|
||||
|
||||
## Client-Side Navigation
|
||||
|
||||
### Basic Link Component
|
||||
|
||||
Use `<Link>` for client-side navigation instead of traditional `<a>` tags:
|
||||
|
||||
@boostsnippet("Inertia Svelte Navigation", "svelte")
|
||||
<script>
|
||||
import { Link } from '@inertiajs/svelte'
|
||||
</script>
|
||||
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/users">Users</Link>
|
||||
<Link href={`/users/${user.id}`}>View User</Link>
|
||||
@endboostsnippet
|
||||
|
||||
### Link with Method
|
||||
|
||||
@boostsnippet("Link with POST Method", "svelte")
|
||||
<script>
|
||||
import { Link } from '@inertiajs/svelte'
|
||||
</script>
|
||||
|
||||
<Link href="/logout" method="post">Logout</Link>
|
||||
@endboostsnippet
|
||||
|
||||
### Programmatic Navigation
|
||||
|
||||
@boostsnippet("Router Visit", "svelte")
|
||||
<script>
|
||||
import { router } from '@inertiajs/svelte'
|
||||
|
||||
function handleClick() {
|
||||
router.visit('/users')
|
||||
}
|
||||
|
||||
// Or with options
|
||||
function createUser() {
|
||||
router.visit('/users', {
|
||||
method: 'post',
|
||||
data: { name: 'John' },
|
||||
onSuccess: () => console.log('Success!'),
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@endboostsnippet
|
||||
|
||||
## Form Handling
|
||||
|
||||
### Using `router.post`
|
||||
|
||||
@boostsnippet("Form with router.post", "svelte")
|
||||
<script>
|
||||
import { router } from '@inertiajs/svelte'
|
||||
|
||||
let form = {
|
||||
name: '',
|
||||
email: '',
|
||||
}
|
||||
let processing = false
|
||||
|
||||
function handleSubmit() {
|
||||
processing = true
|
||||
|
||||
router.post('/users', form, {
|
||||
onFinish: () => processing = false,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={handleSubmit}>
|
||||
<input type="text" bind:value={form.name} />
|
||||
<input type="email" bind:value={form.email} />
|
||||
<button type="submit" disabled={processing}>
|
||||
Create User
|
||||
</button>
|
||||
</form>
|
||||
@endboostsnippet
|
||||
|
||||
## Inertia v1 Limitations
|
||||
|
||||
Inertia v1 does not support these v2 features:
|
||||
- `<Form>` component
|
||||
- Deferred props
|
||||
- Prefetching
|
||||
- Polling
|
||||
- Infinite scrolling with `WhenVisible`
|
||||
- Merging props
|
||||
|
||||
Do not use these features in v1 projects.
|
||||
|
||||
## Server-Side Patterns
|
||||
|
||||
Server-side patterns (Inertia::render, props, middleware) are covered in inertia-laravel guidelines.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
|
||||
- Trying to use Inertia v2 features (deferred props, `<Form>` component, etc.) in v1 projects
|
||||
- Using `<form>` without preventing default submission (use `on:submit|preventDefault`)
|
||||
- Not handling loading states during form submission
|
||||
310
vendor/laravel/boost/.ai/inertia-svelte/2/skill/inertia-svelte-development/SKILL.blade.php
vendored
Normal file
310
vendor/laravel/boost/.ai/inertia-svelte/2/skill/inertia-svelte-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
---
|
||||
name: inertia-svelte-development
|
||||
description: "Develops Inertia.js v2 Svelte client-side applications. Activates when creating Svelte pages, forms, or navigation; using Link, Form, or router; working with deferred props, prefetching, or polling; or when user mentions Svelte with Inertia, Svelte pages, Svelte forms, or Svelte navigation."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Inertia Svelte Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Inertia v2 Svelte patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Page Components Location
|
||||
|
||||
Svelte page components should be placed in the `{{ $assist->inertia()->pagesDirectory() }}` directory.
|
||||
|
||||
### Page Component Structure
|
||||
|
||||
@boostsnippet("Basic Svelte Page Component", "svelte")
|
||||
<script>
|
||||
export let users
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
<ul>
|
||||
{#each users as user (user.id)}
|
||||
<li>{user.name}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
@endboostsnippet
|
||||
|
||||
## Client-Side Navigation
|
||||
|
||||
### Basic Link Component
|
||||
|
||||
Use `<Link>` for client-side navigation instead of traditional `<a>` tags:
|
||||
|
||||
@boostsnippet("Inertia Svelte Navigation", "svelte")
|
||||
<script>
|
||||
import { Link } from '@inertiajs/svelte'
|
||||
</script>
|
||||
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/users">Users</Link>
|
||||
<Link href={`/users/${user.id}`}>View User</Link>
|
||||
@endboostsnippet
|
||||
|
||||
### Link With Method
|
||||
|
||||
@boostsnippet("Link With POST Method", "svelte")
|
||||
<script>
|
||||
import { Link } from '@inertiajs/svelte'
|
||||
</script>
|
||||
|
||||
<Link href="/logout" method="post">Logout</Link>
|
||||
@endboostsnippet
|
||||
|
||||
### Prefetching
|
||||
|
||||
Prefetch pages to improve perceived performance:
|
||||
|
||||
@boostsnippet("Prefetch on Hover", "svelte")
|
||||
<script>
|
||||
import { Link } from '@inertiajs/svelte'
|
||||
</script>
|
||||
|
||||
<Link href="/users" prefetch>Users</Link>
|
||||
@endboostsnippet
|
||||
|
||||
### Programmatic Navigation
|
||||
|
||||
@boostsnippet("Router Visit", "svelte")
|
||||
<script>
|
||||
import { router } from '@inertiajs/svelte'
|
||||
|
||||
function handleClick() {
|
||||
router.visit('/users')
|
||||
}
|
||||
|
||||
// Or with options
|
||||
function createUser() {
|
||||
router.visit('/users', {
|
||||
method: 'post',
|
||||
data: { name: 'John' },
|
||||
onSuccess: () => console.log('Success!'),
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@endboostsnippet
|
||||
|
||||
## Form Handling
|
||||
|
||||
@if($assist->inertia()->hasFormComponent())
|
||||
### Form Component (Recommended)
|
||||
|
||||
The recommended way to build forms is with the `<Form>` component:
|
||||
|
||||
@boostsnippet("Form Component Example", "svelte")
|
||||
<script>
|
||||
import { Form } from '@inertiajs/svelte'
|
||||
</script>
|
||||
|
||||
<Form action="/users" method="post" let:errors let:processing let:wasSuccessful>
|
||||
<input type="text" name="name" />
|
||||
{#if errors.name}
|
||||
<div>{errors.name}</div>
|
||||
{/if}
|
||||
|
||||
<input type="email" name="email" />
|
||||
{#if errors.email}
|
||||
<div>{errors.email}</div>
|
||||
{/if}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
{processing ? 'Creating...' : 'Create User'}
|
||||
</button>
|
||||
|
||||
{#if wasSuccessful}
|
||||
<div>User created!</div>
|
||||
{/if}
|
||||
</Form>
|
||||
@endboostsnippet
|
||||
|
||||
@if($assist->inertia()->hasFormComponentResets())
|
||||
### Form Component Reset Props
|
||||
|
||||
The `<Form>` component supports automatic resetting:
|
||||
|
||||
- `resetOnError` - Reset form data when the request fails
|
||||
- `resetOnSuccess` - Reset form data when the request succeeds
|
||||
- `setDefaultsOnSuccess` - Update default values on success
|
||||
|
||||
Use the `search-docs` tool with a query of `form component resetting` for detailed guidance.
|
||||
|
||||
@boostsnippet("Form With Reset Props", "svelte")
|
||||
<script>
|
||||
import { Form } from '@inertiajs/svelte'
|
||||
</script>
|
||||
|
||||
<Form
|
||||
action="/users"
|
||||
method="post"
|
||||
resetOnSuccess
|
||||
setDefaultsOnSuccess
|
||||
let:errors
|
||||
let:processing
|
||||
let:wasSuccessful
|
||||
>
|
||||
<input type="text" name="name" />
|
||||
{#if errors.name}
|
||||
<div>{errors.name}</div>
|
||||
{/if}
|
||||
|
||||
<button type="submit" disabled={processing}>
|
||||
Submit
|
||||
</button>
|
||||
</Form>
|
||||
@endboostsnippet
|
||||
@else
|
||||
Note: This version of Inertia does not support `resetOnError`, `resetOnSuccess`, or `setDefaultsOnSuccess` on the `<Form>` component. Using these props will cause errors. Upgrade to Inertia v2.2.0+ to use these features.
|
||||
@endif
|
||||
|
||||
Forms can also be built using the `useForm` hook for more programmatic control. Use the `search-docs` tool with a query of `useForm helper` for guidance.
|
||||
|
||||
@endif
|
||||
|
||||
### `useForm` Hook
|
||||
|
||||
@if($assist->inertia()->hasFormComponent() === false)
|
||||
For Inertia v2.0.x: Build forms using the `useForm` hook as the `<Form>` component is not available until v2.1.0+.
|
||||
@else
|
||||
For more programmatic control or to follow existing conventions, use the `useForm` hook:
|
||||
@endif
|
||||
|
||||
@boostsnippet("useForm Example", "svelte")
|
||||
<script>
|
||||
import { useForm } from '@inertiajs/svelte'
|
||||
|
||||
const form = useForm({
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
})
|
||||
|
||||
function submit() {
|
||||
$form.post('/users', {
|
||||
onSuccess: () => $form.reset('password'),
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<form on:submit|preventDefault={submit}>
|
||||
<input type="text" bind:value={$form.name} />
|
||||
{#if $form.errors.name}
|
||||
<div>{$form.errors.name}</div>
|
||||
{/if}
|
||||
|
||||
<input type="email" bind:value={$form.email} />
|
||||
{#if $form.errors.email}
|
||||
<div>{$form.errors.email}</div>
|
||||
{/if}
|
||||
|
||||
<input type="password" bind:value={$form.password} />
|
||||
{#if $form.errors.password}
|
||||
<div>{$form.errors.password}</div>
|
||||
{/if}
|
||||
|
||||
<button type="submit" disabled={$form.processing}>
|
||||
Create User
|
||||
</button>
|
||||
</form>
|
||||
@endboostsnippet
|
||||
|
||||
## Inertia v2 Features
|
||||
|
||||
### Deferred Props
|
||||
|
||||
Use deferred props to load data after initial page render:
|
||||
|
||||
@boostsnippet("Deferred Props with Empty State", "svelte")
|
||||
<script>
|
||||
export let users
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
{#if !users}
|
||||
<div class="animate-pulse">
|
||||
<div class="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
|
||||
<div class="h-4 bg-gray-200 rounded w-1/2"></div>
|
||||
</div>
|
||||
{:else}
|
||||
<ul>
|
||||
{#each users as user (user.id)}
|
||||
<li>{user.name}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
{/if}
|
||||
</div>
|
||||
@endboostsnippet
|
||||
|
||||
### Polling
|
||||
|
||||
Use the `usePoll` hook to automatically refresh data at intervals. It handles cleanup on unmount and throttles polling when the tab is inactive.
|
||||
|
||||
@boostsnippet("Basic Polling", "svelte")
|
||||
<script>
|
||||
import { usePoll } from '@inertiajs/svelte'
|
||||
|
||||
export let stats
|
||||
|
||||
usePoll(5000)
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {stats.activeUsers}</div>
|
||||
</div>
|
||||
@endboostsnippet
|
||||
|
||||
@boostsnippet("Polling With Request Options and Manual Control", "svelte")
|
||||
<script>
|
||||
import { usePoll } from '@inertiajs/svelte'
|
||||
|
||||
export let stats
|
||||
|
||||
const { start, stop } = usePoll(5000, {
|
||||
only: ['stats'],
|
||||
onStart() {
|
||||
console.log('Polling request started')
|
||||
},
|
||||
onFinish() {
|
||||
console.log('Polling request finished')
|
||||
},
|
||||
}, {
|
||||
autoStart: false,
|
||||
keepAlive: true,
|
||||
})
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {stats.activeUsers}</div>
|
||||
<button on:click={start}>Start Polling</button>
|
||||
<button on:click={stop}>Stop Polling</button>
|
||||
</div>
|
||||
@endboostsnippet
|
||||
|
||||
- `autoStart` (default `true`) — set to `false` to start polling manually via the returned `start()` function
|
||||
- `keepAlive` (default `false`) — set to `true` to prevent throttling when the browser tab is inactive
|
||||
|
||||
## Server-Side Patterns
|
||||
|
||||
Server-side patterns (Inertia::render, props, middleware) are covered in inertia-laravel guidelines.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
|
||||
- Forgetting to add loading states (skeleton screens) when using deferred props
|
||||
- Not handling the `undefined` state of deferred props before data loads
|
||||
- Using `<form>` without preventing default submission (use `<Form>` component or `on:submit|preventDefault`)
|
||||
- Forgetting to check if `<Form>` component is available in your Inertia version
|
||||
3
vendor/laravel/boost/.ai/inertia-svelte/core.blade.php
vendored
Normal file
3
vendor/laravel/boost/.ai/inertia-svelte/core.blade.php
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Inertia + Svelte
|
||||
|
||||
- IMPORTANT: Activate `inertia-svelte-development` when working with Inertia Svelte client-side patterns.
|
||||
162
vendor/laravel/boost/.ai/inertia-vue/1/skill/inertia-vue-development/SKILL.blade.php
vendored
Normal file
162
vendor/laravel/boost/.ai/inertia-vue/1/skill/inertia-vue-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
name: inertia-vue-development
|
||||
description: "Develops Inertia.js v1 Vue client-side applications. Activates when creating Vue pages, forms, or navigation; using Link or router; or when user mentions Vue with Inertia, Vue pages, Vue forms, or Vue navigation."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Inertia Vue Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Inertia v1 Vue patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Page Components Location
|
||||
|
||||
Vue page components should be placed in the `{{ $assist->inertia()->pagesDirectory() }}` directory.
|
||||
|
||||
### Page Component Structure
|
||||
|
||||
Important: Vue components must have a single root element.
|
||||
|
||||
@verbatim
|
||||
@boostsnippet("Basic Vue Page Component", "vue")
|
||||
<script setup>
|
||||
defineProps({
|
||||
users: Array
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
<ul>
|
||||
<li v-for="user in users" :key="user.id">
|
||||
{{ user.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
@endverbatim
|
||||
|
||||
## Client-Side Navigation
|
||||
|
||||
### Basic Link Component
|
||||
|
||||
Use `<Link>` for client-side navigation instead of traditional `<a>` tags:
|
||||
|
||||
@boostsnippet("Inertia Vue Navigation", "vue")
|
||||
<script setup>
|
||||
import { Link } from '@inertiajs/vue3'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/users">Users</Link>
|
||||
<Link :href="`/users/${user.id}`">View User</Link>
|
||||
</div>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
|
||||
### Link With Method
|
||||
|
||||
@boostsnippet("Link With POST Method", "vue")
|
||||
<script setup>
|
||||
import { Link } from '@inertiajs/vue3'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Link href="/logout" method="post" as="button">
|
||||
Logout
|
||||
</Link>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
|
||||
### Programmatic Navigation
|
||||
|
||||
@boostsnippet("Router Visit", "vue")
|
||||
<script setup>
|
||||
import { router } from '@inertiajs/vue3'
|
||||
|
||||
function handleClick() {
|
||||
router.visit('/users')
|
||||
}
|
||||
|
||||
// Or with options
|
||||
function createUser() {
|
||||
router.visit('/users', {
|
||||
method: 'post',
|
||||
data: { name: 'John' },
|
||||
onSuccess: () => console.log('Success!'),
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@endboostsnippet
|
||||
|
||||
## Form Handling
|
||||
|
||||
### Using `router.post`
|
||||
|
||||
@boostsnippet("Form with router.post", "vue")
|
||||
<script setup>
|
||||
import { router } from '@inertiajs/vue3'
|
||||
import { reactive, ref } from 'vue'
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
email: '',
|
||||
})
|
||||
|
||||
const processing = ref(false)
|
||||
|
||||
function handleSubmit() {
|
||||
processing.value = true
|
||||
|
||||
router.post('/users', form, {
|
||||
onFinish: () => processing.value = false,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @submit.prevent="handleSubmit">
|
||||
<input type="text" v-model="form.name" />
|
||||
<input type="email" v-model="form.email" />
|
||||
<button type="submit" :disabled="processing">
|
||||
Create User
|
||||
</button>
|
||||
</form>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
|
||||
## Inertia v1 Limitations
|
||||
|
||||
Inertia v1 does not support these v2 features:
|
||||
|
||||
- `<Form>` component
|
||||
- Deferred props
|
||||
- Prefetching
|
||||
- Polling
|
||||
- Infinite scrolling with `WhenVisible`
|
||||
- Merging props
|
||||
|
||||
Do not use these features in v1 projects.
|
||||
|
||||
## Server-Side Patterns
|
||||
|
||||
Server-side patterns (Inertia::render, props, middleware) are covered in inertia-laravel guidelines.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
|
||||
- Using multiple root elements in Vue components (while Vue 3 supports this, a single root is recommended for Inertia v1 compatibility)
|
||||
- Trying to use Inertia v2 features (deferred props, `<Form>` component, etc.) in v1 projects
|
||||
- Using `<form>` without preventing default submission (use `@submit.prevent`)
|
||||
- Not handling loading states during form submission
|
||||
422
vendor/laravel/boost/.ai/inertia-vue/2/skill/inertia-vue-development/SKILL.blade.php
vendored
Normal file
422
vendor/laravel/boost/.ai/inertia-vue/2/skill/inertia-vue-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,422 @@
|
||||
---
|
||||
name: inertia-vue-development
|
||||
description: "Develops Inertia.js v2 Vue client-side applications. Activates when creating Vue pages, forms, or navigation; using <Link>, <Form>, useForm, or router; working with deferred props, prefetching, or polling; or when user mentions Vue with Inertia, Vue pages, Vue forms, or Vue navigation."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Inertia Vue Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Inertia v2 Vue patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Page Components Location
|
||||
|
||||
Vue page components should be placed in the `{{ $assist->inertia()->pagesDirectory() }}` directory.
|
||||
|
||||
### Page Component Structure
|
||||
|
||||
Important: Vue components must have a single root element.
|
||||
|
||||
@verbatim
|
||||
@boostsnippet("Basic Vue Page Component", "vue")
|
||||
<script setup>
|
||||
defineProps({
|
||||
users: Array
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
<ul>
|
||||
<li v-for="user in users" :key="user.id">
|
||||
{{ user.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
@endverbatim
|
||||
|
||||
## Client-Side Navigation
|
||||
|
||||
### Basic Link Component
|
||||
|
||||
Use `<Link>` for client-side navigation instead of traditional `<a>` tags:
|
||||
|
||||
@boostsnippet("Inertia Vue Navigation", "vue")
|
||||
<script setup>
|
||||
import { Link } from '@inertiajs/vue3'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/users">Users</Link>
|
||||
<Link :href="`/users/${user.id}`">View User</Link>
|
||||
</div>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
|
||||
### Link with Method
|
||||
|
||||
@boostsnippet("Link with POST Method", "vue")
|
||||
<script setup>
|
||||
import { Link } from '@inertiajs/vue3'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Link href="/logout" method="post" as="button">
|
||||
Logout
|
||||
</Link>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
|
||||
### Prefetching
|
||||
|
||||
Prefetch pages to improve perceived performance:
|
||||
|
||||
@boostsnippet("Prefetch on Hover", "vue")
|
||||
<script setup>
|
||||
import { Link } from '@inertiajs/vue3'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Link href="/users" prefetch>
|
||||
Users
|
||||
</Link>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
|
||||
### Programmatic Navigation
|
||||
|
||||
@boostsnippet("Router Visit", "vue")
|
||||
<script setup>
|
||||
import { router } from '@inertiajs/vue3'
|
||||
|
||||
function handleClick() {
|
||||
router.visit('/users')
|
||||
}
|
||||
|
||||
// Or with options
|
||||
function createUser() {
|
||||
router.visit('/users', {
|
||||
method: 'post',
|
||||
data: { name: 'John' },
|
||||
onSuccess: () => console.log('Done'),
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Link href="/users">Users</Link>
|
||||
<Link href="/logout" method="post" as="button">Logout</Link>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
|
||||
## Form Handling
|
||||
|
||||
@if($assist->inertia()->hasFormComponent())
|
||||
### Form Component (Recommended)
|
||||
|
||||
The recommended way to build forms is with the `<Form>` component:
|
||||
|
||||
@verbatim
|
||||
@boostsnippet("Form Component Example", "vue")
|
||||
<script setup>
|
||||
import { Form } from '@inertiajs/vue3'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form action="/users" method="post" #default="{ errors, processing, wasSuccessful }">
|
||||
<input type="text" name="name" />
|
||||
<div v-if="errors.name">{{ errors.name }}</div>
|
||||
|
||||
<input type="email" name="email" />
|
||||
<div v-if="errors.email">{{ errors.email }}</div>
|
||||
|
||||
<button type="submit" :disabled="processing">
|
||||
{{ processing ? 'Creating...' : 'Create User' }}
|
||||
</button>
|
||||
|
||||
<div v-if="wasSuccessful">User created!</div>
|
||||
</Form>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
@endverbatim
|
||||
|
||||
### Form Component With All Props
|
||||
|
||||
@verbatim
|
||||
@boostsnippet("Form Component Full Example", "vue")
|
||||
<script setup>
|
||||
import { Form } from '@inertiajs/vue3'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form
|
||||
action="/users"
|
||||
method="post"
|
||||
#default="{
|
||||
errors,
|
||||
hasErrors,
|
||||
processing,
|
||||
progress,
|
||||
wasSuccessful,
|
||||
recentlySuccessful,
|
||||
setError,
|
||||
clearErrors,
|
||||
resetAndClearErrors,
|
||||
defaults,
|
||||
isDirty,
|
||||
reset,
|
||||
submit
|
||||
}"
|
||||
>
|
||||
<input type="text" name="name" :value="defaults.name" />
|
||||
<div v-if="errors.name">{{ errors.name }}</div>
|
||||
|
||||
<button type="submit" :disabled="processing">
|
||||
{{ processing ? 'Saving...' : 'Save' }}
|
||||
</button>
|
||||
|
||||
<progress v-if="progress" :value="progress.percentage" max="100">
|
||||
{{ progress.percentage }}%
|
||||
</progress>
|
||||
|
||||
<div v-if="wasSuccessful">Saved!</div>
|
||||
</Form>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
@endverbatim
|
||||
|
||||
@if($assist->inertia()->hasFormComponentResets())
|
||||
### Form Component Reset Props
|
||||
|
||||
The `<Form>` component supports automatic resetting:
|
||||
|
||||
- `resetOnError` - Reset form data when the request fails
|
||||
- `resetOnSuccess` - Reset form data when the request succeeds
|
||||
- `setDefaultsOnSuccess` - Update default values on success
|
||||
|
||||
Use the `search-docs` tool with a query of `form component resetting` for detailed guidance.
|
||||
|
||||
@verbatim
|
||||
@boostsnippet("Form with Reset Props", "vue")
|
||||
<script setup>
|
||||
import { Form } from '@inertiajs/vue3'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form
|
||||
action="/users"
|
||||
method="post"
|
||||
reset-on-success
|
||||
set-defaults-on-success
|
||||
#default="{ errors, processing, wasSuccessful }"
|
||||
>
|
||||
<input type="text" name="name" />
|
||||
<div v-if="errors.name">{{ errors.name }}</div>
|
||||
|
||||
<button type="submit" :disabled="processing">
|
||||
Submit
|
||||
</button>
|
||||
</Form>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
@endverbatim
|
||||
@else
|
||||
Note: This version of Inertia does not support `resetOnError`, `resetOnSuccess`, or `setDefaultsOnSuccess` on the `<Form>` component. Using these props will cause errors. Upgrade to Inertia v2.2.0+ to use these features.
|
||||
@endif
|
||||
|
||||
Forms can also be built using the `useForm` composable for more programmatic control. Use the `search-docs` tool with a query of `useForm helper` for guidance.
|
||||
|
||||
@endif
|
||||
|
||||
### `useForm` Composable
|
||||
|
||||
@if($assist->inertia()->hasFormComponent() === false)
|
||||
For Inertia v2.0.x: Build forms using the `useForm` composable as the `<Form>` component is not available until v2.1.0+.
|
||||
@else
|
||||
For more programmatic control or to follow existing conventions, use the `useForm` composable:
|
||||
@endif
|
||||
|
||||
@verbatim
|
||||
@boostsnippet("useForm Composable Example", "vue")
|
||||
<script setup>
|
||||
import { useForm } from '@inertiajs/vue3'
|
||||
|
||||
const form = useForm({
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
})
|
||||
|
||||
function submit() {
|
||||
form.post('/users', {
|
||||
onSuccess: () => form.reset('password'),
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form @submit.prevent="submit">
|
||||
<input type="text" v-model="form.name" />
|
||||
<div v-if="form.errors.name">{{ form.errors.name }}</div>
|
||||
|
||||
<input type="email" v-model="form.email" />
|
||||
<div v-if="form.errors.email">{{ form.errors.email }}</div>
|
||||
|
||||
<input type="password" v-model="form.password" />
|
||||
<div v-if="form.errors.password">{{ form.errors.password }}</div>
|
||||
|
||||
<button type="submit" :disabled="form.processing">
|
||||
Create User
|
||||
</button>
|
||||
</form>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
@endverbatim
|
||||
|
||||
## Inertia v2 Features
|
||||
|
||||
### Deferred Props
|
||||
|
||||
Use deferred props to load data after initial page render:
|
||||
|
||||
@verbatim
|
||||
@boostsnippet("Deferred Props with Empty State", "vue")
|
||||
<script setup>
|
||||
defineProps({
|
||||
users: Array
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Users</h1>
|
||||
<div v-if="!users" class="animate-pulse">
|
||||
<div class="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
|
||||
<div class="h-4 bg-gray-200 rounded w-1/2"></div>
|
||||
</div>
|
||||
<ul v-else>
|
||||
<li v-for="user in users" :key="user.id">
|
||||
{{ user.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
@endverbatim
|
||||
|
||||
### Polling
|
||||
|
||||
Use the `usePoll` composable to automatically refresh data at intervals. It handles cleanup on unmount and throttles polling when the tab is inactive.
|
||||
|
||||
@boostsnippet("Basic Polling", "vue")
|
||||
<script setup>
|
||||
import { usePoll } from '@inertiajs/vue3'
|
||||
|
||||
defineProps({
|
||||
stats: Object
|
||||
})
|
||||
|
||||
usePoll(5000)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {{ stats.activeUsers }}</div>
|
||||
</div>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
|
||||
@boostsnippet("Polling With Request Options and Manual Control", "vue")
|
||||
<script setup>
|
||||
import { usePoll } from '@inertiajs/vue3'
|
||||
|
||||
defineProps({
|
||||
stats: Object
|
||||
})
|
||||
|
||||
const { start, stop } = usePoll(5000, {
|
||||
only: ['stats'],
|
||||
onStart() {
|
||||
console.log('Polling request started')
|
||||
},
|
||||
onFinish() {
|
||||
console.log('Polling request finished')
|
||||
},
|
||||
}, {
|
||||
autoStart: false,
|
||||
keepAlive: true,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div>Active Users: {{ stats.activeUsers }}</div>
|
||||
<button @click="start">Start Polling</button>
|
||||
<button @click="stop">Stop Polling</button>
|
||||
</div>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
|
||||
- `autoStart` (default `true`) — set to `false` to start polling manually via the returned `start()` function
|
||||
- `keepAlive` (default `false`) — set to `true` to prevent throttling when the browser tab is inactive
|
||||
|
||||
### WhenVisible (Infinite Scroll)
|
||||
|
||||
Load more data when user scrolls to a specific element:
|
||||
|
||||
@verbatim
|
||||
@boostsnippet("Infinite Scroll with WhenVisible", "vue")
|
||||
<script setup>
|
||||
import { WhenVisible } from '@inertiajs/vue3'
|
||||
|
||||
defineProps({
|
||||
users: Object
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="user in users.data" :key="user.id">
|
||||
{{ user.name }}
|
||||
</div>
|
||||
|
||||
<WhenVisible
|
||||
v-if="users.next_page_url"
|
||||
data="users"
|
||||
:params="{ page: users.current_page + 1 }"
|
||||
>
|
||||
<template #fallback>
|
||||
<div>Loading more...</div>
|
||||
</template>
|
||||
</WhenVisible>
|
||||
</div>
|
||||
</template>
|
||||
@endboostsnippet
|
||||
@endverbatim
|
||||
|
||||
## Server-Side Patterns
|
||||
|
||||
Server-side patterns (Inertia::render, props, middleware) are covered in inertia-laravel guidelines.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using traditional `<a>` links instead of Inertia's `<Link>` component (breaks SPA behavior)
|
||||
- Forgetting that Vue components must have a single root element
|
||||
- Forgetting to add loading states (skeleton screens) when using deferred props
|
||||
- Not handling the `undefined` state of deferred props before data loads
|
||||
- Using `<form>` without preventing default submission (use `<Form>` component or `@submit.prevent`)
|
||||
- Forgetting to check if `<Form>` component is available in your Inertia version
|
||||
4
vendor/laravel/boost/.ai/inertia-vue/core.blade.php
vendored
Normal file
4
vendor/laravel/boost/.ai/inertia-vue/core.blade.php
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
# Inertia + Vue
|
||||
|
||||
Vue components must have a single root element.
|
||||
- IMPORTANT: Activate `inertia-vue-development` when working with Inertia Vue client-side patterns.
|
||||
42
vendor/laravel/boost/.ai/laravel/11/core.blade.php
vendored
Normal file
42
vendor/laravel/boost/.ai/laravel/11/core.blade.php
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Laravel 11
|
||||
@if($assist->hasMcpEnabled())
|
||||
- CRITICAL: ALWAYS use `search-docs` tool for version-specific Laravel documentation and updated code examples.
|
||||
@endif
|
||||
@if (file_exists(app_path('Http/Kernel.php')))
|
||||
- This project upgraded from Laravel 10 without migrating to the new streamlined Laravel 11 file structure.
|
||||
- This is perfectly fine and recommended by Laravel. Follow the existing structure from Laravel 10. We do not need to migrate to the Laravel 11 structure unless the user explicitly requests it.
|
||||
|
||||
## Laravel 10 Structure
|
||||
- Middleware typically lives in `{{ $assist->appPath('Http/Middleware/') }}` and service providers in `{{ $assist->appPath('Providers/') }}`.
|
||||
- There is no `bootstrap/app.php` application configuration in a Laravel 10 structure:
|
||||
- Middleware registration is in `{{ $assist->appPath('Http/Kernel.php') }}`
|
||||
- Exception handling is in `{{ $assist->appPath('Exceptions/Handler.php') }}`
|
||||
- Console commands and schedule registration is in `{{ $assist->appPath('Console/Kernel.php') }}`
|
||||
- Rate limits likely exist in `RouteServiceProvider` or `{{ $assist->appPath('Http/Kernel.php') }}`
|
||||
@else
|
||||
- Laravel 11 brought a new streamlined file structure which this project now uses.
|
||||
|
||||
## Laravel 11 Structure
|
||||
- In Laravel 11, middleware are no longer registered in `{{ $assist->appPath('Http/Kernel.php') }}`.
|
||||
- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`.
|
||||
- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files.
|
||||
- `bootstrap/providers.php` contains application specific service providers.
|
||||
- No app\Console\Kernel.php - use `bootstrap/app.php` or `routes/console.php` for console configuration.
|
||||
- Commands auto-register - files in `{{ $assist->appPath('Console/Commands/') }}` are automatically available and do not require manual registration.
|
||||
@endif
|
||||
|
||||
## Database
|
||||
- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.
|
||||
- Laravel 11 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
|
||||
|
||||
### Models
|
||||
- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.
|
||||
|
||||
## New Artisan Commands
|
||||
- List Artisan commands using Boost's MCP tool, if available. New commands available in Laravel 11:
|
||||
- `{{ $assist->artisanCommand('make:enum') }}`
|
||||
- `{{ $assist->artisanCommand('make:class') }}`
|
||||
- `{{ $assist->artisanCommand('make:interface') }}`
|
||||
33
vendor/laravel/boost/.ai/laravel/12/core.blade.php
vendored
Normal file
33
vendor/laravel/boost/.ai/laravel/12/core.blade.php
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Laravel 12
|
||||
@if($assist->hasMcpEnabled())
|
||||
- CRITICAL: ALWAYS use `search-docs` tool for version-specific Laravel documentation and updated code examples.
|
||||
@endif
|
||||
@if (file_exists(app_path('Http/Kernel.php')))
|
||||
- This project upgraded from Laravel 10 without migrating to the new streamlined Laravel file structure.
|
||||
- This is perfectly fine and recommended by Laravel. Follow the existing structure from Laravel 10. We do not need to migrate to the new Laravel structure unless the user explicitly requests it.
|
||||
|
||||
## Laravel 10 Structure
|
||||
- Middleware typically lives in `{{ $assist->appPath('Http/Middleware/') }}` and service providers in `{{ $assist->appPath('Providers/') }}`.
|
||||
- There is no `bootstrap/app.php` application configuration in a Laravel 10 structure:
|
||||
- Middleware registration happens in `{{ $assist->appPath('Http/Kernel.php') }}`
|
||||
- Exception handling is in `{{ $assist->appPath('Exceptions/Handler.php') }}`
|
||||
- Console commands and schedule register in `{{ $assist->appPath('Console/Kernel.php') }}`
|
||||
- Rate limits likely exist in `RouteServiceProvider` or `{{ $assist->appPath('Http/Kernel.php') }}`
|
||||
@else
|
||||
- Since Laravel 11, Laravel has a new streamlined file structure which this project uses.
|
||||
|
||||
## Laravel 12 Structure
|
||||
- In Laravel 12, middleware are no longer registered in `{{ $assist->appPath('Http/Kernel.php') }}`.
|
||||
- Middleware are configured declaratively in `bootstrap/app.php` using `Application::configure()->withMiddleware()`.
|
||||
- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files.
|
||||
- `bootstrap/providers.php` contains application specific service providers.
|
||||
- The `{{ $assist->appPath('Console/Kernel.php') }}` file no longer exists; use `bootstrap/app.php` or `routes/console.php` for console configuration.
|
||||
- Console commands in `{{ $assist->appPath('Console/Commands/') }}` are automatically available and do not require manual registration.
|
||||
@endif
|
||||
|
||||
## Database
|
||||
- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost.
|
||||
- Laravel 12 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`.
|
||||
|
||||
### Models
|
||||
- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models.
|
||||
25
vendor/laravel/boost/.ai/laravel/core.blade.php
vendored
Normal file
25
vendor/laravel/boost/.ai/laravel/core.blade.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Do Things the Laravel Way
|
||||
|
||||
- Use `{{ $assist->artisanCommand('make:') }}` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using `{{ $assist->artisanCommand('list') }}` and check their parameters with `{{ $assist->artisanCommand('[command] --help') }}`.
|
||||
- If you're creating a generic PHP class, use `{{ $assist->artisanCommand('make:class') }}`.
|
||||
- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior.
|
||||
|
||||
### Model Creation
|
||||
- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `{{ $assist->artisanCommand('make:model --help') }}` to check the available options.
|
||||
|
||||
## APIs & Eloquent Resources
|
||||
- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention.
|
||||
|
||||
## URL Generation
|
||||
- When generating links to other pages, prefer named routes and the `route()` function.
|
||||
|
||||
## Testing
|
||||
- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model.
|
||||
- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`.
|
||||
- When creating tests, make use of `{{ $assist->artisanCommand('make:test [options] {name}') }}` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests.
|
||||
|
||||
## Vite Error
|
||||
- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `{{ $assist->nodePackageManagerCommand('run build') }}` or ask the user to run `{{ $assist->nodePackageManagerCommand('run dev') }}` or `{{ $assist->composerCommand('run dev') }}`.
|
||||
190
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/SKILL.md
vendored
Normal file
190
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/SKILL.md
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
---
|
||||
name: laravel-best-practices
|
||||
description: "Apply this skill whenever writing, reviewing, or refactoring Laravel PHP code. This includes creating or modifying controllers, models, migrations, form requests, policies, jobs, scheduled commands, service classes, and Eloquent queries. Triggers for N+1 and query performance issues, caching strategies, authorization and security patterns, validation, error handling, queue and job configuration, route definitions, and architectural decisions. Also use for Laravel code reviews and refactoring existing Laravel code to follow best practices. Covers any task involving Laravel backend PHP code patterns."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
|
||||
# Laravel Best Practices
|
||||
|
||||
Best practices for Laravel, prioritized by impact. Each rule teaches what to do and why. For exact API syntax, verify with `search-docs`.
|
||||
|
||||
## Consistency First
|
||||
|
||||
Before applying any rule, check what the application already does. Laravel offers multiple valid approaches — the best choice is the one the codebase already uses, even if another pattern would be theoretically better. Inconsistency is worse than a suboptimal pattern.
|
||||
|
||||
Check sibling files, related controllers, models, or tests for established patterns. If one exists, follow it — don't introduce a second way. These rules are defaults for when no pattern exists yet, not overrides.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### 1. Database Performance → `rules/db-performance.md`
|
||||
|
||||
- Eager load with `with()` to prevent N+1 queries
|
||||
- Enable `Model::preventLazyLoading()` in development
|
||||
- Select only needed columns, avoid `SELECT *`
|
||||
- `chunk()` / `chunkById()` for large datasets
|
||||
- Index columns used in `WHERE`, `ORDER BY`, `JOIN`
|
||||
- `withCount()` instead of loading relations to count
|
||||
- `cursor()` for memory-efficient read-only iteration
|
||||
- Never query in Blade templates
|
||||
|
||||
### 2. Advanced Query Patterns → `rules/advanced-queries.md`
|
||||
|
||||
- `addSelect()` subqueries over eager-loading entire has-many for a single value
|
||||
- Dynamic relationships via subquery FK + `belongsTo`
|
||||
- Conditional aggregates (`CASE WHEN` in `selectRaw`) over multiple count queries
|
||||
- `setRelation()` to prevent circular N+1 queries
|
||||
- `whereIn` + `pluck()` over `whereHas` for better index usage
|
||||
- Two simple queries can beat one complex query
|
||||
- Compound indexes matching `orderBy` column order
|
||||
- Correlated subqueries in `orderBy` for has-many sorting (avoid joins)
|
||||
|
||||
### 3. Security → `rules/security.md`
|
||||
|
||||
- Define `$fillable` or `$guarded` on every model, authorize every action via policies or gates
|
||||
- No raw SQL with user input — use Eloquent or query builder
|
||||
- `{{ }}` for output escaping, `@csrf` on all POST/PUT/DELETE forms, `throttle` on auth and API routes
|
||||
- Validate MIME type, extension, and size for file uploads
|
||||
- Never commit `.env`, use `config()` for secrets, `encrypted` cast for sensitive DB fields
|
||||
|
||||
### 4. Caching → `rules/caching.md`
|
||||
|
||||
- `Cache::remember()` over manual get/put
|
||||
- `Cache::flexible()` for stale-while-revalidate on high-traffic data
|
||||
- `Cache::memo()` to avoid redundant cache hits within a request
|
||||
- Cache tags to invalidate related groups
|
||||
- `Cache::add()` for atomic conditional writes
|
||||
- `once()` to memoize per-request or per-object lifetime
|
||||
- `Cache::lock()` / `lockForUpdate()` for race conditions
|
||||
- Failover cache stores in production
|
||||
|
||||
### 5. Eloquent Patterns → `rules/eloquent.md`
|
||||
|
||||
- Correct relationship types with return type hints
|
||||
- Local scopes for reusable query constraints
|
||||
- Global scopes sparingly — document their existence
|
||||
- Attribute casts in the `casts()` method
|
||||
- Cast date columns, use Carbon instances in templates
|
||||
- `whereBelongsTo($model)` for cleaner queries
|
||||
- Never hardcode table names — use `(new Model)->getTable()` or Eloquent queries
|
||||
|
||||
### 6. Validation & Forms → `rules/validation.md`
|
||||
|
||||
- Form Request classes, not inline validation
|
||||
- Array notation `['required', 'email']` for new code; follow existing convention
|
||||
- `$request->validated()` only — never `$request->all()`
|
||||
- `Rule::when()` for conditional validation
|
||||
- `after()` instead of `withValidator()`
|
||||
|
||||
### 7. Configuration → `rules/config.md`
|
||||
|
||||
- `env()` only inside config files
|
||||
- `App::environment()` or `app()->isProduction()`
|
||||
- Config, lang files, and constants over hardcoded text
|
||||
|
||||
### 8. Testing Patterns → `rules/testing.md`
|
||||
|
||||
- `LazilyRefreshDatabase` over `RefreshDatabase` for speed
|
||||
- `assertModelExists()` over raw `assertDatabaseHas()`
|
||||
- Factory states and sequences over manual overrides
|
||||
- Use fakes (`Event::fake()`, `Exceptions::fake()`, etc.) — but always after factory setup, not before
|
||||
- `recycle()` to share relationship instances across factories
|
||||
|
||||
### 9. Queue & Job Patterns → `rules/queue-jobs.md`
|
||||
|
||||
- `retry_after` must exceed job `timeout`; use exponential backoff `[1, 5, 10]`
|
||||
- `ShouldBeUnique` to prevent duplicates; `ShouldBeUniqueUntilProcessing` for early lock release
|
||||
- Always implement `failed()`; with `retryUntil()`, set `$tries = 0`
|
||||
- `RateLimited` middleware for external API calls; `Bus::batch()` for related jobs
|
||||
- Horizon for complex multi-queue scenarios
|
||||
|
||||
### 10. Routing & Controllers → `rules/routing.md`
|
||||
|
||||
- Implicit route model binding
|
||||
- Scoped bindings for nested resources
|
||||
- `Route::resource()` or `apiResource()`
|
||||
- Methods under 10 lines — extract to actions/services
|
||||
- Type-hint Form Requests for auto-validation
|
||||
|
||||
### 11. HTTP Client → `rules/http-client.md`
|
||||
|
||||
- Explicit `timeout` and `connectTimeout` on every request
|
||||
- `retry()` with exponential backoff for external APIs
|
||||
- Check response status or use `throw()`
|
||||
- `Http::pool()` for concurrent independent requests
|
||||
- `Http::fake()` and `preventStrayRequests()` in tests
|
||||
|
||||
### 12. Events, Notifications & Mail → `rules/events-notifications.md`, `rules/mail.md`
|
||||
|
||||
- Event discovery over manual registration; `event:cache` in production
|
||||
- `ShouldDispatchAfterCommit` / `afterCommit()` inside transactions
|
||||
- Queue notifications and mailables with `ShouldQueue`
|
||||
- On-demand notifications for non-user recipients
|
||||
- `HasLocalePreference` on notifiable models
|
||||
- `assertQueued()` not `assertSent()` for queued mailables
|
||||
- Markdown mailables for transactional emails
|
||||
|
||||
### 13. Error Handling → `rules/error-handling.md`
|
||||
|
||||
- `report()`/`render()` on exception classes or in `bootstrap/app.php` — follow existing pattern
|
||||
- `ShouldntReport` for exceptions that should never log
|
||||
- Throttle high-volume exceptions to protect log sinks
|
||||
- `dontReportDuplicates()` for multi-catch scenarios
|
||||
- Force JSON rendering for API routes
|
||||
- Structured context via `context()` on exception classes
|
||||
|
||||
### 14. Task Scheduling → `rules/scheduling.md`
|
||||
|
||||
- `withoutOverlapping()` on variable-duration tasks
|
||||
- `onOneServer()` on multi-server deployments
|
||||
- `runInBackground()` for concurrent long tasks
|
||||
- `environments()` to restrict to appropriate environments
|
||||
- `takeUntilTimeout()` for time-bounded processing
|
||||
- Schedule groups for shared configuration
|
||||
|
||||
### 15. Architecture → `rules/architecture.md`
|
||||
|
||||
- Single-purpose Action classes; dependency injection over `app()` helper
|
||||
- Prefer official Laravel packages and follow conventions, don't override defaults
|
||||
- Default to `ORDER BY id DESC` or `created_at DESC`; `mb_*` for UTF-8 safety
|
||||
- `defer()` for post-response work; `Context` for request-scoped data; `Concurrency::run()` for parallel execution
|
||||
|
||||
### 16. Migrations → `rules/migrations.md`
|
||||
|
||||
- Generate migrations with `php artisan make:migration`
|
||||
- `constrained()` for foreign keys
|
||||
- Never modify migrations that have run in production
|
||||
- Add indexes in the migration, not as an afterthought
|
||||
- Mirror column defaults in model `$attributes`
|
||||
- Reversible `down()` by default; forward-fix migrations for intentionally irreversible changes
|
||||
- One concern per migration — never mix DDL and DML
|
||||
|
||||
### 17. Collections → `rules/collections.md`
|
||||
|
||||
- Higher-order messages for simple collection operations
|
||||
- `cursor()` vs. `lazy()` — choose based on relationship needs
|
||||
- `lazyById()` when updating records while iterating
|
||||
- `toQuery()` for bulk operations on collections
|
||||
|
||||
### 18. Blade & Views → `rules/blade-views.md`
|
||||
|
||||
- `$attributes->merge()` in component templates
|
||||
- Blade components over `@include`; `@pushOnce` for per-component scripts
|
||||
- View Composers for shared view data
|
||||
- `@aware` for deeply nested component props
|
||||
|
||||
### 19. Conventions & Style → `rules/style.md`
|
||||
|
||||
- Follow Laravel naming conventions for all entities
|
||||
- Prefer Laravel helpers (`Str`, `Arr`, `Number`, `Uri`, `Str::of()`, `$request->string()`) over raw PHP functions
|
||||
- No JS/CSS in Blade, no HTML in PHP classes
|
||||
- Code should be readable; comments only for config files
|
||||
|
||||
## How to Apply
|
||||
|
||||
Always use a sub-agent to read rule files and explore this skill's content.
|
||||
|
||||
1. Identify the file type and select relevant sections (e.g., migration → §16, controller → §1, §3, §5, §6, §10)
|
||||
2. Check sibling files for existing patterns — follow those first per Consistency First
|
||||
3. Verify API syntax with `search-docs` for the installed Laravel version
|
||||
106
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/advanced-queries.md
vendored
Normal file
106
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/advanced-queries.md
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
# Advanced Query Patterns
|
||||
|
||||
## Use `addSelect()` Subqueries for Single Values from Has-Many
|
||||
|
||||
Instead of eager-loading an entire has-many relationship for a single value (like the latest timestamp), use a correlated subquery via `addSelect()`. This pulls the value directly in the main SQL query — zero extra queries.
|
||||
|
||||
```php
|
||||
public function scopeWithLastLoginAt($query): void
|
||||
{
|
||||
$query->addSelect([
|
||||
'last_login_at' => Login::select('created_at')
|
||||
->whereColumn('user_id', 'users.id')
|
||||
->latest()
|
||||
->take(1),
|
||||
])->withCasts(['last_login_at' => 'datetime']);
|
||||
}
|
||||
```
|
||||
|
||||
## Create Dynamic Relationships via Subquery FK
|
||||
|
||||
Extend the `addSelect()` pattern to fetch a foreign key via subquery, then define a `belongsTo` relationship on that virtual attribute. This provides a fully-hydrated related model without loading the entire collection.
|
||||
|
||||
```php
|
||||
public function lastLogin(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Login::class);
|
||||
}
|
||||
|
||||
public function scopeWithLastLogin($query): void
|
||||
{
|
||||
$query->addSelect([
|
||||
'last_login_id' => Login::select('id')
|
||||
->whereColumn('user_id', 'users.id')
|
||||
->latest()
|
||||
->take(1),
|
||||
])->with('lastLogin');
|
||||
}
|
||||
```
|
||||
|
||||
## Use Conditional Aggregates Instead of Multiple Count Queries
|
||||
|
||||
Replace N separate `count()` queries with a single query using `CASE WHEN` inside `selectRaw()`. Use `toBase()` to skip model hydration when you only need scalar values.
|
||||
|
||||
```php
|
||||
$statuses = Feature::toBase()
|
||||
->selectRaw("count(case when status = 'Requested' then 1 end) as requested")
|
||||
->selectRaw("count(case when status = 'Planned' then 1 end) as planned")
|
||||
->selectRaw("count(case when status = 'Completed' then 1 end) as completed")
|
||||
->first();
|
||||
```
|
||||
|
||||
## Use `setRelation()` to Prevent Circular N+1
|
||||
|
||||
When a parent model is eager-loaded with its children, and the view also needs `$child->parent`, use `setRelation()` to inject the already-loaded parent rather than letting Eloquent fire N additional queries.
|
||||
|
||||
```php
|
||||
$feature->load('comments.user');
|
||||
$feature->comments->each->setRelation('feature', $feature);
|
||||
```
|
||||
|
||||
## Prefer `whereIn` + Subquery Over `whereHas`
|
||||
|
||||
`whereHas()` emits a correlated `EXISTS` subquery that re-executes per row. Using `whereIn()` with a `select('id')` subquery lets the database use an index lookup instead, without loading data into PHP memory.
|
||||
|
||||
Incorrect (correlated EXISTS re-executes per row):
|
||||
|
||||
```php
|
||||
$query->whereHas('company', fn ($q) => $q->where('name', 'like', $term));
|
||||
```
|
||||
|
||||
Correct (index-friendly subquery, no PHP memory overhead):
|
||||
|
||||
```php
|
||||
$query->whereIn('company_id', Company::where('name', 'like', $term)->select('id'));
|
||||
```
|
||||
|
||||
## Sometimes Two Simple Queries Beat One Complex Query
|
||||
|
||||
Running a small, targeted secondary query and passing its results via `whereIn` is often faster than a single complex correlated subquery or join. The additional round-trip is worthwhile when the secondary query is highly selective and uses its own index.
|
||||
|
||||
## Use Compound Indexes Matching `orderBy` Column Order
|
||||
|
||||
When ordering by multiple columns, create a single compound index in the same column order as the `ORDER BY` clause. Individual single-column indexes cannot combine for multi-column sorts — the database will filesort without a compound index.
|
||||
|
||||
```php
|
||||
// Migration
|
||||
$table->index(['last_name', 'first_name']);
|
||||
|
||||
// Query — column order must match the index
|
||||
User::query()->orderBy('last_name')->orderBy('first_name')->paginate();
|
||||
```
|
||||
|
||||
## Use Correlated Subqueries for Has-Many Ordering
|
||||
|
||||
When sorting by a value from a has-many relationship, avoid joins (they duplicate rows). Use a correlated subquery inside `orderBy()` instead, paired with an `addSelect` scope for eager loading.
|
||||
|
||||
```php
|
||||
public function scopeOrderByLastLogin($query): void
|
||||
{
|
||||
$query->orderByDesc(Login::select('created_at')
|
||||
->whereColumn('user_id', 'users.id')
|
||||
->latest()
|
||||
->take(1)
|
||||
);
|
||||
}
|
||||
```
|
||||
202
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/architecture.md
vendored
Normal file
202
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/architecture.md
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
# Architecture Best Practices
|
||||
|
||||
## Single-Purpose Action Classes
|
||||
|
||||
Extract discrete business operations into invokable Action classes.
|
||||
|
||||
```php
|
||||
class CreateOrderAction
|
||||
{
|
||||
public function __construct(private InventoryService $inventory) {}
|
||||
|
||||
public function execute(array $data): Order
|
||||
{
|
||||
$order = Order::create($data);
|
||||
$this->inventory->reserve($order);
|
||||
|
||||
return $order;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Use Dependency Injection
|
||||
|
||||
Always use constructor injection. Avoid `app()` or `resolve()` inside classes.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function store(StoreOrderRequest $request)
|
||||
{
|
||||
$service = app(OrderService::class);
|
||||
|
||||
return $service->create($request->validated());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
class OrderController extends Controller
|
||||
{
|
||||
public function __construct(private OrderService $service) {}
|
||||
|
||||
public function store(StoreOrderRequest $request)
|
||||
{
|
||||
return $this->service->create($request->validated());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Code to Interfaces
|
||||
|
||||
Depend on contracts at system boundaries (payment gateways, notification channels, external APIs) for testability and swappability.
|
||||
|
||||
Incorrect (concrete dependency):
|
||||
```php
|
||||
class OrderService
|
||||
{
|
||||
public function __construct(private StripeGateway $gateway) {}
|
||||
}
|
||||
```
|
||||
|
||||
Correct (interface dependency):
|
||||
```php
|
||||
interface PaymentGateway
|
||||
{
|
||||
public function charge(int $amount, string $customerId): PaymentResult;
|
||||
}
|
||||
|
||||
class OrderService
|
||||
{
|
||||
public function __construct(private PaymentGateway $gateway) {}
|
||||
}
|
||||
```
|
||||
|
||||
Bind in a service provider:
|
||||
|
||||
```php
|
||||
$this->app->bind(PaymentGateway::class, StripeGateway::class);
|
||||
```
|
||||
|
||||
## Default Sort by Descending
|
||||
|
||||
When no explicit order is specified, sort by `id` or `created_at` descending. Without an explicit `ORDER BY`, row order is undefined.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$posts = Post::paginate();
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
$posts = Post::latest()->paginate();
|
||||
```
|
||||
|
||||
## Use Atomic Locks for Race Conditions
|
||||
|
||||
Prevent race conditions with `Cache::lock()` or `lockForUpdate()`.
|
||||
|
||||
```php
|
||||
Cache::lock('order-processing-'.$order->id, 10)->block(5, function () use ($order) {
|
||||
$order->process();
|
||||
});
|
||||
|
||||
// Or at query level
|
||||
$product = Product::where('id', $id)->lockForUpdate()->first();
|
||||
```
|
||||
|
||||
## Use `mb_*` String Functions
|
||||
|
||||
When no Laravel helper exists, prefer `mb_strlen`, `mb_strtolower`, etc. for UTF-8 safety. Standard PHP string functions count bytes, not characters.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
strlen('José'); // 5 (bytes, not characters)
|
||||
strtolower('MÜNCHEN'); // 'mÜnchen' — fails on multibyte
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
mb_strlen('José'); // 4 (characters)
|
||||
mb_strtolower('MÜNCHEN'); // 'münchen'
|
||||
|
||||
// Prefer Laravel's Str helpers when available
|
||||
Str::length('José'); // 4
|
||||
Str::lower('MÜNCHEN'); // 'münchen'
|
||||
```
|
||||
|
||||
## Use `defer()` for Post-Response Work
|
||||
|
||||
For lightweight tasks that don't need to survive a crash (logging, analytics, cleanup), use `defer()` instead of dispatching a job. The callback runs after the HTTP response is sent — no queue overhead.
|
||||
|
||||
Incorrect (job overhead for trivial work):
|
||||
```php
|
||||
dispatch(new LogPageView($page));
|
||||
```
|
||||
|
||||
Correct (runs after response, same process):
|
||||
```php
|
||||
defer(fn () => PageView::create(['page_id' => $page->id, 'user_id' => auth()->id()]));
|
||||
```
|
||||
|
||||
Use jobs when the work must survive process crashes or needs retry logic. Use `defer()` for fire-and-forget work.
|
||||
|
||||
## Use `Context` for Request-Scoped Data
|
||||
|
||||
The `Context` facade passes data through the entire request lifecycle — middleware, controllers, jobs, logs — without passing arguments manually.
|
||||
|
||||
```php
|
||||
// In middleware
|
||||
Context::add('tenant_id', $request->header('X-Tenant-ID'));
|
||||
|
||||
// Anywhere later — controllers, jobs, log context
|
||||
$tenantId = Context::get('tenant_id');
|
||||
```
|
||||
|
||||
Context data automatically propagates to queued jobs and is included in log entries. Use `Context::addHidden()` for sensitive data that should be available in queued jobs but excluded from log context. If data must not leave the current process, do not store it in `Context`.
|
||||
|
||||
## Use `Concurrency::run()` for Parallel Execution
|
||||
|
||||
Run independent operations in parallel using child processes — no async libraries needed.
|
||||
|
||||
```php
|
||||
use Illuminate\Support\Facades\Concurrency;
|
||||
|
||||
[$users, $orders] = Concurrency::run([
|
||||
fn () => User::count(),
|
||||
fn () => Order::where('status', 'pending')->count(),
|
||||
]);
|
||||
```
|
||||
|
||||
Each closure runs in a separate process with full Laravel access. Use for independent database queries, API calls, or computations that would otherwise run sequentially.
|
||||
|
||||
## Convention Over Configuration
|
||||
|
||||
Follow Laravel conventions. Don't override defaults unnecessarily.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
class Customer extends Model
|
||||
{
|
||||
protected $table = 'Customer';
|
||||
protected $primaryKey = 'customer_id';
|
||||
|
||||
public function roles(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Role::class, 'role_customer', 'customer_id', 'role_id');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
class Customer extends Model
|
||||
{
|
||||
public function roles(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Role::class);
|
||||
}
|
||||
}
|
||||
```
|
||||
36
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/blade-views.md
vendored
Normal file
36
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/blade-views.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Blade & Views Best Practices
|
||||
|
||||
## Use `$attributes->merge()` in Component Templates
|
||||
|
||||
Hardcoding classes prevents consumers from adding their own. `merge()` combines class attributes cleanly.
|
||||
|
||||
```blade
|
||||
<div {{ $attributes->merge(['class' => 'alert alert-'.$type]) }}>
|
||||
{{ $message }}
|
||||
</div>
|
||||
```
|
||||
|
||||
## Use `@pushOnce` for Per-Component Scripts
|
||||
|
||||
If a component renders inside a `@foreach`, `@push` inserts the script N times. `@pushOnce` guarantees it's included exactly once.
|
||||
|
||||
## Prefer Blade Components Over `@include`
|
||||
|
||||
`@include` shares all parent variables implicitly (hidden coupling). Components have explicit props, attribute bags, and slots.
|
||||
|
||||
## Use View Composers for Shared View Data
|
||||
|
||||
If every controller rendering a sidebar must pass `$categories`, that's duplicated code. A View Composer centralizes it.
|
||||
|
||||
## Use Blade Fragments for Partial Re-Renders (htmx/Turbo)
|
||||
|
||||
A single view can return either the full page or just a fragment, keeping routing clean.
|
||||
|
||||
```php
|
||||
return view('dashboard', compact('users'))
|
||||
->fragmentIf($request->hasHeader('HX-Request'), 'user-list');
|
||||
```
|
||||
|
||||
## Use `@aware` for Deeply Nested Component Props
|
||||
|
||||
Avoids re-passing parent props through every level of nested components.
|
||||
70
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/caching.md
vendored
Normal file
70
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/caching.md
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
# Caching Best Practices
|
||||
|
||||
## Use `Cache::remember()` Instead of Manual Get/Put
|
||||
|
||||
Cleaner cache-aside pattern that removes boilerplate. use `Cache::lock()` for race conditions.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$val = Cache::get('stats');
|
||||
if (! $val) {
|
||||
$val = $this->computeStats();
|
||||
Cache::put('stats', $val, 60);
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
$val = Cache::remember('stats', 60, fn () => $this->computeStats());
|
||||
```
|
||||
|
||||
## Use `Cache::flexible()` for Stale-While-Revalidate
|
||||
|
||||
On high-traffic keys, one user always gets a slow response when the cache expires. `flexible()` serves slightly stale data while refreshing in the background.
|
||||
|
||||
Incorrect: `Cache::remember('users', 300, fn () => User::all());`
|
||||
|
||||
Correct: `Cache::flexible('users', [300, 600], fn () => User::all());` — fresh for 5 min, stale-but-served up to 10 min, refreshes via deferred function.
|
||||
|
||||
## Use `Cache::memo()` to Avoid Redundant Hits Within a Request
|
||||
|
||||
If the same cache key is read multiple times per request (e.g., a service called from multiple places), `memo()` stores the resolved value in memory.
|
||||
|
||||
`Cache::memo()->get('settings');` — 5 calls = 1 Redis round-trip instead of 5.
|
||||
|
||||
## Use Cache Tags to Invalidate Related Groups
|
||||
|
||||
Without tags, invalidating a group of entries requires tracking every key. Tags let you flush atomically. Only works with `redis`, `memcached`, `dynamodb` — not `file` or `database`.
|
||||
|
||||
```php
|
||||
Cache::tags(['user-1'])->flush();
|
||||
```
|
||||
|
||||
## Use `Cache::add()` for Atomic Conditional Writes
|
||||
|
||||
`add()` only writes if the key does not exist — atomic, no race condition between checking and writing.
|
||||
|
||||
Incorrect: `if (! Cache::has('lock')) { Cache::put('lock', true, 10); }`
|
||||
|
||||
Correct: `Cache::add('lock', true, 10);`
|
||||
|
||||
## Use `once()` for Per-Request Memoization
|
||||
|
||||
`once()` memoizes a function's return value for the lifetime of the object (or request for closures). Unlike `Cache::memo()`, it doesn't hit the cache store at all — pure in-memory.
|
||||
|
||||
```php
|
||||
public function roles(): Collection
|
||||
{
|
||||
return once(fn () => $this->loadRoles());
|
||||
}
|
||||
```
|
||||
|
||||
Multiple calls return the cached result without re-executing. Use `once()` for expensive computations called multiple times per request. Use `Cache::memo()` when you also want cross-request caching.
|
||||
|
||||
## Configure Failover Cache Stores in Production
|
||||
|
||||
If Redis goes down, the app falls back to a secondary store automatically.
|
||||
|
||||
```php
|
||||
'failover' => ['driver' => 'failover', 'stores' => ['redis', 'database']],
|
||||
```
|
||||
44
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/collections.md
vendored
Normal file
44
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/collections.md
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
# Collection Best Practices
|
||||
|
||||
## Use Higher-Order Messages for Simple Operations
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$users->each(function (User $user) {
|
||||
$user->markAsVip();
|
||||
});
|
||||
```
|
||||
|
||||
Correct: `$users->each->markAsVip();`
|
||||
|
||||
Works with `each`, `map`, `sum`, `filter`, `reject`, `contains`, etc.
|
||||
|
||||
## Choose `cursor()` vs. `lazy()` Correctly
|
||||
|
||||
- `cursor()` — one model in memory, but cannot eager-load relationships (N+1 risk).
|
||||
- `lazy()` — chunked pagination returning a flat LazyCollection, supports eager loading.
|
||||
|
||||
Incorrect: `User::with('roles')->cursor()` — eager loading silently ignored.
|
||||
|
||||
Correct: `User::with('roles')->lazy()` for relationship access; `User::cursor()` for attribute-only work.
|
||||
|
||||
## Use `lazyById()` When Updating Records While Iterating
|
||||
|
||||
`lazy()` uses offset pagination — updating records during iteration can skip or double-process. `lazyById()` uses `id > last_id`, safe against mutation.
|
||||
|
||||
## Use `toQuery()` for Bulk Operations on Collections
|
||||
|
||||
Avoids manual `whereIn` construction.
|
||||
|
||||
Incorrect: `User::whereIn('id', $users->pluck('id'))->update([...]);`
|
||||
|
||||
Correct: `$users->toQuery()->update([...]);`
|
||||
|
||||
## Use `#[CollectedBy]` for Custom Collection Classes
|
||||
|
||||
More declarative than overriding `newCollection()`.
|
||||
|
||||
```php
|
||||
#[CollectedBy(UserCollection::class)]
|
||||
class User extends Model {}
|
||||
```
|
||||
71
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/config.md
vendored
Normal file
71
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/config.md
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
# Configuration Best Practices
|
||||
|
||||
## `env()` Only in Config Files
|
||||
|
||||
Direct `env()` calls may return `null` when config is cached.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$key = env('API_KEY');
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
// config/services.php
|
||||
'key' => env('API_KEY'),
|
||||
|
||||
// Application code
|
||||
$key = config('services.key');
|
||||
```
|
||||
|
||||
## Use Encrypted Env or External Secrets
|
||||
|
||||
Never store production secrets in plain `.env` files in version control.
|
||||
|
||||
Incorrect:
|
||||
```bash
|
||||
# .env committed to repo or shared in Slack
|
||||
STRIPE_SECRET=sk_live_abc123
|
||||
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI
|
||||
```
|
||||
|
||||
Correct:
|
||||
```bash
|
||||
php artisan env:encrypt --env=production --readable
|
||||
php artisan env:decrypt --env=production
|
||||
```
|
||||
|
||||
For cloud deployments, prefer the platform's native secret store (AWS Secrets Manager, Vault, etc.) and inject at runtime.
|
||||
|
||||
## Use `App::environment()` for Environment Checks
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
if (env('APP_ENV') === 'production') {
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
if (app()->isProduction()) {
|
||||
// or
|
||||
if (App::environment('production')) {
|
||||
```
|
||||
|
||||
## Use Constants and Language Files
|
||||
|
||||
Use class constants instead of hardcoded magic strings for model states, types, and statuses.
|
||||
|
||||
```php
|
||||
// Incorrect
|
||||
return $this->type === 'normal';
|
||||
|
||||
// Correct
|
||||
return $this->type === self::TYPE_NORMAL;
|
||||
```
|
||||
|
||||
If the application already uses language files for localization, use `__()` for user-facing strings too. Do not introduce language files purely for English-only apps — simple string literals are fine there.
|
||||
|
||||
```php
|
||||
// Only when lang files already exist in the project
|
||||
return back()->with('message', __('app.article_added'));
|
||||
```
|
||||
192
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/db-performance.md
vendored
Normal file
192
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/db-performance.md
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
# Database Performance Best Practices
|
||||
|
||||
## Always Eager Load Relationships
|
||||
|
||||
Lazy loading causes N+1 query problems — one query per loop iteration. Always use `with()` to load relationships upfront.
|
||||
|
||||
Incorrect (N+1 — executes 1 + N queries):
|
||||
```php
|
||||
$posts = Post::all();
|
||||
foreach ($posts as $post) {
|
||||
echo $post->author->name;
|
||||
}
|
||||
```
|
||||
|
||||
Correct (2 queries total):
|
||||
```php
|
||||
$posts = Post::with('author')->get();
|
||||
foreach ($posts as $post) {
|
||||
echo $post->author->name;
|
||||
}
|
||||
```
|
||||
|
||||
Constrain eager loads to select only needed columns (always include the foreign key):
|
||||
|
||||
```php
|
||||
$users = User::with(['posts' => function ($query) {
|
||||
$query->select('id', 'user_id', 'title')
|
||||
->where('published', true)
|
||||
->latest()
|
||||
->limit(10);
|
||||
}])->get();
|
||||
```
|
||||
|
||||
## Prevent Lazy Loading in Development
|
||||
|
||||
Enable this in `AppServiceProvider::boot()` to catch N+1 issues during development.
|
||||
|
||||
```php
|
||||
public function boot(): void
|
||||
{
|
||||
Model::preventLazyLoading(! app()->isProduction());
|
||||
}
|
||||
```
|
||||
|
||||
Throws `LazyLoadingViolationException` when a relationship is accessed without being eager-loaded.
|
||||
|
||||
## Select Only Needed Columns
|
||||
|
||||
Avoid `SELECT *` — especially when tables have large text or JSON columns.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$posts = Post::with('author')->get();
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
$posts = Post::select('id', 'title', 'user_id', 'created_at')
|
||||
->with(['author:id,name,avatar'])
|
||||
->get();
|
||||
```
|
||||
|
||||
When selecting columns on eager-loaded relationships, always include the foreign key column or the relationship won't match.
|
||||
|
||||
## Chunk Large Datasets
|
||||
|
||||
Never load thousands of records at once. Use chunking for batch processing.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$users = User::all();
|
||||
foreach ($users as $user) {
|
||||
$user->notify(new WeeklyDigest);
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
User::where('subscribed', true)->chunk(200, function ($users) {
|
||||
foreach ($users as $user) {
|
||||
$user->notify(new WeeklyDigest);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Use `chunkById()` when modifying records during iteration — standard `chunk()` uses OFFSET which shifts when rows change:
|
||||
|
||||
```php
|
||||
User::where('active', false)->chunkById(200, function ($users) {
|
||||
$users->each->delete();
|
||||
});
|
||||
```
|
||||
|
||||
## Add Database Indexes
|
||||
|
||||
Index columns that appear in `WHERE`, `ORDER BY`, `JOIN`, and `GROUP BY` clauses.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
Schema::create('orders', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained();
|
||||
$table->string('status');
|
||||
$table->timestamps();
|
||||
});
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
Schema::create('orders', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->index()->constrained();
|
||||
$table->string('status')->index();
|
||||
$table->timestamps();
|
||||
$table->index(['status', 'created_at']);
|
||||
});
|
||||
```
|
||||
|
||||
Add composite indexes for common query patterns (e.g., `WHERE status = ? ORDER BY created_at`).
|
||||
|
||||
## Use `withCount()` for Counting Relations
|
||||
|
||||
Never load entire collections just to count them.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$posts = Post::all();
|
||||
foreach ($posts as $post) {
|
||||
echo $post->comments->count();
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
$posts = Post::withCount('comments')->get();
|
||||
foreach ($posts as $post) {
|
||||
echo $post->comments_count;
|
||||
}
|
||||
```
|
||||
|
||||
Conditional counting:
|
||||
|
||||
```php
|
||||
$posts = Post::withCount([
|
||||
'comments',
|
||||
'comments as approved_comments_count' => function ($query) {
|
||||
$query->where('approved', true);
|
||||
},
|
||||
])->get();
|
||||
```
|
||||
|
||||
## Use `cursor()` for Memory-Efficient Iteration
|
||||
|
||||
For read-only iteration over large result sets, `cursor()` loads one record at a time via a PHP generator.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$users = User::where('active', true)->get();
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
foreach (User::where('active', true)->cursor() as $user) {
|
||||
ProcessUser::dispatch($user->id);
|
||||
}
|
||||
```
|
||||
|
||||
Use `cursor()` for read-only iteration. Use `chunk()` / `chunkById()` when modifying records.
|
||||
|
||||
## No Queries in Blade Templates
|
||||
|
||||
Never execute queries in Blade templates. Pass data from controllers.
|
||||
|
||||
Incorrect:
|
||||
```blade
|
||||
@foreach (User::all() as $user)
|
||||
{{ $user->profile->name }}
|
||||
@endforeach
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
// Controller
|
||||
$users = User::with('profile')->get();
|
||||
return view('users.index', compact('users'));
|
||||
```
|
||||
|
||||
```blade
|
||||
@foreach ($users as $user)
|
||||
{{ $user->profile->name }}
|
||||
@endforeach
|
||||
```
|
||||
148
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/eloquent.md
vendored
Normal file
148
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/eloquent.md
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
# Eloquent Best Practices
|
||||
|
||||
## Use Correct Relationship Types
|
||||
|
||||
Use `hasMany`, `belongsTo`, `morphMany`, etc. with proper return type hints.
|
||||
|
||||
```php
|
||||
public function comments(): HasMany
|
||||
{
|
||||
return $this->hasMany(Comment::class);
|
||||
}
|
||||
|
||||
public function author(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
```
|
||||
|
||||
## Use Local Scopes for Reusable Queries
|
||||
|
||||
Extract reusable query constraints into local scopes to avoid duplication.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$active = User::where('verified', true)->whereNotNull('activated_at')->get();
|
||||
$articles = Article::whereHas('user', function ($q) {
|
||||
$q->where('verified', true)->whereNotNull('activated_at');
|
||||
})->get();
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
public function scopeActive(Builder $query): Builder
|
||||
{
|
||||
return $query->where('verified', true)->whereNotNull('activated_at');
|
||||
}
|
||||
|
||||
// Usage
|
||||
$active = User::active()->get();
|
||||
$articles = Article::whereHas('user', fn ($q) => $q->active())->get();
|
||||
```
|
||||
|
||||
## Apply Global Scopes Sparingly
|
||||
|
||||
Global scopes silently modify every query on the model, making debugging difficult. Prefer local scopes and reserve global scopes for truly universal constraints like soft deletes or multi-tenancy.
|
||||
|
||||
Incorrect (global scope for a conditional filter):
|
||||
```php
|
||||
class PublishedScope implements Scope
|
||||
{
|
||||
public function apply(Builder $builder, Model $model): void
|
||||
{
|
||||
$builder->where('published', true);
|
||||
}
|
||||
}
|
||||
// Now admin panels, reports, and background jobs all silently skip drafts
|
||||
```
|
||||
|
||||
Correct (local scope you opt into):
|
||||
```php
|
||||
public function scopePublished(Builder $query): Builder
|
||||
{
|
||||
return $query->where('published', true);
|
||||
}
|
||||
|
||||
Post::published()->paginate(); // Explicit
|
||||
Post::paginate(); // Admin sees all
|
||||
```
|
||||
|
||||
## Define Attribute Casts
|
||||
|
||||
Use the `casts()` method (or `$casts` property following project convention) for automatic type conversion.
|
||||
|
||||
```php
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'is_active' => 'boolean',
|
||||
'metadata' => 'array',
|
||||
'total' => 'decimal:2',
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
## Cast Date Columns Properly
|
||||
|
||||
Always cast date columns. Use Carbon instances in templates instead of formatting strings manually.
|
||||
|
||||
Incorrect:
|
||||
```blade
|
||||
{{ Carbon::createFromFormat('Y-d-m H-i', $order->ordered_at)->toDateString() }}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'ordered_at' => 'datetime',
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
```blade
|
||||
{{ $order->ordered_at->toDateString() }}
|
||||
{{ $order->ordered_at->format('m-d') }}
|
||||
```
|
||||
|
||||
## Use `whereBelongsTo()` for Relationship Queries
|
||||
|
||||
Cleaner than manually specifying foreign keys.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
Post::where('user_id', $user->id)->get();
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
Post::whereBelongsTo($user)->get();
|
||||
Post::whereBelongsTo($user, 'author')->get();
|
||||
```
|
||||
|
||||
## Avoid Hardcoded Table Names in Queries
|
||||
|
||||
Never use string literals for table names in raw queries, joins, or subqueries. Hardcoded table names make it impossible to find all places a model is used and break refactoring (e.g., renaming a table requires hunting through every raw string).
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
DB::table('users')->where('active', true)->get();
|
||||
|
||||
$query->join('companies', 'companies.id', '=', 'users.company_id');
|
||||
|
||||
DB::select('SELECT * FROM orders WHERE status = ?', ['pending']);
|
||||
```
|
||||
|
||||
Correct — reference the model's table:
|
||||
```php
|
||||
DB::table((new User)->getTable())->where('active', true)->get();
|
||||
|
||||
// Even better — use Eloquent or the query builder instead of raw SQL
|
||||
User::where('active', true)->get();
|
||||
Order::where('status', 'pending')->get();
|
||||
```
|
||||
|
||||
Prefer Eloquent queries and relationships over `DB::table()` whenever possible — they already reference the model's table. When `DB::table()` or raw joins are unavoidable, always use `(new Model)->getTable()` to keep the reference traceable.
|
||||
|
||||
**Exception — migrations:** In migrations, hardcoded table names via `DB::table('settings')` are acceptable and preferred. Models change over time but migrations are frozen snapshots — referencing a model that is later renamed or deleted would break the migration.
|
||||
72
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/error-handling.md
vendored
Normal file
72
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/error-handling.md
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
# Error Handling Best Practices
|
||||
|
||||
## Exception Reporting and Rendering
|
||||
|
||||
There are two valid approaches — choose one and apply it consistently across the project.
|
||||
|
||||
**Co-location on the exception class** — keeps behavior alongside the exception definition, easier to find:
|
||||
|
||||
```php
|
||||
class InvalidOrderException extends Exception
|
||||
{
|
||||
public function report(): void { /* custom reporting */ }
|
||||
|
||||
public function render(Request $request): Response
|
||||
{
|
||||
return response()->view('errors.invalid-order', status: 422);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Centralized in `bootstrap/app.php`** — all exception handling in one place, easier to see the full picture:
|
||||
|
||||
```php
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
$exceptions->report(function (InvalidOrderException $e) { /* ... */ });
|
||||
$exceptions->render(function (InvalidOrderException $e, Request $request) {
|
||||
return response()->view('errors.invalid-order', status: 422);
|
||||
});
|
||||
})
|
||||
```
|
||||
|
||||
Check the existing codebase and follow whichever pattern is already established.
|
||||
|
||||
## Use `ShouldntReport` for Exceptions That Should Never Log
|
||||
|
||||
More discoverable than listing classes in `dontReport()`.
|
||||
|
||||
```php
|
||||
class PodcastProcessingException extends Exception implements ShouldntReport {}
|
||||
```
|
||||
|
||||
## Throttle High-Volume Exceptions
|
||||
|
||||
A single failing integration can flood error tracking. Use `throttle()` to rate-limit per exception type.
|
||||
|
||||
## Enable `dontReportDuplicates()`
|
||||
|
||||
Prevents the same exception instance from being logged multiple times when `report($e)` is called in multiple catch blocks.
|
||||
|
||||
## Force JSON Error Rendering for API Routes
|
||||
|
||||
Laravel auto-detects `Accept: application/json` but API clients may not set it. Explicitly declare JSON rendering for API routes.
|
||||
|
||||
```php
|
||||
$exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
|
||||
return $request->is('api/*') || $request->expectsJson();
|
||||
});
|
||||
```
|
||||
|
||||
## Add Context to Exception Classes
|
||||
|
||||
Attach structured data to exceptions at the source via a `context()` method — Laravel includes it automatically in the log entry.
|
||||
|
||||
```php
|
||||
class InvalidOrderException extends Exception
|
||||
{
|
||||
public function context(): array
|
||||
{
|
||||
return ['order_id' => $this->orderId];
|
||||
}
|
||||
}
|
||||
```
|
||||
52
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/events-notifications.md
vendored
Normal file
52
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/events-notifications.md
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# Events & Notifications Best Practices
|
||||
|
||||
## Rely on Event Discovery
|
||||
|
||||
Laravel auto-discovers listeners by reading `handle(EventType $event)` type-hints. No manual registration needed in `AppServiceProvider`.
|
||||
|
||||
## Run `event:cache` in Production Deploy
|
||||
|
||||
Event discovery scans the filesystem per-request in dev. Cache it in production: `php artisan optimize` or `php artisan event:cache`.
|
||||
|
||||
## Use `ShouldDispatchAfterCommit` Inside Transactions
|
||||
|
||||
Without it, a queued listener may process before the DB transaction commits, reading data that doesn't exist yet.
|
||||
|
||||
```php
|
||||
class OrderShipped implements ShouldDispatchAfterCommit {}
|
||||
```
|
||||
|
||||
## Always Queue Notifications
|
||||
|
||||
Notifications often hit external APIs (email, SMS, Slack). Without `ShouldQueue`, they block the HTTP response.
|
||||
|
||||
```php
|
||||
class InvoicePaid extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
}
|
||||
```
|
||||
|
||||
## Use `afterCommit()` on Notifications in Transactions
|
||||
|
||||
Same race condition as events — call `afterCommit()` to delay dispatch until the transaction commits.
|
||||
|
||||
```php
|
||||
$user->notify((new InvoicePaid($invoice))->afterCommit());
|
||||
```
|
||||
|
||||
## Route Notification Channels to Dedicated Queues
|
||||
|
||||
Mail and database notifications have different priorities. Use `viaQueues()` to route them to separate queues.
|
||||
|
||||
## Use On-Demand Notifications for Non-User Recipients
|
||||
|
||||
Avoid creating dummy models to send notifications to arbitrary addresses.
|
||||
|
||||
```php
|
||||
Notification::route('mail', 'admin@example.com')->notify(new SystemAlert());
|
||||
```
|
||||
|
||||
## Implement `HasLocalePreference` on Notifiable Models
|
||||
|
||||
Laravel automatically uses the user's preferred locale for all notifications and mailables — no per-call `locale()` needed.
|
||||
160
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/http-client.md
vendored
Normal file
160
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/http-client.md
vendored
Normal file
@@ -0,0 +1,160 @@
|
||||
# HTTP Client Best Practices
|
||||
|
||||
## Always Set Explicit Timeouts
|
||||
|
||||
The default timeout is 30 seconds — too long for most API calls. Always set explicit `timeout` and `connectTimeout` to fail fast.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$response = Http::get('https://api.example.com/users');
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
$response = Http::timeout(5)
|
||||
->connectTimeout(3)
|
||||
->get('https://api.example.com/users');
|
||||
```
|
||||
|
||||
For service-specific clients, define timeouts in a macro:
|
||||
|
||||
```php
|
||||
Http::macro('github', function () {
|
||||
return Http::baseUrl('https://api.github.com')
|
||||
->timeout(10)
|
||||
->connectTimeout(3)
|
||||
->withToken(config('services.github.token'));
|
||||
});
|
||||
|
||||
$response = Http::github()->get('/repos/laravel/framework');
|
||||
```
|
||||
|
||||
## Use Retry with Backoff for External APIs
|
||||
|
||||
External APIs have transient failures. Use `retry()` with increasing delays.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$response = Http::post('https://api.stripe.com/v1/charges', $data);
|
||||
|
||||
if ($response->failed()) {
|
||||
throw new PaymentFailedException('Charge failed');
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
$response = Http::retry([100, 500, 1000])
|
||||
->timeout(10)
|
||||
->post('https://api.stripe.com/v1/charges', $data);
|
||||
```
|
||||
|
||||
Only retry on specific errors:
|
||||
|
||||
```php
|
||||
$response = Http::retry(3, 100, function (Throwable $exception, PendingRequest $request) {
|
||||
return $exception instanceof ConnectionException
|
||||
|| ($exception instanceof RequestException && $exception->response->serverError());
|
||||
})->post('https://api.example.com/data');
|
||||
```
|
||||
|
||||
## Handle Errors Explicitly
|
||||
|
||||
The HTTP Client does not throw on 4xx/5xx by default. Always check status or use `throw()`.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$response = Http::get('https://api.example.com/users/1');
|
||||
$user = $response->json(); // Could be an error body
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
$response = Http::timeout(5)
|
||||
->get('https://api.example.com/users/1')
|
||||
->throw();
|
||||
|
||||
$user = $response->json();
|
||||
```
|
||||
|
||||
For graceful degradation:
|
||||
|
||||
```php
|
||||
$response = Http::get('https://api.example.com/users/1');
|
||||
|
||||
if ($response->successful()) {
|
||||
return $response->json();
|
||||
}
|
||||
|
||||
if ($response->notFound()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$response->throw();
|
||||
```
|
||||
|
||||
## Use Request Pooling for Concurrent Requests
|
||||
|
||||
When making multiple independent API calls, use `Http::pool()` instead of sequential calls.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$users = Http::get('https://api.example.com/users')->json();
|
||||
$posts = Http::get('https://api.example.com/posts')->json();
|
||||
$comments = Http::get('https://api.example.com/comments')->json();
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
use Illuminate\Http\Client\Pool;
|
||||
|
||||
$responses = Http::pool(fn (Pool $pool) => [
|
||||
$pool->as('users')->get('https://api.example.com/users'),
|
||||
$pool->as('posts')->get('https://api.example.com/posts'),
|
||||
$pool->as('comments')->get('https://api.example.com/comments'),
|
||||
]);
|
||||
|
||||
$users = $responses['users']->json();
|
||||
$posts = $responses['posts']->json();
|
||||
```
|
||||
|
||||
## Fake HTTP Calls in Tests
|
||||
|
||||
Never make real HTTP requests in tests. Use `Http::fake()` and `preventStrayRequests()`.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
it('syncs user from API', function () {
|
||||
$service = new UserSyncService;
|
||||
$service->sync(1); // Hits the real API
|
||||
});
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
it('syncs user from API', function () {
|
||||
Http::preventStrayRequests();
|
||||
|
||||
Http::fake([
|
||||
'api.example.com/users/1' => Http::response([
|
||||
'name' => 'John Doe',
|
||||
'email' => 'john@example.com',
|
||||
]),
|
||||
]);
|
||||
|
||||
$service = new UserSyncService;
|
||||
$service->sync(1);
|
||||
|
||||
Http::assertSent(function (Request $request) {
|
||||
return $request->url() === 'https://api.example.com/users/1';
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Test failure scenarios too:
|
||||
|
||||
```php
|
||||
Http::fake([
|
||||
'api.example.com/*' => Http::failedConnection(),
|
||||
]);
|
||||
```
|
||||
27
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/mail.md
vendored
Normal file
27
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/mail.md
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Mail Best Practices
|
||||
|
||||
## Implement `ShouldQueue` on the Mailable Class
|
||||
|
||||
Makes queueing the default regardless of how the mailable is dispatched. No need to remember `Mail::queue()` at every call site — `Mail::send()` also queues it.
|
||||
|
||||
## Use `afterCommit()` on Mailables Inside Transactions
|
||||
|
||||
A queued mailable dispatched inside a transaction may process before the commit. Use `$this->afterCommit()` in the constructor.
|
||||
|
||||
## Use `assertQueued()` Not `assertSent()` for Queued Mailables
|
||||
|
||||
`Mail::assertSent()` only catches synchronous mail. Queued mailables fail `assertSent` with a "Did you mean to use assertQueued()?" hint.
|
||||
|
||||
Incorrect: `Mail::assertSent(OrderShipped::class);` when mailable implements `ShouldQueue`.
|
||||
|
||||
Correct: `Mail::assertQueued(OrderShipped::class);`
|
||||
|
||||
## Use Markdown Mailables for Transactional Emails
|
||||
|
||||
Markdown mailables auto-generate both HTML and plain-text versions, use responsive components, and allow global style customization. Generate with `--markdown` flag.
|
||||
|
||||
## Separate Content Tests from Sending Tests
|
||||
|
||||
Content tests: instantiate the mailable directly, call `assertSeeInHtml()`.
|
||||
Sending tests: use `Mail::fake()` and `assertSent()`/`assertQueued()`.
|
||||
Don't mix them — it conflates concerns and makes tests brittle.
|
||||
121
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/migrations.md
vendored
Normal file
121
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/migrations.md
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
# Migration Best Practices
|
||||
|
||||
## Generate Migrations with Artisan
|
||||
|
||||
Always use `php artisan make:migration` for consistent naming and timestamps.
|
||||
|
||||
Incorrect (manually created file):
|
||||
```php
|
||||
// database/migrations/posts_migration.php ← wrong naming, no timestamp
|
||||
```
|
||||
|
||||
Correct (Artisan-generated):
|
||||
```bash
|
||||
php artisan make:migration create_posts_table
|
||||
php artisan make:migration add_slug_to_posts_table
|
||||
```
|
||||
|
||||
## Use `constrained()` for Foreign Keys
|
||||
|
||||
Automatic naming and referential integrity.
|
||||
|
||||
```php
|
||||
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
|
||||
|
||||
// Non-standard names
|
||||
$table->foreignId('author_id')->constrained('users');
|
||||
```
|
||||
|
||||
## Never Modify Deployed Migrations
|
||||
|
||||
Once a migration has run in production, treat it as immutable. Create a new migration to change the table.
|
||||
|
||||
Incorrect (editing a deployed migration):
|
||||
```php
|
||||
// 2024_01_01_create_posts_table.php — already in production
|
||||
$table->string('slug')->unique(); // ← added after deployment
|
||||
```
|
||||
|
||||
Correct (new migration to alter):
|
||||
```php
|
||||
// 2024_03_15_add_slug_to_posts_table.php
|
||||
Schema::table('posts', function (Blueprint $table) {
|
||||
$table->string('slug')->unique()->after('title');
|
||||
});
|
||||
```
|
||||
|
||||
## Add Indexes in the Migration
|
||||
|
||||
Add indexes when creating the table, not as an afterthought. Columns used in `WHERE`, `ORDER BY`, and `JOIN` clauses need indexes.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
Schema::create('orders', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained();
|
||||
$table->string('status');
|
||||
$table->timestamps();
|
||||
});
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
Schema::create('orders', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->index();
|
||||
$table->string('status')->index();
|
||||
$table->timestamp('shipped_at')->nullable()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
```
|
||||
|
||||
## Mirror Defaults in Model `$attributes`
|
||||
|
||||
When a column has a database default, mirror it in the model so new instances have correct values before saving.
|
||||
|
||||
```php
|
||||
// Migration
|
||||
$table->string('status')->default('pending');
|
||||
|
||||
// Model
|
||||
protected $attributes = [
|
||||
'status' => 'pending',
|
||||
];
|
||||
```
|
||||
|
||||
## Write Reversible `down()` Methods by Default
|
||||
|
||||
Implement `down()` for schema changes that can be safely reversed so `migrate:rollback` works in CI and failed deployments.
|
||||
|
||||
```php
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('posts', function (Blueprint $table) {
|
||||
$table->dropColumn('slug');
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
For intentionally irreversible migrations (e.g., destructive data backfills), leave a clear comment and require a forward fix migration instead of pretending rollback is supported.
|
||||
|
||||
## Keep Migrations Focused
|
||||
|
||||
One concern per migration. Never mix DDL (schema changes) and DML (data manipulation).
|
||||
|
||||
Incorrect (partial failure creates unrecoverable state):
|
||||
```php
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('settings', function (Blueprint $table) { ... });
|
||||
DB::table('settings')->insert(['key' => 'version', 'value' => '1.0']);
|
||||
}
|
||||
```
|
||||
|
||||
Correct (separate migrations):
|
||||
```php
|
||||
// Migration 1: create_settings_table
|
||||
Schema::create('settings', function (Blueprint $table) { ... });
|
||||
|
||||
// Migration 2: seed_default_settings
|
||||
DB::table('settings')->insert(['key' => 'version', 'value' => '1.0']);
|
||||
```
|
||||
144
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/queue-jobs.md
vendored
Normal file
144
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/queue-jobs.md
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
# Queue & Job Best Practices
|
||||
|
||||
## Set `retry_after` Greater Than `timeout`
|
||||
|
||||
If `retry_after` is shorter than the job's `timeout`, the queue worker re-dispatches the job while it's still running, causing duplicate execution.
|
||||
|
||||
Incorrect (`retry_after` ≤ `timeout`):
|
||||
```php
|
||||
class ProcessReport implements ShouldQueue
|
||||
{
|
||||
public $timeout = 120;
|
||||
}
|
||||
|
||||
// config/queue.php — retry_after: 90 ← job retried while still running!
|
||||
```
|
||||
|
||||
Correct (`retry_after` > `timeout`):
|
||||
```php
|
||||
class ProcessReport implements ShouldQueue
|
||||
{
|
||||
public $timeout = 120;
|
||||
}
|
||||
|
||||
// config/queue.php — retry_after: 180 ← safely longer than any job timeout
|
||||
```
|
||||
|
||||
## Use Exponential Backoff
|
||||
|
||||
Use progressively longer delays between retries to avoid hammering failing services.
|
||||
|
||||
Incorrect (fixed retry interval):
|
||||
```php
|
||||
class SyncWithStripe implements ShouldQueue
|
||||
{
|
||||
public $tries = 3;
|
||||
// Default: retries immediately, overwhelming the API
|
||||
}
|
||||
```
|
||||
|
||||
Correct (exponential backoff):
|
||||
```php
|
||||
class SyncWithStripe implements ShouldQueue
|
||||
{
|
||||
public $tries = 3;
|
||||
public $backoff = [1, 5, 10];
|
||||
}
|
||||
```
|
||||
|
||||
## Implement `ShouldBeUnique`
|
||||
|
||||
Prevent duplicate job processing.
|
||||
|
||||
```php
|
||||
class GenerateInvoice implements ShouldQueue, ShouldBeUnique
|
||||
{
|
||||
public function uniqueId(): string
|
||||
{
|
||||
return $this->order->id;
|
||||
}
|
||||
|
||||
public $uniqueFor = 3600;
|
||||
}
|
||||
```
|
||||
|
||||
## Always Implement `failed()`
|
||||
|
||||
Handle errors explicitly — don't rely on silent failure.
|
||||
|
||||
```php
|
||||
public function failed(?Throwable $exception): void
|
||||
{
|
||||
$this->podcast->update(['status' => 'failed']);
|
||||
Log::error('Processing failed', ['id' => $this->podcast->id, 'error' => $exception->getMessage()]);
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limit External API Calls in Jobs
|
||||
|
||||
Use `RateLimited` middleware to throttle jobs calling third-party APIs.
|
||||
|
||||
```php
|
||||
public function middleware(): array
|
||||
{
|
||||
return [new RateLimited('external-api')];
|
||||
}
|
||||
```
|
||||
|
||||
## Batch Related Jobs
|
||||
|
||||
Use `Bus::batch()` when jobs should succeed or fail together.
|
||||
|
||||
```php
|
||||
Bus::batch([
|
||||
new ImportCsvChunk($chunk1),
|
||||
new ImportCsvChunk($chunk2),
|
||||
])
|
||||
->then(fn (Batch $batch) => Notification::send($user, new ImportComplete))
|
||||
->catch(fn (Batch $batch, Throwable $e) => Log::error('Batch failed'))
|
||||
->dispatch();
|
||||
```
|
||||
|
||||
## `retryUntil()` Needs `$tries = 0`
|
||||
|
||||
When using time-based retry limits, set `$tries = 0` to avoid premature failure.
|
||||
|
||||
```php
|
||||
public $tries = 0;
|
||||
|
||||
public function retryUntil(): \DateTimeInterface
|
||||
{
|
||||
return now()->addHours(4);
|
||||
}
|
||||
```
|
||||
|
||||
## Use `ShouldBeUniqueUntilProcessing` for Early Lock Release
|
||||
|
||||
`ShouldBeUnique` holds the lock until the job completes. `ShouldBeUniqueUntilProcessing` releases it when processing starts, allowing new instances to queue.
|
||||
|
||||
```php
|
||||
class UpdateSearchIndex implements ShouldQueue, ShouldBeUniqueUntilProcessing
|
||||
{
|
||||
// Lock releases when processing begins, not when it finishes
|
||||
}
|
||||
```
|
||||
|
||||
## Use Horizon for Complex Queue Scenarios
|
||||
|
||||
Use Laravel Horizon when you need monitoring, auto-scaling, failure tracking, or multiple queues with different priorities.
|
||||
|
||||
```php
|
||||
// config/horizon.php
|
||||
'environments' => [
|
||||
'production' => [
|
||||
'supervisor-1' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => ['high', 'default', 'low'],
|
||||
'balance' => 'auto',
|
||||
'minProcesses' => 1,
|
||||
'maxProcesses' => 10,
|
||||
'tries' => 3,
|
||||
],
|
||||
],
|
||||
],
|
||||
```
|
||||
99
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/routing.md
vendored
Normal file
99
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/routing.md
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
# Routing & Controllers Best Practices
|
||||
|
||||
## Use Implicit Route Model Binding
|
||||
|
||||
Let Laravel resolve models automatically from route parameters.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
public function show(int $id)
|
||||
{
|
||||
$post = Post::findOrFail($id);
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
public function show(Post $post)
|
||||
{
|
||||
return view('posts.show', ['post' => $post]);
|
||||
}
|
||||
```
|
||||
|
||||
## Use Scoped Bindings for Nested Resources
|
||||
|
||||
Enforce parent-child relationships automatically.
|
||||
|
||||
```php
|
||||
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
|
||||
// $post is automatically scoped to $user
|
||||
})->scopeBindings();
|
||||
```
|
||||
|
||||
## Use Resource Controllers
|
||||
|
||||
Use `Route::resource()` or `apiResource()` for RESTful endpoints.
|
||||
|
||||
```php
|
||||
Route::resource('posts', PostController::class);
|
||||
// In routes/api.php — the /api prefix is applied automatically
|
||||
Route::apiResource('posts', Api\PostController::class);
|
||||
```
|
||||
|
||||
## Keep Controllers Thin
|
||||
|
||||
Aim for under 10 lines per method. Extract business logic to action or service classes.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([...]);
|
||||
if ($request->hasFile('image')) {
|
||||
$request->file('image')->move(public_path('images'));
|
||||
}
|
||||
$post = Post::create($validated);
|
||||
$post->tags()->sync($validated['tags']);
|
||||
event(new PostCreated($post));
|
||||
return redirect()->route('posts.show', $post);
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
public function store(StorePostRequest $request, CreatePostAction $create)
|
||||
{
|
||||
$post = $create->execute($request->validated());
|
||||
|
||||
return redirect()->route('posts.show', $post);
|
||||
}
|
||||
```
|
||||
|
||||
## Type-Hint Form Requests
|
||||
|
||||
Type-hinting Form Requests triggers automatic validation and authorization before the method executes.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'title' => ['required', 'max:255'],
|
||||
'body' => ['required'],
|
||||
]);
|
||||
|
||||
Post::create($validated);
|
||||
|
||||
return redirect()->route('posts.index');
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
public function store(StorePostRequest $request): RedirectResponse
|
||||
{
|
||||
Post::create($request->validated());
|
||||
|
||||
return redirect()->route('posts.index');
|
||||
}
|
||||
```
|
||||
39
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/scheduling.md
vendored
Normal file
39
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/scheduling.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Task Scheduling Best Practices
|
||||
|
||||
## Use `withoutOverlapping()` on Variable-Duration Tasks
|
||||
|
||||
Without it, a long-running task spawns a second instance on the next tick, causing double-processing or resource exhaustion.
|
||||
|
||||
## Use `onOneServer()` on Multi-Server Deployments
|
||||
|
||||
Without it, every server runs the same task simultaneously. Requires a shared cache driver (Redis, database, Memcached).
|
||||
|
||||
## Use `runInBackground()` for Concurrent Long Tasks
|
||||
|
||||
By default, tasks at the same tick run sequentially. A slow first task delays all subsequent ones. `runInBackground()` runs them as separate processes.
|
||||
|
||||
## Use `environments()` to Restrict Tasks
|
||||
|
||||
Prevent accidental execution of production-only tasks (billing, reporting) on staging.
|
||||
|
||||
```php
|
||||
Schedule::command('billing:charge')->monthly()->environments(['production']);
|
||||
```
|
||||
|
||||
## Use `takeUntilTimeout()` for Time-Bounded Processing
|
||||
|
||||
A task running every 15 minutes that processes an unbounded cursor can overlap with the next run. Bound execution time.
|
||||
|
||||
## Use Schedule Groups for Shared Configuration
|
||||
|
||||
Avoid repeating `->onOneServer()->timezone('America/New_York')` across many tasks.
|
||||
|
||||
```php
|
||||
Schedule::daily()
|
||||
->onOneServer()
|
||||
->timezone('America/New_York')
|
||||
->group(function () {
|
||||
Schedule::command('emails:send --force');
|
||||
Schedule::command('emails:prune');
|
||||
});
|
||||
```
|
||||
198
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/security.md
vendored
Normal file
198
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/security.md
vendored
Normal file
@@ -0,0 +1,198 @@
|
||||
# Security Best Practices
|
||||
|
||||
## Mass Assignment Protection
|
||||
|
||||
Every model must define `$fillable` (whitelist) or `$guarded` (blacklist).
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
class User extends Model
|
||||
{
|
||||
protected $guarded = []; // All fields are mass assignable
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
class User extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
Never use `$guarded = []` on models that accept user input.
|
||||
|
||||
## Authorize Every Action
|
||||
|
||||
Use policies or gates in controllers. Never skip authorization.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
public function update(UpdatePostRequest $request, Post $post)
|
||||
{
|
||||
$post->update($request->validated());
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
public function update(UpdatePostRequest $request, Post $post)
|
||||
{
|
||||
Gate::authorize('update', $post);
|
||||
|
||||
$post->update($request->validated());
|
||||
}
|
||||
```
|
||||
|
||||
Or via Form Request:
|
||||
|
||||
```php
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()->can('update', $this->route('post'));
|
||||
}
|
||||
```
|
||||
|
||||
## Prevent SQL Injection
|
||||
|
||||
Always use parameter binding. Never interpolate user input into queries.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
DB::select("SELECT * FROM users WHERE name = '{$request->name}'");
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
User::where('name', $request->name)->get();
|
||||
|
||||
// Raw expressions with bindings
|
||||
User::whereRaw('LOWER(name) = ?', [strtolower($request->name)])->get();
|
||||
```
|
||||
|
||||
## Escape Output to Prevent XSS
|
||||
|
||||
Use `{{ }}` for HTML escaping. Only use `{!! !!}` for trusted, pre-sanitized content.
|
||||
|
||||
Incorrect:
|
||||
```blade
|
||||
{!! $user->bio !!}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```blade
|
||||
{{ $user->bio }}
|
||||
```
|
||||
|
||||
## CSRF Protection
|
||||
|
||||
Include `@csrf` in all POST/PUT/DELETE Blade forms. In Inertia apps, the `@csrf` directive is automatically applied.
|
||||
|
||||
Incorrect:
|
||||
```blade
|
||||
<form method="POST" action="/posts">
|
||||
<input type="text" name="title">
|
||||
</form>
|
||||
```
|
||||
|
||||
Correct:
|
||||
```blade
|
||||
<form method="POST" action="/posts">
|
||||
@csrf
|
||||
<input type="text" name="title">
|
||||
</form>
|
||||
```
|
||||
|
||||
## Rate Limit Auth and API Routes
|
||||
|
||||
Apply `throttle` middleware to authentication and API routes.
|
||||
|
||||
```php
|
||||
RateLimiter::for('login', function (Request $request) {
|
||||
return Limit::perMinute(5)->by($request->ip());
|
||||
});
|
||||
|
||||
Route::post('/login', LoginController::class)->middleware('throttle:login');
|
||||
```
|
||||
|
||||
## Validate File Uploads
|
||||
|
||||
Validate extension, MIME type, and size. The `mimes` rule checks extensions; use `mimetypes` for actual MIME type validation. Never trust client-provided filenames.
|
||||
|
||||
```php
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'avatar' => ['required', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'],
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
Store with generated filenames:
|
||||
|
||||
```php
|
||||
$path = $request->file('avatar')->store('avatars', 'public');
|
||||
```
|
||||
|
||||
## Keep Secrets Out of Code
|
||||
|
||||
Never commit `.env`. Access secrets via `config()` only.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
$key = env('API_KEY');
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
// config/services.php
|
||||
'api_key' => env('API_KEY'),
|
||||
|
||||
// In application code
|
||||
$key = config('services.api_key');
|
||||
```
|
||||
|
||||
## Audit Dependencies
|
||||
|
||||
Run `composer audit` periodically to check for known vulnerabilities in dependencies. Automate this in CI to catch issues before deployment.
|
||||
|
||||
```bash
|
||||
composer audit
|
||||
```
|
||||
|
||||
## Encrypt Sensitive Database Fields
|
||||
|
||||
Use `encrypted` cast for API keys/tokens and mark the attribute as `hidden`.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
class Integration extends Model
|
||||
{
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'api_key' => 'string',
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
class Integration extends Model
|
||||
{
|
||||
protected $hidden = ['api_key', 'api_secret'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'api_key' => 'encrypted',
|
||||
'api_secret' => 'encrypted',
|
||||
];
|
||||
}
|
||||
}
|
||||
```
|
||||
125
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/style.md
vendored
Normal file
125
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/style.md
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
# Conventions & Style
|
||||
|
||||
## Follow Laravel Naming Conventions
|
||||
|
||||
| What | Convention | Good | Bad |
|
||||
|------|-----------|------|-----|
|
||||
| Controller | singular | `ArticleController` | `ArticlesController` |
|
||||
| Model | singular | `User` | `Users` |
|
||||
| Table | plural, snake_case | `article_comments` | `articleComments` |
|
||||
| Pivot table | singular alphabetical | `article_user` | `user_article` |
|
||||
| Column | snake_case, no model name | `meta_title` | `article_meta_title` |
|
||||
| Foreign key | singular model + `_id` | `article_id` | `articles_id` |
|
||||
| Route | plural | `articles/1` | `article/1` |
|
||||
| Route name | snake_case with dots | `users.show_active` | `users.show-active` |
|
||||
| Method | camelCase | `getAll` | `get_all` |
|
||||
| Variable | camelCase | `$articlesWithAuthor` | `$articles_with_author` |
|
||||
| Collection | descriptive, plural | `$activeUsers` | `$data` |
|
||||
| Object | descriptive, singular | `$activeUser` | `$users` |
|
||||
| View | kebab-case | `show-filtered.blade.php` | `showFiltered.blade.php` |
|
||||
| Config | snake_case | `google_calendar.php` | `googleCalendar.php` |
|
||||
| Enum | singular | `UserType` | `UserTypes` |
|
||||
|
||||
## Prefer Shorter Readable Syntax
|
||||
|
||||
| Verbose | Shorter |
|
||||
|---------|---------|
|
||||
| `Session::get('cart')` | `session('cart')` |
|
||||
| `$request->session()->get('cart')` | `session('cart')` |
|
||||
| `$request->input('name')` | `$request->name` |
|
||||
| `return Redirect::back()` | `return back()` |
|
||||
| `Carbon::now()` | `now()` |
|
||||
| `App::make('Class')` | `app('Class')` |
|
||||
| `->where('column', '=', 1)` | `->where('column', 1)` |
|
||||
| `->orderBy('created_at', 'desc')` | `->latest()` |
|
||||
| `->orderBy('created_at', 'asc')` | `->oldest()` |
|
||||
| `->first()->name` | `->value('name')` |
|
||||
|
||||
## Use Laravel String & Array Helpers
|
||||
|
||||
Laravel provides `Str`, `Arr`, `Number`, and `Uri` helper classes that are more readable, chainable, and UTF-8 safe than raw PHP functions. Always prefer them.
|
||||
|
||||
Strings — use `Str` and fluent `Str::of()` over raw PHP:
|
||||
```php
|
||||
// Incorrect
|
||||
$slug = strtolower(str_replace(' ', '-', $title));
|
||||
$short = substr($text, 0, 100) . '...';
|
||||
$class = substr(strrchr('App\Models\User', '\'), 1);
|
||||
|
||||
// Correct
|
||||
$slug = Str::slug($title);
|
||||
$short = Str::limit($text, 100);
|
||||
$class = class_basename('App\Models\User');
|
||||
```
|
||||
|
||||
Fluent strings — chain operations for complex transformations:
|
||||
```php
|
||||
// Incorrect
|
||||
$result = strtolower(trim(str_replace('_', '-', $input)));
|
||||
|
||||
// Correct
|
||||
$result = Str::of($input)->trim()->replace('_', '-')->lower();
|
||||
```
|
||||
|
||||
Key `Str` methods to prefer: `Str::slug()`, `Str::limit()`, `Str::contains()`, `Str::before()`, `Str::after()`, `Str::between()`, `Str::camel()`, `Str::snake()`, `Str::kebab()`, `Str::headline()`, `Str::squish()`, `Str::mask()`, `Str::uuid()`, `Str::ulid()`, `Str::random()`, `Str::is()`.
|
||||
|
||||
Arrays — use `Arr` over raw PHP:
|
||||
```php
|
||||
// Incorrect
|
||||
$name = isset($array['user']['name']) ? $array['user']['name'] : 'default';
|
||||
|
||||
// Correct
|
||||
$name = Arr::get($array, 'user.name', 'default');
|
||||
```
|
||||
|
||||
Key `Arr` methods: `Arr::get()`, `Arr::has()`, `Arr::only()`, `Arr::except()`, `Arr::first()`, `Arr::flatten()`, `Arr::pluck()`, `Arr::where()`, `Arr::wrap()`.
|
||||
|
||||
Numbers — use `Number` for display formatting:
|
||||
```php
|
||||
Number::format(1000000); // "1,000,000"
|
||||
Number::currency(1500, 'USD'); // "$1,500.00"
|
||||
Number::abbreviate(1000000); // "1M"
|
||||
Number::fileSize(1024 * 1024); // "1 MB"
|
||||
Number::percentage(75.5); // "75.5%"
|
||||
```
|
||||
|
||||
URIs — use `Uri` for URL manipulation:
|
||||
```php
|
||||
$uri = Uri::of('https://example.com/search')
|
||||
->withQuery(['q' => 'laravel', 'page' => 1]);
|
||||
```
|
||||
|
||||
Use `$request->string('name')` to get a fluent `Stringable` directly from request input for immediate chaining.
|
||||
|
||||
Use `search-docs` for the full list of available methods — these helpers are extensive.
|
||||
|
||||
## No Inline JS/CSS in Blade
|
||||
|
||||
Do not put JS or CSS in Blade templates. Do not put HTML in PHP classes.
|
||||
|
||||
Incorrect:
|
||||
```blade
|
||||
let article = `{{ json_encode($article) }}`;
|
||||
```
|
||||
|
||||
Correct:
|
||||
```blade
|
||||
<button class="js-fav-article" data-article='@json($article)'>{{ $article->name }}</button>
|
||||
```
|
||||
|
||||
Pass data to JS via data attributes or use a dedicated PHP-to-JS package.
|
||||
|
||||
## No Unnecessary Comments
|
||||
|
||||
Code should be readable on its own. Use descriptive method and variable names instead of comments. The only exception is config files, where descriptive comments are expected.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
// Check if there are any joins
|
||||
if (count((array) $builder->getQuery()->joins) > 0)
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
if ($this->hasJoins())
|
||||
```
|
||||
43
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/testing.md
vendored
Normal file
43
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/testing.md
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
# Testing Best Practices
|
||||
|
||||
## Use `LazilyRefreshDatabase` Over `RefreshDatabase`
|
||||
|
||||
`RefreshDatabase` migrates once per process and wraps each test in a rolled-back transaction. `LazilyRefreshDatabase` skips even that first migration if the schema is already up to date.
|
||||
|
||||
## Use Model Assertions Over Raw Database Assertions
|
||||
|
||||
Incorrect: `$this->assertDatabaseHas('users', ['id' => $user->id]);`
|
||||
|
||||
Correct: `$this->assertModelExists($user);`
|
||||
|
||||
More expressive, type-safe, and fails with clearer messages.
|
||||
|
||||
## Use Factory States and Sequences
|
||||
|
||||
Named states make tests self-documenting. Sequences eliminate repetitive setup.
|
||||
|
||||
Incorrect: `User::factory()->create(['email_verified_at' => null]);`
|
||||
|
||||
Correct: `User::factory()->unverified()->create();`
|
||||
|
||||
## Use `Exceptions::fake()` to Assert Exception Reporting
|
||||
|
||||
Instead of `withoutExceptionHandling()`, use `Exceptions::fake()` to assert the correct exception was reported while the request completes normally.
|
||||
|
||||
## Call `Event::fake()` After Factory Setup
|
||||
|
||||
Model factories rely on model events (e.g., `creating` to generate UUIDs). Calling `Event::fake()` before factory calls silences those events, producing broken models.
|
||||
|
||||
Incorrect: `Event::fake(); $user = User::factory()->create();`
|
||||
|
||||
Correct: `$user = User::factory()->create(); Event::fake();`
|
||||
|
||||
## Use `recycle()` to Share Relationship Instances Across Factories
|
||||
|
||||
Without `recycle()`, nested factories create separate instances of the same conceptual entity.
|
||||
|
||||
```php
|
||||
Ticket::factory()
|
||||
->recycle(Airline::factory()->create())
|
||||
->create();
|
||||
```
|
||||
75
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/validation.md
vendored
Normal file
75
vendor/laravel/boost/.ai/laravel/skill/laravel-best-practices/rules/validation.md
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
# Validation & Forms Best Practices
|
||||
|
||||
## Use Form Request Classes
|
||||
|
||||
Extract validation from controllers into dedicated Form Request classes.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'title' => 'required|max:255',
|
||||
'body' => 'required',
|
||||
]);
|
||||
}
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
public function store(StorePostRequest $request)
|
||||
{
|
||||
Post::create($request->validated());
|
||||
}
|
||||
```
|
||||
|
||||
## Array vs. String Notation for Rules
|
||||
|
||||
Array syntax is more readable and composes cleanly with `Rule::` objects. Prefer it in new code, but check existing Form Requests first and match whatever notation the project already uses.
|
||||
|
||||
```php
|
||||
// Preferred for new code
|
||||
'email' => ['required', 'email', Rule::unique('users')],
|
||||
|
||||
// Follow existing convention if the project uses string notation
|
||||
'email' => 'required|email|unique:users',
|
||||
```
|
||||
|
||||
## Always Use `validated()`
|
||||
|
||||
Get only validated data. Never use `$request->all()` for mass operations.
|
||||
|
||||
Incorrect:
|
||||
```php
|
||||
Post::create($request->all());
|
||||
```
|
||||
|
||||
Correct:
|
||||
```php
|
||||
Post::create($request->validated());
|
||||
```
|
||||
|
||||
## Use `Rule::when()` for Conditional Validation
|
||||
|
||||
```php
|
||||
'company_name' => [
|
||||
Rule::when($this->account_type === 'business', ['required', 'string', 'max:255']),
|
||||
],
|
||||
```
|
||||
|
||||
## Use the `after()` Method for Custom Validation
|
||||
|
||||
Use `after()` instead of `withValidator()` for custom validation logic that depends on multiple fields.
|
||||
|
||||
```php
|
||||
public function after(): array
|
||||
{
|
||||
return [
|
||||
function (Validator $validator) {
|
||||
if ($this->quantity > Product::find($this->product_id)?->stock) {
|
||||
$validator->errors()->add('quantity', 'Not enough stock.');
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
95
vendor/laravel/boost/.ai/livewire/2/skill/livewire-development/SKILL.blade.php
vendored
Normal file
95
vendor/laravel/boost/.ai/livewire/2/skill/livewire-development/SKILL.blade.php
vendored
Normal 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)
|
||||
111
vendor/laravel/boost/.ai/livewire/3/skill/livewire-development/SKILL.blade.php
vendored
Normal file
111
vendor/laravel/boost/.ai/livewire/3/skill/livewire-development/SKILL.blade.php
vendored
Normal 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
|
||||
164
vendor/laravel/boost/.ai/livewire/4/skill/livewire-development/SKILL.blade.php
vendored
Normal file
164
vendor/laravel/boost/.ai/livewire/4/skill/livewire-development/SKILL.blade.php
vendored
Normal 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)
|
||||
39
vendor/laravel/boost/.ai/livewire/4/skill/livewire-development/reference/javascript-hooks.md
vendored
Normal file
39
vendor/laravel/boost/.ai/livewire/4/skill/livewire-development/reference/javascript-hooks.md
vendored
Normal 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
|
||||
5
vendor/laravel/boost/.ai/livewire/core.blade.php
vendored
Normal file
5
vendor/laravel/boost/.ai/livewire/core.blade.php
vendored
Normal 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.
|
||||
147
vendor/laravel/boost/.ai/mcp/skill/mcp-development/SKILL.blade.php
vendored
Normal file
147
vendor/laravel/boost/.ai/mcp/skill/mcp-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
name: mcp-development
|
||||
description: "Use this skill for Laravel MCP development only. Trigger when creating or editing MCP tools, resources, prompts, or servers in Laravel projects. Covers: artisan make:mcp-* generators, mcp:inspector, routes/ai.php, Tool/Resource/Prompt classes, schema validation, shouldRegister(), OAuth setup, URI templates, read-only attributes, and MCP debugging. Do not use for non-Laravel MCP projects or generic AI features without MCP."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# MCP Development
|
||||
|
||||
## Documentation First
|
||||
|
||||
**CRITICAL**: Always use `search-docs` BEFORE writing MCP code. The documentation is version-specific, comprehensive, and always up-to-date.
|
||||
|
||||
@boostsnippet("Search MCP Documentation", "bash")
|
||||
# Example searches
|
||||
search-docs(['mcp tools', 'mcp resources', 'mcp validation'])
|
||||
@endboostsnippet
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Artisan Commands
|
||||
|
||||
Create MCP Primitives
|
||||
```bash
|
||||
{{ $assist->artisanCommand('make:mcp-tool ToolName') }}
|
||||
{{ $assist->artisanCommand('make:mcp-resource ResourceName') }}
|
||||
{{ $assist->artisanCommand('make:mcp-prompt PromptName') }}
|
||||
{{ $assist->artisanCommand('make:mcp-server ServerName') }}
|
||||
```
|
||||
|
||||
### Basic Tool Implementation
|
||||
|
||||
@boostsnippet("Tool Example", "php")
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
use Laravel\Mcp\Request;
|
||||
use Laravel\Mcp\Response;
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
|
||||
class MyTool extends Tool
|
||||
{
|
||||
protected string $description = 'Tool description for LLM';
|
||||
|
||||
public function schema(JsonSchema $schema): array
|
||||
{
|
||||
return [
|
||||
'param' => $schema->string()->required(),
|
||||
];
|
||||
}
|
||||
|
||||
public function handle(Request $request): Response
|
||||
{
|
||||
return Response::text($request->get('param'));
|
||||
}
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
### Basic Resource Implementation
|
||||
|
||||
@boostsnippet("Resource Example", "php")
|
||||
use Laravel\Mcp\Response;
|
||||
use Laravel\Mcp\Server\Resource;
|
||||
|
||||
class MyResource extends Resource
|
||||
{
|
||||
protected string $description = 'Resource description';
|
||||
protected string $uri = 'file://path/to/resource';
|
||||
protected string $mimeType = 'text/markdown';
|
||||
|
||||
public function handle(): Response
|
||||
{
|
||||
return Response::text($content);
|
||||
}
|
||||
}
|
||||
@endboostsnippet
|
||||
|
||||
### Response Methods
|
||||
|
||||
@boostsnippet("Available Responses", "php")
|
||||
Response::text('Text content');
|
||||
Response::error('Error message');
|
||||
Response::structured(['key' => 'value']);
|
||||
@endboostsnippet
|
||||
|
||||
## Testing MCP Primitives
|
||||
|
||||
Test tools, resources, and prompts directly on their server:
|
||||
|
||||
@boostsnippet("Test MCP Primitives", "php")
|
||||
// Test a tool
|
||||
$response = MyServer::tool(MyTool::class, ['param' => 'value']);
|
||||
$response->assertOk()->assertSee('Expected text');
|
||||
|
||||
// Test as authenticated user
|
||||
$response = MyServer::actingAs($user)->tool(MyTool::class, [...]);
|
||||
|
||||
// Available assertions
|
||||
$response->assertOk();
|
||||
$response->assertSee('text');
|
||||
$response->assertHasErrors();
|
||||
$response->assertHasNoErrors();
|
||||
$response->assertName('tool-name');
|
||||
$response->assertSentNotification('event/type', ['data' => 'value']);
|
||||
@endboostsnippet
|
||||
|
||||
### MCP Inspector
|
||||
|
||||
Test interactively using the inspector:
|
||||
|
||||
<!--Launch MCP Inspector-->
|
||||
```bash
|
||||
{{ $assist->artisanCommand('mcp:inspector mcp/my-server') }} # Web server
|
||||
{{ $assist->artisanCommand('mcp:inspector my-server') }} # Local server
|
||||
```
|
||||
|
||||
## Available Features
|
||||
|
||||
The following features exist—**use `search-docs` for implementation details**:
|
||||
|
||||
- **Tools**: `schema()`, validation, annotations (`#[IsReadOnly]`, `#[IsDestructive]`, etc.)
|
||||
- **Resources**: URI templates (`HasUriTemplate`), Dynamic resources
|
||||
- **Prompts**: Arguments, multi-message responses
|
||||
- **All primitives**: Dependency injection, `shouldRegister()`, validation
|
||||
- **Responses**: Text, error, structured, streaming, metadata
|
||||
- **Server registration**: Web routes, local routes, OAuth
|
||||
|
||||
## Critical Imports
|
||||
|
||||
@boostsnippet("Correct Imports", "php")
|
||||
use Laravel\Mcp\Request; // NOT Laravel\Mcp\Server\Request
|
||||
use Laravel\Mcp\Response; // NOT Laravel\Mcp\Server\Response
|
||||
use Laravel\Mcp\Server\Tool;
|
||||
use Laravel\Mcp\Server\Resource;
|
||||
use Laravel\Mcp\Server\Prompt;
|
||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||
@endboostsnippet
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- **Not using `search-docs` before implementation**
|
||||
- Wrong imports: `Laravel\Mcp\Server\Request` (wrong) vs `Laravel\Mcp\Request` (correct)
|
||||
- Forgetting `schema()` method for tools with parameters
|
||||
- Missing required properties: `$description`, `$uri`, `$mimeType`
|
||||
- Wrong response pattern: `new Response()` instead of `Response::text()`
|
||||
- Running `mcp:start` command locally (hangs waiting for stdin)
|
||||
68
vendor/laravel/boost/.ai/pennant/skill/pennant-development/SKILL.md
vendored
Normal file
68
vendor/laravel/boost/.ai/pennant/skill/pennant-development/SKILL.md
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
---
|
||||
name: pennant-development
|
||||
description: "Use when working with Laravel Pennant the official Laravel feature flag package. Trigger whenever the query mentions Pennant by name or involves feature flags or feature toggles in a Laravel project. Tasks include defining feature flags checking whether features are active creating class based features in `app/Features` using Blade `@feature` directives scoping flags to users or teams building custom Pennant storage drivers protecting routes with feature flags testing feature flags with Pest or PHPUnit and implementing A B testing or gradual rollouts with feature flags. Do not trigger for generic Laravel configuration authorization policies authentication or non Pennant feature management systems."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
# Pennant Features
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Pennant patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Defining Features
|
||||
|
||||
<!-- Defining Features -->
|
||||
```php
|
||||
use Laravel\Pennant\Feature;
|
||||
|
||||
Feature::define('new-dashboard', function (User $user) {
|
||||
return $user->isAdmin();
|
||||
});
|
||||
```
|
||||
|
||||
### Checking Features
|
||||
|
||||
<!-- Checking Features -->
|
||||
```php
|
||||
if (Feature::active('new-dashboard')) {
|
||||
// Feature is active
|
||||
}
|
||||
|
||||
// With scope
|
||||
if (Feature::for($user)->active('new-dashboard')) {
|
||||
// Feature is active for this user
|
||||
}
|
||||
```
|
||||
|
||||
### Blade Directive
|
||||
|
||||
<!-- Blade Directive -->
|
||||
```blade
|
||||
@feature('new-dashboard')
|
||||
<x-new-dashboard />
|
||||
@else
|
||||
<x-old-dashboard />
|
||||
@endfeature
|
||||
```
|
||||
|
||||
### Activating / Deactivating
|
||||
|
||||
<!-- Activating Features -->
|
||||
```php
|
||||
Feature::activate('new-dashboard');
|
||||
Feature::for($user)->activate('new-dashboard');
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
1. Check feature flag is defined
|
||||
2. Test with different scopes/users
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Forgetting to scope features for specific users/entities
|
||||
- Not following existing naming conventions
|
||||
115
vendor/laravel/boost/.ai/pest/3/skill/pest-testing/SKILL.blade.php
vendored
Normal file
115
vendor/laravel/boost/.ai/pest/3/skill/pest-testing/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
---
|
||||
name: pest-testing
|
||||
description: "Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit) or architecture tests. Covers: test()/it()/expect() syntax, datasets, mocking, browser testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 3 features. Do not use for editing factories, seeders, migrations, controllers, models, or non-test PHP code."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Pest Testing 3
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Pest 3 patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Creating Tests
|
||||
|
||||
All tests must be written using Pest. Use `{{ $assist->artisanCommand('make:test --pest {name}') }}`.
|
||||
|
||||
The `{name}` argument should include only the path and test name, but should not include the test suite.
|
||||
- Incorrect: `{{ $assist->artisanCommand('make:test --pest Feature/SomeFeatureTest') }}` will generate `tests/Feature/Feature/SomeFeatureTest.php`
|
||||
- Correct: `{{ $assist->artisanCommand('make:test --pest SomeControllerTest') }}` will generate `tests/Feature/SomeControllerTest.php`
|
||||
- Incorrect: `{{ $assist->artisanCommand('make:test --pest --unit Unit/SomeServiceTest') }}` will generate `tests/Unit/Unit/SomeServiceTest.php`
|
||||
- Correct: `{{ $assist->artisanCommand('make:test --pest --unit SomeServiceTest') }}` will generate `tests/Unit/SomeServiceTest.php`
|
||||
|
||||
### Test Organization
|
||||
|
||||
- Tests live in the `tests/Feature` and `tests/Unit` directories.
|
||||
- Do NOT remove tests without approval - these are core application code.
|
||||
- Test happy paths, failure paths, and edge cases.
|
||||
|
||||
### Basic Test Structure
|
||||
|
||||
Pest supports both `test()` and `it()` functions. Before writing new tests, check existing test files in the same directory to match the project's convention. Use `test()` if existing tests use `test()`, or `it()` if they use `it()`.
|
||||
|
||||
@boostsnippet("Basic Pest Test Example", "php")
|
||||
it('is true', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
@endboostsnippet
|
||||
|
||||
### Running Tests
|
||||
|
||||
- Run minimal tests with filter before finalizing: `{{ $assist->artisanCommand('test --compact --filter=testName') }}`.
|
||||
- Run all tests: `{{ $assist->artisanCommand('test --compact') }}`.
|
||||
- Run file: `{{ $assist->artisanCommand('test --compact tests/Feature/ExampleTest.php') }}`.
|
||||
|
||||
## Assertions
|
||||
|
||||
Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:
|
||||
|
||||
@boostsnippet("Pest Response Assertion", "php")
|
||||
it('returns all', function () {
|
||||
$this->postJson('/api/docs', [])->assertSuccessful();
|
||||
});
|
||||
@endboostsnippet
|
||||
|
||||
| Use | Instead of |
|
||||
|-----|------------|
|
||||
| `assertSuccessful()` | `assertStatus(200)` |
|
||||
| `assertNotFound()` | `assertStatus(404)` |
|
||||
| `assertForbidden()` | `assertStatus(403)` |
|
||||
|
||||
## Mocking
|
||||
|
||||
Import mock function before use: `use function Pest\Laravel\mock;`
|
||||
|
||||
## Datasets
|
||||
|
||||
Use datasets for repetitive tests (validation rules, etc.):
|
||||
|
||||
@boostsnippet("Pest Dataset Example", "php")
|
||||
it('has emails', function (string $email) {
|
||||
expect($email)->not->toBeEmpty();
|
||||
})->with([
|
||||
'james' => 'james@laravel.com',
|
||||
'taylor' => 'taylor@laravel.com',
|
||||
]);
|
||||
@endboostsnippet
|
||||
|
||||
## Pest 3 Features
|
||||
|
||||
### Architecture Testing
|
||||
|
||||
Pest 3 includes architecture testing to enforce code conventions:
|
||||
|
||||
@boostsnippet("Architecture Test Example", "php")
|
||||
arch('controllers')
|
||||
->expect('App\Http\Controllers')
|
||||
->toExtendNothing()
|
||||
->toHaveSuffix('Controller');
|
||||
|
||||
arch('models')
|
||||
->expect('App\Models')
|
||||
->toExtend('Illuminate\Database\Eloquent\Model');
|
||||
|
||||
arch('no debugging')
|
||||
->expect(['dd', 'dump', 'ray'])
|
||||
->not->toBeUsed();
|
||||
@endboostsnippet
|
||||
|
||||
### Type Coverage
|
||||
|
||||
Pest 3 provides improved type coverage analysis. Run with `--type-coverage` flag.
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Not importing `use function Pest\Laravel\mock;` before using mock
|
||||
- Using `assertStatus(200)` instead of `assertSuccessful()`
|
||||
- Forgetting datasets for repetitive validation tests
|
||||
- Deleting tests without approval
|
||||
- Prefixing `Feature/` or `Unit/` in `{name}` when using `make:test`
|
||||
162
vendor/laravel/boost/.ai/pest/4/skill/pest-testing/SKILL.blade.php
vendored
Normal file
162
vendor/laravel/boost/.ai/pest/4/skill/pest-testing/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
---
|
||||
name: pest-testing
|
||||
description: "Use this skill for Pest PHP testing in Laravel projects only. Trigger whenever any test is being written, edited, fixed, or refactored — including fixing tests that broke after a code change, adding assertions, converting PHPUnit to Pest, adding datasets, and TDD workflows. Always activate when the user asks how to write something in Pest, mentions test files or directories (tests/Feature, tests/Unit, tests/Browser), or needs browser testing, smoke testing multiple pages for JS errors, or architecture tests. Covers: test()/it()/expect() syntax, datasets, mocking, browser testing (visit/click/fill), smoke testing, arch(), Livewire component tests, RefreshDatabase, and all Pest 4 features. Do not use for factories, seeders, migrations, controllers, models, or non-test PHP code."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Pest Testing 4
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Pest 4 patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Creating Tests
|
||||
|
||||
All tests must be written using Pest. Use `{{ $assist->artisanCommand('make:test --pest {name}') }}`.
|
||||
|
||||
The `{name}` argument should include only the path and test name, but should not include the test suite.
|
||||
- Incorrect: `{{ $assist->artisanCommand('make:test --pest Feature/SomeFeatureTest') }}` will generate `tests/Feature/Feature/SomeFeatureTest.php`
|
||||
- Correct: `{{ $assist->artisanCommand('make:test --pest SomeControllerTest') }}` will generate `tests/Feature/SomeControllerTest.php`
|
||||
- Incorrect: `{{ $assist->artisanCommand('make:test --pest --unit Unit/SomeServiceTest') }}` will generate `tests/Unit/Unit/SomeServiceTest.php`
|
||||
- Correct: `{{ $assist->artisanCommand('make:test --pest --unit SomeServiceTest') }}` will generate `tests/Unit/SomeServiceTest.php`
|
||||
|
||||
### Test Organization
|
||||
|
||||
- Unit/Feature tests: `tests/Feature` and `tests/Unit` directories.
|
||||
- Browser tests: `tests/Browser/` directory.
|
||||
- Do NOT remove tests without approval - these are core application code.
|
||||
|
||||
### Basic Test Structure
|
||||
|
||||
Pest supports both `test()` and `it()` functions. Before writing new tests, check existing test files in the same directory to match the project's convention. Use `test()` if existing tests use `test()`, or `it()` if they use `it()`.
|
||||
|
||||
@boostsnippet("Basic Pest Test Example", "php")
|
||||
it('is true', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
@endboostsnippet
|
||||
|
||||
### Running Tests
|
||||
|
||||
- Run minimal tests with filter before finalizing: `{{ $assist->artisanCommand('test --compact --filter=testName') }}`.
|
||||
- Run all tests: `{{ $assist->artisanCommand('test --compact') }}`.
|
||||
- Run file: `{{ $assist->artisanCommand('test --compact tests/Feature/ExampleTest.php') }}`.
|
||||
|
||||
## Assertions
|
||||
|
||||
Use specific assertions (`assertSuccessful()`, `assertNotFound()`) instead of `assertStatus()`:
|
||||
|
||||
@boostsnippet("Pest Response Assertion", "php")
|
||||
it('returns all', function () {
|
||||
$this->postJson('/api/docs', [])->assertSuccessful();
|
||||
});
|
||||
@endboostsnippet
|
||||
|
||||
| Use | Instead of |
|
||||
|-----|------------|
|
||||
| `assertSuccessful()` | `assertStatus(200)` |
|
||||
| `assertNotFound()` | `assertStatus(404)` |
|
||||
| `assertForbidden()` | `assertStatus(403)` |
|
||||
|
||||
## Mocking
|
||||
|
||||
Import mock function before use: `use function Pest\Laravel\mock;`
|
||||
|
||||
## Datasets
|
||||
|
||||
Use datasets for repetitive tests (validation rules, etc.):
|
||||
|
||||
@boostsnippet("Pest Dataset Example", "php")
|
||||
it('has emails', function (string $email) {
|
||||
expect($email)->not->toBeEmpty();
|
||||
})->with([
|
||||
'james' => 'james@laravel.com',
|
||||
'taylor' => 'taylor@laravel.com',
|
||||
]);
|
||||
@endboostsnippet
|
||||
|
||||
## Pest 4 Features
|
||||
|
||||
| Feature | Purpose |
|
||||
|---------|---------|
|
||||
| Browser Testing | Full integration tests in real browsers |
|
||||
| Smoke Testing | Validate multiple pages quickly |
|
||||
| Visual Regression | Compare screenshots for visual changes |
|
||||
| Test Sharding | Parallel CI runs |
|
||||
| Architecture Testing | Enforce code conventions |
|
||||
|
||||
### Browser Test Example
|
||||
|
||||
Browser tests run in real browsers for full integration testing:
|
||||
|
||||
- Browser tests live in `tests/Browser/`.
|
||||
- Use Laravel features like `Event::fake()`, `assertAuthenticated()`, and model factories.
|
||||
- Use `RefreshDatabase` for clean state per test.
|
||||
- Interact with page: click, type, scroll, select, submit, drag-and-drop, touch gestures.
|
||||
- Test on multiple browsers (Chrome, Firefox, Safari) if requested.
|
||||
- Test on different devices/viewports (iPhone 14 Pro, tablets) if requested.
|
||||
- Switch color schemes (light/dark mode) when appropriate.
|
||||
- Take screenshots or pause tests for debugging.
|
||||
|
||||
@boostsnippet("Pest Browser Test Example", "php")
|
||||
it('may reset the password', function () {
|
||||
Notification::fake();
|
||||
|
||||
$this->actingAs(User::factory()->create());
|
||||
|
||||
$page = visit('/sign-in');
|
||||
|
||||
$page->assertSee('Sign In')
|
||||
->assertNoJavaScriptErrors()
|
||||
->click('Forgot Password?')
|
||||
->fill('email', 'nuno@laravel.com')
|
||||
->click('Send Reset Link')
|
||||
->assertSee('We have emailed your password reset link!');
|
||||
|
||||
Notification::assertSent(ResetPassword::class);
|
||||
});
|
||||
@endboostsnippet
|
||||
|
||||
### Smoke Testing
|
||||
|
||||
Quickly validate multiple pages have no JavaScript errors:
|
||||
|
||||
@boostsnippet("Pest Smoke Testing Example", "php")
|
||||
$pages = visit(['/', '/about', '/contact']);
|
||||
|
||||
$pages->assertNoJavaScriptErrors()->assertNoConsoleLogs();
|
||||
@endboostsnippet
|
||||
|
||||
### Visual Regression Testing
|
||||
|
||||
Capture and compare screenshots to detect visual changes.
|
||||
|
||||
### Test Sharding
|
||||
|
||||
Split tests across parallel processes for faster CI runs.
|
||||
|
||||
### Architecture Testing
|
||||
|
||||
Pest 4 includes architecture testing (from Pest 3):
|
||||
|
||||
@boostsnippet("Architecture Test Example", "php")
|
||||
arch('controllers')
|
||||
->expect('App\Http\Controllers')
|
||||
->toExtendNothing()
|
||||
->toHaveSuffix('Controller');
|
||||
@endboostsnippet
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Not importing `use function Pest\Laravel\mock;` before using mock
|
||||
- Using `assertStatus(200)` instead of `assertSuccessful()`
|
||||
- Forgetting datasets for repetitive validation tests
|
||||
- Deleting tests without approval
|
||||
- Forgetting `assertNoJavaScriptErrors()` in browser tests
|
||||
- Prefixing `Feature/` or `Unit/` in `{name}` when using `make:test`
|
||||
9
vendor/laravel/boost/.ai/pest/core.blade.php
vendored
Normal file
9
vendor/laravel/boost/.ai/pest/core.blade.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
## Pest
|
||||
|
||||
- This project uses Pest for testing. Create tests: `{{ $assist->artisanCommand('make:test --pest {name}') }}`.
|
||||
- The `{name}` argument should not include the test suite directory. Use `{{ $assist->artisanCommand('make:test --pest SomeFeatureTest') }}` instead of `{{ $assist->artisanCommand('make:test --pest Feature/SomeFeatureTest') }}`.
|
||||
- Run tests: `{{ $assist->artisanCommand('test --compact') }}` or filter: `{{ $assist->artisanCommand('test --compact --filter=testName') }}`.
|
||||
- Do NOT delete tests without approval.
|
||||
0
vendor/laravel/boost/.ai/php/8.2/core.blade.php
vendored
Normal file
0
vendor/laravel/boost/.ai/php/8.2/core.blade.php
vendored
Normal file
0
vendor/laravel/boost/.ai/php/8.3/core.blade.php
vendored
Normal file
0
vendor/laravel/boost/.ai/php/8.3/core.blade.php
vendored
Normal file
13
vendor/laravel/boost/.ai/php/8.4/core.blade.php
vendored
Normal file
13
vendor/laravel/boost/.ai/php/8.4/core.blade.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
## PHP 8.4
|
||||
|
||||
Use these array functions instead of manual loops when not using Laravel collections:
|
||||
- `array_find(array $array, callable $callback): mixed` - first matching element
|
||||
- `array_find_key(array $array, callable $callback): int|string|null` - first matching key
|
||||
- `array_any(array $array, callable $callback): bool` - true if any element matches
|
||||
- `array_all(array $array, callable $callback): bool` - true if all elements match
|
||||
|
||||
Chain directly on new instances without wrapping in parentheses:
|
||||
```php
|
||||
// Before: $response = (new JsonResponse(['data' => $data]))->setStatusCode(201);
|
||||
$response = new JsonResponse(['data' => $data])->setStatusCode(201);
|
||||
```
|
||||
13
vendor/laravel/boost/.ai/php/8.5/core.blade.php
vendored
Normal file
13
vendor/laravel/boost/.ai/php/8.5/core.blade.php
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
## PHP 8.5
|
||||
|
||||
Use these array functions instead of manual loops when not using Laravel collections:
|
||||
- `array_first(array $array): mixed` - first value or `null` if empty
|
||||
- `array_last(array $array): mixed` - last value or `null` if empty
|
||||
|
||||
Use the pipe operator (`|>`) to chain function calls left-to-right instead of nesting:
|
||||
```php
|
||||
// Before: $slug = strtolower(str_replace(' ', '-', trim($title)));
|
||||
$slug = $title |> trim(...) |> (fn($s) => str_replace(' ', '-', $s)) |> strtolower(...);
|
||||
```
|
||||
|
||||
Use `clone($object, ['property' => $value])` to modify properties during cloning. Ideal for readonly classes.
|
||||
15
vendor/laravel/boost/.ai/php/core.blade.php
vendored
Normal file
15
vendor/laravel/boost/.ai/php/core.blade.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# PHP
|
||||
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
- Always use curly braces for control structures, even for single-line bodies.
|
||||
- Use PHP 8 constructor property promotion: `public function __construct(public GitHub $github) { }`. Do not leave empty zero-parameter `__construct()` methods unless the constructor is private.
|
||||
- Use explicit return type declarations and type hints for all method parameters: `function isAccessible(User $user, ?string $path = null): bool`
|
||||
@if(empty($assist->enums()) || !preg_match('/[A-Z]{3,8}/', $assist->enumContents()))
|
||||
- Use TitleCase for Enum keys: `FavoritePerson`, `BestLake`, `Monthly`.
|
||||
@else
|
||||
- Follow existing application Enum naming conventions.
|
||||
@endif
|
||||
- Prefer PHPDoc blocks over inline comments. Only add inline comments for exceptionally complex logic.
|
||||
- Use array shape type definitions in PHPDoc blocks.
|
||||
17
vendor/laravel/boost/.ai/phpunit/core.blade.php
vendored
Normal file
17
vendor/laravel/boost/.ai/phpunit/core.blade.php
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# PHPUnit
|
||||
|
||||
- This application uses PHPUnit for testing. All tests must be written as PHPUnit classes. Use `{{ $assist->artisanCommand('make:test --phpunit {name}') }}` to create a new test.
|
||||
- If you see a test using "Pest", convert it to PHPUnit.
|
||||
- Every time a test has been updated, run that singular test.
|
||||
- When the tests relating to your feature are passing, ask the user if they would like to also run the entire test suite to make sure everything is still passing.
|
||||
- Tests should cover all happy paths, failure paths, and edge cases.
|
||||
- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files; these are core to the application.
|
||||
|
||||
## Running Tests
|
||||
- Run the minimal number of tests, using an appropriate filter, before finalizing.
|
||||
- To run all tests: `{{ $assist->artisanCommand('test --compact') }}`.
|
||||
- To run all tests in a file: `{{ $assist->artisanCommand('test --compact tests/Feature/ExampleTest.php') }}`.
|
||||
- To filter on a particular test name: `{{ $assist->artisanCommand('test --compact --filter=testName') }}` (recommended after making a change to a related file).
|
||||
12
vendor/laravel/boost/.ai/pint/core.blade.php
vendored
Normal file
12
vendor/laravel/boost/.ai/pint/core.blade.php
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Laravel Pint Code Formatter
|
||||
|
||||
@if($assist->supportsPintAgentFormatter())
|
||||
- If you have modified any PHP files, you must run `{{ $assist->binCommand('pint') }} --dirty --format agent` before finalizing changes to ensure your code matches the project's expected style.
|
||||
- Do not run `{{ $assist->binCommand('pint') }} --test --format agent`, simply run `{{ $assist->binCommand('pint') }} --format agent` to fix any formatting issues.
|
||||
@else
|
||||
- If you have modified any PHP files, you must run `{{ $assist->binCommand('pint') }} --dirty` before finalizing changes to ensure your code matches the project's expected style.
|
||||
- Do not run `{{ $assist->binCommand('pint') }} --test`, simply run `{{ $assist->binCommand('pint') }}` to fix any formatting issues.
|
||||
@endif
|
||||
14
vendor/laravel/boost/.ai/sail/core.blade.php
vendored
Normal file
14
vendor/laravel/boost/.ai/sail/core.blade.php
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Laravel Sail
|
||||
|
||||
- This project runs inside Laravel Sail's Docker containers. You MUST execute all commands through Sail.
|
||||
- Start services using `{{ $assist->sailBinaryPath() }} up -d` and stop them with `{{ $assist->sailBinaryPath() }} stop`.
|
||||
- Open the application in the browser by running `{{ $assist->sailBinaryPath() }} open`.
|
||||
- Always prefix PHP, Artisan, Composer, and Node commands with `{{ $assist->sailBinaryPath() }}`. Examples:
|
||||
- Run Artisan Commands: `{{ $assist->artisanCommand('migrate') }}`
|
||||
- Install Composer packages: `{{ $assist->composerCommand('install') }}`
|
||||
- Execute Node commands: `{{ $assist->nodePackageManagerCommand('run dev') }}`
|
||||
- Execute PHP scripts: `{{ $assist->sailBinaryPath() }} php [script]`
|
||||
- View all available Sail commands by running `{{ $assist->sailBinaryPath() }}` without arguments.
|
||||
90
vendor/laravel/boost/.ai/tailwindcss/3/skill/tailwindcss-development/SKILL.md
vendored
Normal file
90
vendor/laravel/boost/.ai/tailwindcss/3/skill/tailwindcss-development/SKILL.md
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: tailwindcss-development
|
||||
description: "Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
# Tailwind CSS Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Tailwind CSS v3 patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
- Use Tailwind CSS classes to style HTML. Check and follow existing Tailwind conventions in the project before introducing new patterns.
|
||||
- Offer to extract repeated patterns into components that match the project's conventions (e.g., Blade, JSX, Vue).
|
||||
- Consider class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child elements carefully to reduce repetition, and group elements logically.
|
||||
|
||||
## Tailwind CSS v3 Specifics
|
||||
|
||||
- Always use Tailwind CSS v3 and verify you're using only classes it supports.
|
||||
- Configuration is done in the `tailwind.config.js` file.
|
||||
- Import using `@tailwind` directives:
|
||||
|
||||
<!-- v3 Import Syntax -->
|
||||
```css
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
```
|
||||
|
||||
## Spacing
|
||||
|
||||
When listing items, use gap utilities for spacing; don't use margins.
|
||||
|
||||
<!-- Gap Utilities -->
|
||||
```html
|
||||
<div class="flex gap-8">
|
||||
<div>Item 1</div>
|
||||
<div>Item 2</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Dark Mode
|
||||
|
||||
If existing pages and components support dark mode, new pages and components must support it the same way, typically using the `dark:` variant:
|
||||
|
||||
<!-- Dark Mode -->
|
||||
```html
|
||||
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
|
||||
Content adapts to color scheme
|
||||
</div>
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Flexbox Layout
|
||||
|
||||
<!-- Flexbox Layout -->
|
||||
```html
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>Left content</div>
|
||||
<div>Right content</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Grid Layout
|
||||
|
||||
<!-- Grid Layout -->
|
||||
```html
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div>Card 1</div>
|
||||
<div>Card 2</div>
|
||||
<div>Card 3</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
1. Check browser for visual rendering
|
||||
2. Test responsive breakpoints
|
||||
3. Verify dark mode if project uses it
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using margins for spacing between siblings instead of gap utilities
|
||||
- Forgetting to add dark mode variants when the project uses dark mode
|
||||
- Not checking existing project conventions before adding new utilities
|
||||
- Overusing inline styles when Tailwind classes would suffice
|
||||
118
vendor/laravel/boost/.ai/tailwindcss/4/skill/tailwindcss-development/SKILL.md
vendored
Normal file
118
vendor/laravel/boost/.ai/tailwindcss/4/skill/tailwindcss-development/SKILL.md
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
name: tailwindcss-development
|
||||
description: "Always invoke when the user's message includes 'tailwind' in any form. Also invoke for: building responsive grid layouts (multi-column card grids, product grids), flex/grid page structures (dashboards with sidebars, fixed topbars, mobile-toggle navs), styling UI components (cards, tables, navbars, pricing sections, forms, inputs, badges), adding dark mode variants, fixing spacing or typography, and Tailwind v3/v4 work. The core use case: writing or fixing Tailwind utility classes in HTML templates (Blade, JSX, Vue). Skip for backend PHP logic, database queries, API routes, JavaScript with no HTML/CSS component, CSS file audits, build tool configuration, and vanilla CSS."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
# Tailwind CSS Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Tailwind CSS v4 patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
- Use Tailwind CSS classes to style HTML. Check and follow existing Tailwind conventions in the project before introducing new patterns.
|
||||
- Offer to extract repeated patterns into components that match the project's conventions (e.g., Blade, JSX, Vue).
|
||||
- Consider class placement, order, priority, and defaults. Remove redundant classes, add classes to parent or child elements carefully to reduce repetition, and group elements logically.
|
||||
|
||||
## Tailwind CSS v4 Specifics
|
||||
|
||||
- Always use Tailwind CSS v4 and avoid deprecated utilities.
|
||||
- `corePlugins` is not supported in Tailwind v4.
|
||||
|
||||
### CSS-First Configuration
|
||||
|
||||
In Tailwind v4, configuration is CSS-first using the `@theme` directive — no separate `tailwind.config.js` file is needed:
|
||||
|
||||
<!-- CSS-First Config -->
|
||||
```css
|
||||
@theme {
|
||||
--color-brand: oklch(0.72 0.11 178);
|
||||
}
|
||||
```
|
||||
|
||||
### Import Syntax
|
||||
|
||||
In Tailwind v4, import Tailwind with a regular CSS `@import` statement instead of the `@tailwind` directives used in v3:
|
||||
|
||||
<!-- v4 Import Syntax -->
|
||||
```diff
|
||||
- @tailwind base;
|
||||
- @tailwind components;
|
||||
- @tailwind utilities;
|
||||
+ @import "tailwindcss";
|
||||
```
|
||||
|
||||
### Replaced Utilities
|
||||
|
||||
Tailwind v4 removed deprecated utilities. Use the replacements shown below. Opacity values remain numeric.
|
||||
|
||||
| Deprecated | Replacement |
|
||||
|------------|-------------|
|
||||
| bg-opacity-* | bg-black/* |
|
||||
| text-opacity-* | text-black/* |
|
||||
| border-opacity-* | border-black/* |
|
||||
| divide-opacity-* | divide-black/* |
|
||||
| ring-opacity-* | ring-black/* |
|
||||
| placeholder-opacity-* | placeholder-black/* |
|
||||
| flex-shrink-* | shrink-* |
|
||||
| flex-grow-* | grow-* |
|
||||
| overflow-ellipsis | text-ellipsis |
|
||||
| decoration-slice | box-decoration-slice |
|
||||
| decoration-clone | box-decoration-clone |
|
||||
|
||||
## Spacing
|
||||
|
||||
Use `gap` utilities instead of margins for spacing between siblings:
|
||||
|
||||
<!-- Gap Utilities -->
|
||||
```html
|
||||
<div class="flex gap-8">
|
||||
<div>Item 1</div>
|
||||
<div>Item 2</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Dark Mode
|
||||
|
||||
If existing pages and components support dark mode, new pages and components must support it the same way, typically using the `dark:` variant:
|
||||
|
||||
<!-- Dark Mode -->
|
||||
```html
|
||||
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
|
||||
Content adapts to color scheme
|
||||
</div>
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Flexbox Layout
|
||||
|
||||
<!-- Flexbox Layout -->
|
||||
```html
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>Left content</div>
|
||||
<div>Right content</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Grid Layout
|
||||
|
||||
<!-- Grid Layout -->
|
||||
```html
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div>Card 1</div>
|
||||
<div>Card 2</div>
|
||||
<div>Card 3</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using deprecated v3 utilities (bg-opacity-*, flex-shrink-*, etc.)
|
||||
- Using `@tailwind` directives instead of `@import "tailwindcss"`
|
||||
- Trying to use `tailwind.config.js` instead of CSS `@theme` directive
|
||||
- Using margins for spacing between siblings instead of gap utilities
|
||||
- Forgetting to add dark mode variants when the project uses dark mode
|
||||
6
vendor/laravel/boost/.ai/volt/core.blade.php
vendored
Normal file
6
vendor/laravel/boost/.ai/volt/core.blade.php
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# Livewire Volt
|
||||
|
||||
- Single-file Livewire components: PHP logic and Blade templates in one file.
|
||||
- Always check existing Volt components to determine functional vs class-based style.
|
||||
- IMPORTANT: Always use `search-docs` tool for version-specific Volt documentation and updated code examples.
|
||||
- IMPORTANT: Activate `volt-development` every time you're working with a Volt or single-file component-related task.
|
||||
87
vendor/laravel/boost/.ai/volt/skill/volt-development/SKILL.blade.php
vendored
Normal file
87
vendor/laravel/boost/.ai/volt/skill/volt-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
---
|
||||
name: volt-development
|
||||
description: "Develops single-file Livewire components with Volt. Activates when creating Volt components, converting Livewire to Volt, working with @volt directive, functional or class-based Volt APIs; or when the user mentions Volt, single-file components, functional Livewire, or inline component logic in Blade files."
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Volt Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Volt patterns and documentation.
|
||||
|
||||
## Basic Usage
|
||||
|
||||
Create components with `{{ $assist->artisanCommand('make:volt [name] [--test] [--pest]') }}`.
|
||||
|
||||
Important: Check existing Volt components to determine if they use functional or class-based style before creating new ones.
|
||||
|
||||
### Functional Components
|
||||
|
||||
@boostsnippet("Volt Functional Component", "php")
|
||||
@@volt
|
||||
<?php
|
||||
use function Livewire\Volt\{state, computed};
|
||||
|
||||
state(['count' => 0]);
|
||||
|
||||
$increment = fn () => $this->count++;
|
||||
$double = computed(fn () => $this->count * 2);
|
||||
?>
|
||||
|
||||
<div>
|
||||
<h1>Count: @{{ $count }} (Double: @{{ $this->double }})</h1>
|
||||
<button wire:click="increment">+</button>
|
||||
</div>
|
||||
@@endvolt
|
||||
@endboostsnippet
|
||||
|
||||
### Class-Based Components
|
||||
|
||||
@boostsnippet("Volt Class-based Component", "php")
|
||||
use Livewire\Volt\Component;
|
||||
|
||||
new class extends Component {
|
||||
public int $count = 0;
|
||||
|
||||
public function increment(): void
|
||||
{
|
||||
$this->count++;
|
||||
}
|
||||
} ?>
|
||||
|
||||
<div>
|
||||
<h1>@{{ $count }}</h1>
|
||||
<button wire:click="increment">+</button>
|
||||
</div>
|
||||
@endboostsnippet
|
||||
|
||||
## Testing
|
||||
|
||||
Tests go in existing Volt test directory or `tests/Feature/Volt`:
|
||||
|
||||
@boostsnippet("Volt Test Example", "php")
|
||||
use Livewire\Volt\Volt;
|
||||
|
||||
test('counter increments', function () {
|
||||
Volt::test('counter')
|
||||
->assertSee('Count: 0')
|
||||
->call('increment')
|
||||
->assertSee('Count: 1');
|
||||
});
|
||||
@endboostsnippet
|
||||
|
||||
## Verification
|
||||
|
||||
1. Check existing components for functional vs class-based style
|
||||
2. Test component with `Volt::test()`
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Not checking existing style (functional vs class-based) before creating
|
||||
- Forgetting `@volt` directive wrapper
|
||||
- Missing `--test` or `--pest` flag when tests are needed
|
||||
6
vendor/laravel/boost/.ai/wayfinder/core.blade.php
vendored
Normal file
6
vendor/laravel/boost/.ai/wayfinder/core.blade.php
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Laravel Wayfinder
|
||||
|
||||
Use Wayfinder to generate TypeScript functions for Laravel routes. Import from `@/actions/` (controllers) or `@/routes/` (named routes).
|
||||
103
vendor/laravel/boost/.ai/wayfinder/skill/wayfinder-development/SKILL.blade.php
vendored
Normal file
103
vendor/laravel/boost/.ai/wayfinder/skill/wayfinder-development/SKILL.blade.php
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
---
|
||||
name: wayfinder-development
|
||||
description: "Use this skill for Laravel Wayfinder which auto-generates typed functions for Laravel controllers and routes. ALWAYS use this skill when frontend code needs to call backend routes or controller actions. Trigger when: connecting any React/Vue/Svelte/Inertia frontend to Laravel controllers, routes, building end-to-end features with both frontend and backend, wiring up forms or links to backend endpoints, fixing route-related TypeScript errors, importing from @/actions or @/routes, or running wayfinder:generate. Use Wayfinder route functions instead of hardcoded URLs. Covers: wayfinder() vite plugin, .url()/.get()/.post()/.form(), query params, route model binding, tree-shaking. Do not use for backend-only task"
|
||||
license: MIT
|
||||
metadata:
|
||||
author: laravel
|
||||
---
|
||||
@php
|
||||
/** @var \Laravel\Boost\Install\GuidelineAssist $assist */
|
||||
@endphp
|
||||
# Wayfinder Development
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `search-docs` for detailed Wayfinder patterns and documentation.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Generate Routes
|
||||
|
||||
Run after route changes if Vite plugin isn't installed:
|
||||
```bash
|
||||
{{ $assist->artisanCommand('wayfinder:generate --no-interaction') }}
|
||||
```
|
||||
For form helpers, use `--with-form` flag:
|
||||
```bash
|
||||
{{ $assist->artisanCommand('wayfinder:generate --with-form --no-interaction') }}
|
||||
```
|
||||
### Import Patterns
|
||||
|
||||
@boostsnippet("Controller Action Imports", "typescript")
|
||||
// Named imports for tree-shaking (preferred)...
|
||||
import { show, store, update } from '@/actions/App/Http/Controllers/PostController'
|
||||
|
||||
// Named route imports...
|
||||
import { show as postShow } from '@/routes/post'
|
||||
@endboostsnippet
|
||||
|
||||
### Common Methods
|
||||
|
||||
@boostsnippet("Wayfinder Methods", "typescript")
|
||||
// Get route object...
|
||||
show(1) // { url: "/posts/1", method: "get" }
|
||||
|
||||
// Get URL string...
|
||||
show.url(1) // "/posts/1"
|
||||
|
||||
// Specific HTTP methods...
|
||||
show.get(1)
|
||||
store.post()
|
||||
update.patch(1)
|
||||
destroy.delete(1)
|
||||
|
||||
// Form attributes for HTML forms...
|
||||
store.form() // { action: "/posts", method: "post" }
|
||||
|
||||
// Query parameters...
|
||||
show(1, { query: { page: 1 } }) // "/posts/1?page=1"
|
||||
@endboostsnippet
|
||||
|
||||
@if($assist->roster->uses(\Laravel\Roster\Enums\Packages::INERTIA_LARAVEL) || $assist->roster->uses(\Laravel\Roster\Enums\Packages::INERTIA_REACT) || $assist->roster->uses(\Laravel\Roster\Enums\Packages::INERTIA_VUE) || $assist->roster->uses(\Laravel\Roster\Enums\Packages::INERTIA_SVELTE))
|
||||
## Wayfinder + Inertia
|
||||
|
||||
@if($assist->inertia()->hasFormComponent())
|
||||
Use Wayfinder with the `<Form>` component:
|
||||
@if($assist->roster->uses(\Laravel\Roster\Enums\Packages::INERTIA_REACT))
|
||||
@boostsnippet("Wayfinder Form (React)", "typescript")
|
||||
<Form {...store.form()}><input name="title" /></Form>
|
||||
@endboostsnippet
|
||||
@endif
|
||||
@if($assist->roster->uses(\Laravel\Roster\Enums\Packages::INERTIA_VUE))
|
||||
@boostsnippet("Wayfinder Form (Vue)", "vue")
|
||||
<Form v-bind="store.form()"><input name="title" /></Form>
|
||||
@endboostsnippet
|
||||
@endif
|
||||
@if($assist->roster->uses(\Laravel\Roster\Enums\Packages::INERTIA_SVELTE))
|
||||
@boostsnippet("Wayfinder Form (Svelte)", "svelte")
|
||||
<Form {...store.form()}><input name="title" /></Form>
|
||||
@endboostsnippet
|
||||
@endif
|
||||
@else
|
||||
Use Wayfinder with `useForm`:
|
||||
|
||||
@boostsnippet("Wayfinder useForm", "typescript")
|
||||
import { store } from "@/actions/App/Http/Controllers/ExampleController";
|
||||
|
||||
const form = useForm({ name: "My Big Post" });
|
||||
form.submit(store());
|
||||
@endboostsnippet
|
||||
@endif
|
||||
@endif
|
||||
|
||||
## Verification
|
||||
|
||||
1. Run `{{ $assist->artisanCommand('wayfinder:generate') }}` to regenerate routes if Vite plugin isn't installed
|
||||
2. Check TypeScript imports resolve correctly
|
||||
3. Verify route URLs match expected paths
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
- Using default imports instead of named imports (breaks tree-shaking)
|
||||
- Forgetting to regenerate after route changes
|
||||
- Not using type-safe parameter objects for route model binding
|
||||
Reference in New Issue
Block a user