如何在Laravel FormRequest验证中获取其他字段的值
仓库物料出库验证中获取对应物料ID的方法
问题背景
我有仓库物料出库的输入数据如下:
[ "movement_date" => "2022-09-08", "type" => "1", "transaction_number" => "OUTBOUND-001", "remarks" => "Moved to another warehouse", "items" => [ 0 => [ "material_id" => "47", "uom" => "Kilogram", "qty" => "1000", ], 1 => [ "material_id" => "16", "uom" => "Kilogram", "qty" => "500", ], ], ]
我尝试在FormRequest中检查每个物料的剩余库存,代码如下:
return [ 'type' => 'required', 'movement_date' => ['required','date', function ($attribute, $value, $fail) { if ($value > Carbon::now()->format('Y-m-d')) $fail('Invalid date '.Carbon::now()->format('Y-m-d')); }], 'items' => 'required|array', 'items.*.material_id' => 'required', 'items.*.qty' => ['required', function ($attribute, $value, $fail) { $stock = \App\Models\MaterialStock::where('material_id', 'I need material id from material_id field here')->first(); $stock === null ? $balance = 0 : $balance = $stock->qty; if ($balance < $value) $fail('Insufficient stock'); }], ];
我的问题是:在验证qty字段时,如何获取对应的material_id值?
解决方案
方法一:通过验证闭包的$validator参数获取
验证闭包支持第四个参数$validator,它包含所有请求数据。你可以从字段名中提取索引,再通过索引获取对应item的material_id:
'items.*.qty' => ['required', function ($attribute, $value, $fail, $validator) { // 拆分字段名,比如从"items.0.qty"中提取索引0 $parts = explode('.', $attribute); $index = $parts[1]; // 获取当前item的material_id $materialId = $validator->getData()['items'][$index]['material_id']; $stock = \App\Models\MaterialStock::where('material_id', $materialId)->first(); $balance = $stock ? $stock->qty : 0; if ($balance < $value) { $fail("物料ID {$materialId} 的库存不足,当前剩余 {$balance},请求出库 {$value}"); } }],
方法二:使用自定义验证规则(复用性更强)
创建一个自定义验证规则类,专门处理库存校验:
- 生成规则类:
php artisan make:rule StockAvailable
- 编写规则逻辑:
namespace App\Rules; use Closure; use Illuminate\Contracts\Validation\ValidationRule; use App\Models\MaterialStock; class StockAvailable implements ValidationRule { protected $materialId; public function __construct($materialId) { $this->materialId = $materialId; } public function validate(string $attribute, mixed $value, Closure $fail): void { $stock = MaterialStock::where('material_id', $this->materialId)->first(); $balance = $stock ? $stock->qty : 0; if ($balance < $value) { $fail("物料ID {$this->materialId} 的库存不足,当前剩余 {$balance},请求出库 {$value}"); } } }
- 在FormRequest中使用:
use App\Rules\StockAvailable; // ... return [ // 其他验证规则... 'items' => 'required|array', 'items.*.material_id' => 'required', 'items.*.qty' => ['required', function ($attribute, $value, $fail, $validator) { $parts = explode('.', $attribute); $index = $parts[1]; $materialId = $validator->getData()['items'][$index]['material_id']; // 应用自定义规则 (new StockAvailable($materialId))->validate($attribute, $value, $fail); }], ];
方法三:使用withValidator批量验证(性能更优)
通过FormRequest的withValidator方法,在验证完成后批量处理库存校验,还能减少数据库查询次数:
protected function withValidator($validator) { $validator->after(function ($validator) { $items = $validator->getData()['items'] ?? []; if (empty($items)) return; // 收集所有物料ID,批量查询库存 $materialIds = array_column($items, 'material_id'); $stockMap = \App\Models\MaterialStock::whereIn('material_id', $materialIds) ->pluck('qty', 'material_id') ->toArray(); foreach ($items as $index => $item) { $materialId = $item['material_id']; $qty = $item['qty']; $balance = $stockMap[$materialId] ?? 0; if ($balance < $qty) { $validator->errors()->add("items.{$index}.qty", "物料ID {$materialId} 的库存不足,当前剩余 {$balance},请求出库 {$qty}"); } } }); }
这种方法只需要一次数据库查询,比逐个查询效率更高,适合批量出库场景。
内容的提问来源于stack exchange,提问作者owf
相关产品推荐
相关产品推荐

