本文目录导读:

在 Laravel 中处理数组验证和嵌套数据是非常常见的需求,让我详细介绍各种验证技巧和最佳实践。
基础数组验证
简单数组验证
$request->validate([
'tags' => 'required|array',
'tags.*' => 'required|string|max:50',
]);
数组长度验证
$request->validate([
'items' => 'required|array|min:1|max:10',
]);
嵌套数组验证
多维数组验证
$request->validate([
'users' => 'required|array',
'users.*.name' => 'required|string|max:255',
'users.*.email' => 'required|email|unique:users,email',
'users.*.age' => 'required|integer|min:18',
'users.*.addresses' => 'sometimes|array',
'users.*.addresses.*.city' => 'required_with:users.*.addresses|string',
'users.*.addresses.*.zip' => 'required_with:users.*.addresses|digits:5',
]);
使用 FormRequest 类
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreOrderRequest extends FormRequest
{
public function rules(): array
{
return [
'customer' => 'required|array',
'customer.name' => 'required|string|max:255',
'customer.email' => 'required|email',
'items' => 'required|array|min:1',
'items.*.product_id' => 'required|exists:products,id',
'items.*.quantity' => 'required|integer|min:1|max:99',
'items.*.size' => 'nullable|in:S,M,L,XL',
'items.*.extras' => 'array',
'items.*.extras.*' => 'string|max:50',
];
}
public function messages(): array
{
return [
'items.*.quantity.min' => '每个商品至少需要订购1件',
'items.*.product_id.exists' => '商品不存在',
];
}
}
数组嵌套验证的高级技巧
条件验证
$request->validate([
'products' => 'array',
'products.*.type' => 'required|in:physical,digital',
// 物理商品需要重量
'products.*.weight' => 'required_if:products.*.type,physical|numeric|min:0.1',
// 数字商品需要下载链接
'products.*.download_url' => 'required_if:products.*.type,digital|url',
]);
自定义验证规则
Validator::make($request->all(), [
'selections' => [
'required',
'array',
function ($attribute, $value, $fail) {
// 检查数组中对值
if (count($value) !== count(array_unique($value))) {
$fail('选择项不能重复。');
}
},
],
]);
数组项之间验证
public function rules(): array
{
return [
'items' => 'required|array|min:2',
'items.*.quantity' => 'required|integer|min:1',
];
}
public function withValidator($validator): void
{
$validator->after(function ($validator) {
$items = $this->input('items');
// 检查总数量不超过库存
$totalQuantity = array_sum(array_column($items, 'quantity'));
if ($totalQuantity > 100) {
$validator->errors()->add('items', '总数量不能超过100件。');
}
});
}
数据准备与验证前处理
使用 prepareForValidation
class UpdateProfileRequest extends FormRequest
{
protected function prepareForValidation(): void
{
$this->merge([
'interests' => is_string($this->interests)
? explode(',', $this->interests)
: $this->interests,
'settings' => [
'notifications' => $this->boolean('notifications'),
'theme' => $this->input('theme', 'light'),
],
]);
}
}
验证嵌套数组的常用场景
JSON 数据验证
$request->validate([
'data' => 'required|json',
]);
// 验证 JSON 解码后的数据
$data = json_decode($request->input('data'), true);
$validator = Validator::make($data, [
'items' => 'required|array',
'items.*.name' => 'required|string',
'items.*.price' => 'required|numeric|min:0',
]);
动态字段验证
public function rules(): array
{
$rules = [
'type' => 'required|in:appointment,order,custom',
'fields' => 'required|array',
];
// 根据类型动态添加规则
if ($this->input('type') === 'appointment') {
$rules['fields.date'] = 'required|date|after:today';
$rules['fields.time'] = 'required|date_format:H:i';
$rules['fields.doctor'] = 'required|exists:doctors,id';
}
return $rules;
}
FormRequest 中的复杂验证示例
namespace App\Http\Requests;
class ProcessSurveyRequest extends FormRequest
{
public function rules(): array
{
return [
'survey_id' => 'required|exists:surveys,id',
'respondent' => 'required|array',
'respondent.age_group' => 'required|in:18-25,26-35,36-50,50+',
'respondent.gender' => 'nullable|in:male,female,other',
'answers' => 'required|array|min:1',
'answers.*.question_id' => 'required|distinct|exists:questions,id',
'answers.*.answer' => 'required_without:answers.*.answer_text',
'answers.*.answer_text' => 'required_without:answers.*.answer',
'answers.*.rating' => 'nullable|integer|between:1,5',
'optional_questions' => 'sometimes|array',
'optional_questions.*' => 'nullable|string|max:500',
];
}
public function withValidator($validator): void
{
$validator->after(function ($validator) {
// 检查是否回答了所有必答题
$survey = \App\Models\Survey::find($this->survey_id);
$requiredQuestions = $survey->questions()->where('required', true)->pluck('id')->toArray();
$answeredQuestions = array_column($this->answers, 'question_id');
$missingQuestions = array_diff($requiredQuestions, $answeredQuestions);
if (!empty($missingQuestions)) {
$validator->errors()->add(
'answers',
'请回答所有必答题: ' . implode(', ', $missingQuestions)
);
}
});
}
public function messages(): array
{
return [
'answers.*.distinct' => '每个问题只能回答一次',
'answers.*.rating.between' => '评分必须在1-5之间',
];
}
}
验证后的数据提取
// 在控制器中
public function store(Request $request)
{
$validated = $request->validate([...]);
// 提取部分数据
$userData = $request->safe()->only(['name', 'email']);
$arrayData = $request->safe()->only(['items', 'tags']);
// 排除某些字段
$data = $request->safe()->except(['items', 'tags']);
}
使用自定义规则类
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class ValidItemCombination implements Rule
{
public function passes($attribute, $value)
{
// $value 是数组
$categories = array_column($value, 'category');
$names = array_column($value, 'name');
// 检查组合是否唯一
$combinations = array_map(function($cat, $name) {
return $cat . '-' . $name;
}, $categories, $names);
return count($combinations) === count(array_unique($combinations));
}
public function message()
{
return '每个商品组合必须唯一。';
}
}
// 使用
$request->validate([
'items' => ['required', 'array', new ValidItemCombination],
]);
性能优化建议
// 避免过多嵌套层级 'items.*.details.specifications.technical.description' => 'required'; // 太深了 // 应该扁平化或拆分 'items.*.description' => 'required';
常见陷阱和解决方案
处理可选数组
// 错误方式(可能导致 null)
$request->validate([
'tags.*' => 'string',
]);
// 正确方式
$request->validate([
'tags' => 'sometimes|array',
'tags.*' => 'string',
]);
数组索引连续性
$validator = Validator::make($request->all(), [
'items' => 'required|array',
'items.*' => 'required',
]);
// 检查索引是否连续
$validator->after(function ($validator) {
$items = $this->input('items');
$keys = array_keys($items);
if ($keys !== range(0, count($items) - 1)) {
// 可以重新索引
$reindexed = array_values($items);
$this->merge(['items' => $reindexed]);
}
});
这些技巧可以帮助你处理几乎所有 Laravel 数组验证场景,关键是要根据项目实际需求选择合适的验证策略,并充分利用 Laravel 提供的验证功能。