---
model_tier: medium
name: laravel-validation
description: "Use when writing validation — Form Requests, rules, custom rule objects, request-boundary design — even when the user just says 'validate this input' or 'check the request' without naming it."
domain: engineering
workspaces:
  - engineering
packs:
  - laravel
trust:
  level: professional
install:
  default: false
  removable: true
---

# laravel-validation

## When to use

Use when creating FormRequests, validation rules, or custom rule objects.

Do NOT use when:
- Authorization logic only (use `security` skill)
- API response format (use `api-design` skill)

## Procedure: Create a FormRequest

### Step 0: Inspect

1. Check existing FormRequests — match style, naming, authorization, rule structure.
2. Check existing custom rules — reuse before creating new ones.
3. Check API error response format — match it.

### Step 1: Create the class

1. Name: `{Action}{Entity}Request` — e.g. `CreateProjectRequest`.
2. Implement `authorize()` using Policies.
3. Implement `rules()` with array syntax — never pipe-separated.
4. Use `prepareForValidation()` only when normalization is truly necessary.

### Step 2: Rules

1. Use Laravel's `Illuminate\Validation\Rules` classes where possible.
2. Keep rules explicit — prefer clarity over cleverness.
3. For nested arrays: validate structure AND leaf values.
4. Extract custom rule objects for complex/reusable logic.

### Step 3: Test

- Test required fields, invalid formats, boundary conditions, conditional rules.
- Test authorization where relevant.
- Use focused tests per validation concern.

## Conventions

→ See guideline `php/validations.md` for array syntax, route params, property mapping, custom rules.

## Output format

1. FormRequest class with rules, messages, and authorization
2. Custom Rule object if validation logic is complex

## Gotcha

- `required` ≠ not `nullable` — `required` means present, `nullable` means value can be null.
- Custom Rule objects must return `$fail` callback, not throw exceptions.
- Don't validate data you already trust (e.g., from a verified internal service).

## Do NOT

- Do NOT validate in controllers — use FormRequest classes.
- Do NOT use `$request->all()` — use `$request->validated()`.
- Do NOT put business logic in validation classes.

## Verification

Confirm new rules with a concrete probe: a Pest feature test that POSTs with `curl`-shaped payloads through the browser/HTTP client, or a `phpunit`/`pest` run against the FormRequest. Add at least one negative case per rule (missing, wrong type, boundary). Never claim rules work without running them.

## Anti-bruteforce — diagnose before retry

When a rule fires unexpectedly, do not blindly toggle `required` / `nullable` / `sometimes` until tests pass. Print `$request->all()` once, compare against the rule set, identify the root cause, then make a targeted fix.

## Clarification guard — ambiguous field → ask

If a field's intent is unclear (is `tags` a CSV string, an array, or JSON? is `email` required at create or only at update?), ask the user or check the existing schema / API contract before guessing. Hidden assumptions in validation rules surface as production 422s.

## Auto-trigger keywords

- validation
- Form Request
- validation rules
- custom rule
