Files
Andreas Reinhold / reiniandClaude Opus 5 92b3b3de56 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
2026-09-10 10:42:22 +02:00

2.9 KiB

Validation and Forms Best Practices

Extract Validation When It Improves the Boundary

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.

public function store(StorePostRequest $request): RedirectResponse
{
    $post = Post::create($request->validated());

    return redirect()->route('posts.show', $post);
}

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.

Prefer Readable Rule Syntax

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.

'email' => ['required', 'email', Rule::unique('users')],

String syntax remains valid for simple rules:

'email' => 'required|email|unique:users',

Use Only Intended Validated Data

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:

Post::create($request->all());

Preferred:

$post = Post::create($request->safe()->only(['title', 'body']));

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.

'company_name' => [
    'string',
    'max:255',
    Rule::when(
        $this->input('account_type') === 'business',
        ['required'],
        ['nullable'],
    ),
],

Add Cross-Field Validation After Base Rules

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.

public function after(): array
{
    return [
        function (Validator $validator) {
            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.