Regenerate Boost guidelines and skills

Generated by boost:update for Boost 2.8, which replaces the pest-testing
skill with testing-best-practices and adds infer-conventions.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XYnWFt9pJEwvAmNFN38XD
This commit is contained in:
Andreas Reinhold / reini
2026-09-10 10:42:22 +02:00
co-authored by Claude Opus 5
parent 3638455167
commit 92b3b3de56
38 changed files with 1521 additions and 1001 deletions
@@ -1,75 +1,89 @@
# Validation & Forms Best Practices
# Validation and Forms Best Practices
## Use Form Request Classes
## Extract Validation When It Improves the Boundary
Extract validation from controllers into dedicated Form Request classes.
Use a form request when validation or authorization is substantial, reused, or clearer outside the controller. Inline `$request->validate()` remains appropriate for a small, endpoint-specific rule set.
Incorrect:
```php
public function store(Request $request)
public function store(StorePostRequest $request): RedirectResponse
{
$request->validate([
'title' => 'required|max:255',
'body' => 'required',
]);
$post = Post::create($request->validated());
return redirect()->route('posts.show', $post);
}
```
Correct:
```php
public function store(StorePostRequest $request)
{
Post::create($request->validated());
}
```
A form request's `authorize()` method can enforce access to the operation. Validation establishes the shape and values of input; it does not itself authorize the user.
## Array vs. String Notation for Rules
## Prefer Readable Rule Syntax
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.
Array syntax composes cleanly with rule objects and avoids delimiter issues. Prefer it in new code when it improves readability, while following a consistent local style.
```php
// Preferred for new code
'email' => ['required', 'email', Rule::unique('users')],
```
// Follow existing convention if the project uses string notation
String syntax remains valid for simple rules:
```php
'email' => 'required|email|unique:users',
```
## Always Use `validated()`
## Use Only Intended Validated Data
Get only validated data. Never use `$request->all()` for mass operations.
Use `validated()` or `safe()` instead of `$request->all()` when passing request data onward. Then select the fields intended for the operation when the validation rules also cover control fields or nested data.
Unsafe:
Incorrect:
```php
Post::create($request->all());
```
Correct:
Preferred:
```php
Post::create($request->validated());
$post = Post::create($request->safe()->only(['title', 'body']));
```
## Use `Rule::when()` for Conditional Validation
Validated data is not automatically safe for mass assignment. Keep model `$fillable` or `$guarded` rules aligned with the operation, and never add a sensitive attribute to validation merely to make mass assignment convenient.
## Express Conditional Rules Clearly
Use conditional rules such as `Rule::when()`, `required_if`, or `exclude_unless` when they make the condition explicit. Choose the simplest form that remains easy to test.
```php
'company_name' => [
Rule::when($this->account_type === 'business', ['required', 'string', 'max:255']),
'string',
'max:255',
Rule::when(
$this->input('account_type') === 'business',
['required'],
['nullable'],
),
],
```
## Use the `after()` Method for Custom Validation
## Add Cross-Field Validation After Base Rules
Use `after()` instead of `withValidator()` for custom validation logic that depends on multiple fields.
Use a form request's `after()` method for validation that depends on multiple fields or application state. Avoid expensive queries when prerequisite fields have already failed validation.
```php
public function after(): array
{
return [
function (Validator $validator) {
if ($this->quantity > Product::find($this->product_id)?->stock) {
if ($validator->errors()->hasAny(['product_id', 'quantity'])) {
return;
}
$stock = Product::find($this->integer('product_id'))?->stock;
if ($stock !== null && $this->integer('quantity') > $stock) {
$validator->errors()->add('quantity', 'Not enough stock.');
}
},
];
}
```
Validation against mutable state does not prevent a race between validation and persistence. Enforce inventory, uniqueness, and similar invariants with database constraints, atomic updates, or a database transaction as appropriate.