laravel-validation-patterns について
Laravel Validation Patterns は、Laravel のリクエスト検証を実装する開発者向けのリファレンススキルです。Form Request、カスタムルール、条件付き・ネスト検証、データベースルール、検証済みデータの安全な利用を扱います。
主な機能
- 01Form Request クラスのパターン
- 02再利用可能なカスタムルールオブジェクト
- 03条件付き・ネスト検証
- 04データベースと enum のルール
- 05検証済みデータへの安全なアクセス
活用シーン
- 01検証処理をコントローラー外に構成
- 02ネストしたリクエスト配列の検証
- 03スコープ付き一意性チェックの適用
- 04コントローラーで検証済み入力のみを使用
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ iserter/ laravel-claude-agents --skill laravel-validation-patternsインストール後、Claude Code で/laravel-validation-patternsを入力して読み込まれたか確認します。
詳細
Laravel のバリデーションパターン
Form Request クラス(標準的な方法)
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreOrderRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()->can('create', Order::class);
}
public function rules(): array
{
return [
'customer_id' => ['required', 'exists:customers,id'],
'items' => ['required', 'array', 'min:1'],
'items.*.product_id' => ['required', 'exists:products,id'],
'items.*.quantity' => ['required', 'integer', 'min:1'],
'notes' => ['nullable', 'string', 'max:500'],
];
}
public function messages(): array
{
return [
'items.required' => 'At least one item is required.',
'items.*.product_id.exists' => 'Product #:position does not exist.',
];
}
protected function prepareForValidation(): void
{
$this->merge([
'notes' => strip_tags($this->notes),
'email' => strtolower($this->email),
]);
}
public function after(): array
{
return [
function (\Illuminate\Validation\Validator $validator) {
if ($this->hasExceededOrderLimit()) {
$validator->errors()->add('items', 'You have exceeded the daily order limit.');
}
},
];
}
}
カスタム Rule オブジェクト
<?php
namespace App\Rules;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
class StrongPassword implements ValidationRule
{
public function validate(string $attribute, mixed $value, Closure $fail): void
{
if (! preg_match('/[A-Z]/', $value)) {
$fail('The :attribute must contain at least one uppercase letter.');
}
if (! preg_match('/[0-9]/', $value)) {
$fail('The :attribute must contain at least one number.');
}
}
}
// Usage in rules
'password' => ['required', 'string', 'min:8', new StrongPassword],
条件付きバリデーション
public function rules(): array
{
return [
'type' => ['required', Rule::in(['individual', 'company'])],
// Required only when type is company
'company_name' => ['required_if:type,company', 'string', 'max:255'],
// Excluded when type is individual (not present in validated data)
'tax_id' => ['exclude_if:type,individual', 'required', 'string'],
// Dynamic conditional rule
'billing_address' => [
Rule::when($this->boolean('different_billing'), ['required', 'string']),
],
// Conditional with sometimes (only validates if field is present)
'coupon_code' => ['sometimes', 'string', 'exists:coupons,code'],
];
}
配列とネストされたバリデーション
public function rules(): array
{
return [
'tags' => ['required', 'array', 'min:1', 'max:10'],
'tags.*' => ['string', 'max:50'],
'items' => ['required', 'array'],
'items.*.name' => ['required', 'string'],
'items.*.options' => ['sometimes', 'array'],
'items.*.options.*.key' => ['required_with:items.*.options', 'string'],
];
}
データベースルール
public function rules(): array
{
return [
// Unique with ignore (for updates)
'email' => [
'required',
'email',
Rule::unique('users')->ignore($this->user()),
],
// Unique with scoping
'slug' => [
'required',
Rule::unique('posts')->where('tenant_id', $this->user()->tenant_id),
],
// Exists with additional constraints
'category_id' => [
'required',
Rule::exists('categories', 'id')->where('active', true),
],
];
}
Enum バリデーション
use App\Enums\OrderStatus;
public function rules(): array
{
return [
'status' => ['required', Rule::enum(OrderStatus::class)],
// Only allow specific enum values
'priority' => [
'required',
Rule::enum(Priority::class)->only([Priority::High, Priority::Critical]),
],
];
}
バリデーション済みデータの操作
// In controller
public function store(StoreOrderRequest $request)
{
// ✅ Use validated data only
$validated = $request->validated();
// ✅ Use safe() for partial access
$orderData = $request->safe()->only(['customer_id', 'notes']);
$items = $request->safe()->except(['notes']);
// ✅ Merge additional trusted data
$order = Order::create(
$request->safe()->merge(['user_id' => $request->user()->id])->all()
);
// ❌ Never use unvalidated input
$order = Order::create($request->all());
// ❌ Never bypass validation
$order = Order::create($request->input());
}
よくある落とし穴
// ❌ Validating inline in controllers
public function store(Request $request)
{
$request->validate(['title' => 'required']);
// Hard to test, not reusable
}
// ✅ Use Form Request classes
public function store(StorePostRequest $request)
{
Post::create($request->validated());
}
// ❌ Missing bail - continues validating after first failure
'email' => ['email', 'unique:users', 'dns_check'],
// ✅ Use bail to stop on first failure
'email' => ['bail', 'email', 'unique:users'],
// ❌ Using $request->all() instead of validated data
Order::create($request->all());
// ✅ Only validated and safe data
Order::create($request->validated());
チェックリスト
- バリデーションロジックはコントローラーではなく、Form Request クラスに記述します
- authorize() メソッドで権限を適切に確認します
- 再利用可能な複雑なバリデーションにはカスタム Rule オブジェクトを使用します
- prepareForValidation() でバリデーション前に入力をサニタイズします
- フィールド間やビジネスロジックのバリデーションには after() を使用します
- 配列やネストされたフィールドはワイルドカード表記でバリデーションします
- データベースルールでは適切なスコープ指定と除外パターンを使用します
- モデルの作成または更新には、バリデーション済み/安全なデータのみを使用します
- 早期終了が必要な箇所では bail を使用します
- ユーザーに表示するフィールドにはカスタムエラーメッセージを用意します
コメント
Markdown 対応