Skip to main content

Validation Detection Guide

This guide details Laravel Spectrum's powerful validation detection capabilities. It automatically detects and documents various validation patterns including FormRequest classes, inline validation, and conditional validation.

📋 Supported Validation Methods

1. FormRequest Classes

The most recommended approach. Laravel Spectrum fully analyzes FormRequest.

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateUserRequest extends FormRequest
{
public function authorize()
{
return true;
}

public function rules()
{
return [
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:8|confirmed',
'age' => 'nullable|integer|min:0|max:150',
'role' => 'required|in:admin,editor,viewer',
'profile_image' => 'nullable|image|mimes:jpeg,png|max:2048',
];
}

public function attributes()
{
return [
'name' => 'User Name',
'email' => 'Email Address',
'password' => 'Password',
];
}

public function messages()
{
return [
'name.required' => 'User name is required.',
'email.unique' => 'This email address is already in use.',
];
}
}

2. Inline Validation

The validate() method within controllers is also detected.

public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
'published_at' => 'nullable|date',
'tags' => 'array',
'tags.*' => 'string|max:50',
]);

// ...
}

3. Validator Facade

public function update(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'name' => 'sometimes|required|string|max:255',
'status' => 'sometimes|required|in:active,inactive',
]);

if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}

// ...
}

🎯 Advanced Validation Features

Arrays and Nested Data

public function rules()
{
return [
'users' => 'required|array|min:1',
'users.*.name' => 'required|string',
'users.*.email' => 'required|email|distinct',
'users.*.profile' => 'required|array',
'users.*.profile.bio' => 'nullable|string|max:500',
'users.*.profile.avatar' => 'nullable|url',

'settings' => 'required|array',
'settings.notifications' => 'required|array',
'settings.notifications.email' => 'boolean',
'settings.notifications.push' => 'boolean',
];
}

Generated OpenAPI schema:

{
"users": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["name", "email", "profile"],
"properties": {
"name": { "type": "string" },
"email": { "type": "string", "format": "email" },
"profile": {
"type": "object",
"properties": {
"bio": { "type": "string", "maxLength": 500 },
"avatar": { "type": "string", "format": "uri" }
}
}
}
}
}
}

File Uploads

public function rules()
{
return [
'avatar' => 'required|image|mimes:jpeg,png,jpg|max:2048|dimensions:min_width=100,min_height=100',
'document' => 'required|file|mimes:pdf,doc,docx|max:10240',
'gallery' => 'required|array|max:5',
'gallery.*' => 'image|mimes:jpeg,png|max:1024',
];
}

Properly documented with multipart/form-data encoding.

Custom Validation Rules

use App\Rules\PhoneNumber;
use Illuminate\Validation\Rule;

public function rules()
{
return [
'phone' => ['required', new PhoneNumber],
'country' => ['required', Rule::in(['JP', 'US', 'UK'])],
'username' => [
'required',
'string',
'max:30',
Rule::unique('users')->ignore($this->user),
],
];
}

Enum Type Detection

use App\Enums\UserStatus;
use App\Enums\UserRole;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;

public function rules()
{
return [
// Using Rule::enum
'status' => ['required', Rule::enum(UserStatus::class)],

// Using Enum rule class
'role' => ['required', new Enum(UserRole::class)],

// String-based in validation also supported
'priority' => 'required|in:low,medium,high,urgent',
];
}

⚡ Conditional Validation

Based on HTTP Method

public function rules()
{
$rules = [
'name' => 'required|string|max:255',
'email' => 'required|email',
];

if ($this->isMethod('POST')) {
$rules['password'] = 'required|string|min:8|confirmed';
$rules['email'] .= '|unique:users';
} elseif ($this->isMethod('PUT') || $this->isMethod('PATCH')) {
$rules['password'] = 'sometimes|nullable|string|min:8|confirmed';
$rules['email'] .= '|unique:users,email,' . $this->route('user');
}

return $rules;
}

Based on Other Fields

public function rules()
{
return [
'type' => 'required|in:personal,business',
'first_name' => 'required_if:type,personal|string|max:255',
'last_name' => 'required_if:type,personal|string|max:255',
'company_name' => 'required_if:type,business|string|max:255',
'tax_id' => 'required_if:type,business|string|regex:/^[A-Z0-9\-]+$/',

'subscribe_newsletter' => 'boolean',
'email_frequency' => 'required_if:subscribe_newsletter,true|in:daily,weekly,monthly',
];
}

Using Rule::when

use Illuminate\Validation\Rule;

public function rules()
{
return [
'account_type' => 'required|in:free,premium,enterprise',

'payment_method' => Rule::when(
in_array($this->account_type, ['premium', 'enterprise']),
'required|in:credit_card,paypal,bank_transfer',
'nullable'
),

'billing_address' => Rule::when(
fn() => $this->account_type !== 'free' && $this->payment_method === 'credit_card',
'required|array',
'nullable'
),
];
}

Complex Conditional Validation

public function rules()
{
$rules = [
'event_type' => 'required|in:online,offline,hybrid',
'location' => 'required_unless:event_type,online|string',
'streaming_url' => 'required_if:event_type,online,hybrid|url',
'max_attendees' => 'required|integer|min:1',
];

// Additional rules based on conditions
if ($this->event_type === 'offline' || $this->event_type === 'hybrid') {
$rules['venue_capacity'] = 'required|integer|gte:max_attendees';
$rules['safety_measures'] = 'required|array';
}

return $rules;
}

🔧 Type Inference and Schema Generation

Type Inference from Validation Rules

Validation RuleInferred TypeOpenAPI Schema
stringstring{ "type": "string" }
integerinteger{ "type": "integer" }
numericnumber{ "type": "number" }
booleanboolean{ "type": "boolean" }
arrayarray{ "type": "array" }
datestring{ "type": "string", "format": "date" }
emailstring{ "type": "string", "format": "email" }
urlstring{ "type": "string", "format": "uri" }
file/imagefile{ "type": "string", "format": "binary" }

Constraint Conversion

Validation RuleOpenAPI Constraint
min:3{ "minimum": 3 } (numeric) / { "minLength": 3 } (string)
max:100{ "maximum": 100 } (numeric) / { "maxLength": 100 } (string)
between:1,10{ "minimum": 1, "maximum": 10 }
size:5{ "minItems": 5, "maxItems": 5 } (array)
in:a,b,c{ "enum": ["a", "b", "c"] }
regex:/pattern/{ "pattern": "pattern" }

💡 Best Practices

1. Use FormRequest

// ✅ Recommended
public function store(CreateUserRequest $request)
{
$user = User::create($request->validated());
return new UserResource($user);
}

// ❌ Not recommended (but works)
public function store(Request $request)
{
$validated = $request->validate([...]);
}

2. Explicit Type Specification

public function rules()
{
return [
// ✅ Clear types
'age' => 'required|integer|min:0|max:150',
'price' => 'required|numeric|min:0',
'is_active' => 'required|boolean',

// ❌ Ambiguous type
'value' => 'required',
];
}

3. Meaningful Validation Messages

public function messages()
{
return [
'email.unique' => 'This email address is already registered.',
'password.min' => 'Password must be at least :min characters.',
'age.between' => 'Age must be between :min and :max years.',
];
}

public function attributes()
{
return [
'email' => 'Email Address',
'password' => 'Password',
'age' => 'Age',
];
}

4. Validation Rule Reuse

// Define common validation rules
trait CommonValidationRules
{
protected function passwordRules(): array
{
return ['required', 'string', 'min:8', 'regex:/[A-Z]/', 'regex:/[0-9]/'];
}

protected function emailRules(bool $unique = true): array
{
$rules = ['required', 'email', 'max:255'];

if ($unique) {
$rules[] = 'unique:users,email';
}

return $rules;
}
}

🔍 Troubleshooting

When Validation Is Not Detected

  1. Check FormRequest's authorize() method

    public function authorize()
    {
    return true; // false prevents analysis
    }
  2. Check namespace and use statements

    use Illuminate\Foundation\Http\FormRequest; // Required
  3. Check type hints

    // ✅ Correct
    public function store(CreateUserRequest $request)

    // ❌ Not detected
    public function store($request)

When Conditional Validation Is Not Displayed Correctly

  1. Clear cache

    php artisan spectrum:cache clear
  2. Enable conditional analysis

    php artisan spectrum:generate --analyze-conditions