You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel数组元素验证问题:Pickings数组location唯一性校验报错

问题

需要实现pickings数组中location字段的唯一性校验,请求数据结构如下:

{"id":10,"type":"picking","pickings":[{"id":1,"warehouse_id":10,"type":"mobile","location":"testing","created_at":"2023-07-12T03:29:06.000000Z","updated_at":"2023-07-12T03:29:06.000000Z"},{"id":2,"warehouse_id":10,"type":"fixed","location":"testing 1","created_at":"2023-07-12T03:29:06.000000Z","updated_at":"2023-07-12T03:29:06.000000Z"}]}

在请求类中使用了如下验证规则:

public function rules()
{
return [
        'pickings.*.location' => [
                    Rule::unique('warehouse_pickings')
                        ->where('warehouse_id', $this->input('id'))
                        ->ignore($this->input('pickings.*.id'), 'id')
                ]
       ];
}

触发了如下错误:

exception: "TypeError"
file: "/Users/shahanurmdsharif/development/php/ash/tag/trunk/vendor/laravel/framework/src/Illuminate/Validation/Rules/Unique.php"
line: 70
message: "addslashes(): Argument #1 ($string) must be of type string, array given"

需要获取当前验证的pickings元素对应的id值,如何获取当前正在测试的元素索引对应的id?

解决方案

错误原因是$this->input('pickings.*.id')会返回整个pickings数组的所有id组成的数组,而ignore()方法要求传入单个字符串/数值类型的id。要获取当前验证元素的对应id,核心是解析出当前元素在数组中的索引,再通过索引获取对应id,以下是两种可行方案:

方案一:使用闭包手动校验

直接通过闭包解析属性名获取索引,手动执行数据库查询进行唯一性校验:

public function rules()
{
    return [
        'pickings.*.location' => function ($attribute, $value, $fail) {
            // 从属性字符串中拆分出当前元素的索引,例:从"pickings.0.location"得到索引0
            $index = explode('.', $attribute)[1];
            // 获取当前pickings元素的id
            $currentPickingId = $this->input("pickings.$index.id");
            $warehouseId = $this->input('id');

            // 查询数据库是否存在重复记录
            $hasDuplicate = \DB::table('warehouse_pickings')
                ->where('location', $value)
                ->where('warehouse_id', $warehouseId)
                // 如果是编辑场景,排除当前记录
                ->when($currentPickingId, fn($query) => $query->where('id', '<>', $currentPickingId))
                ->exists();

            if ($hasDuplicate) {
                $fail('该位置已被占用');
            }
        }
    ];
}

方案二:结合Rule::unique动态设置ignore参数

利用闭包解析索引后,动态给Rule::unique()设置ignore参数:

use Illuminate\Validation\Rule;

public function rules()
{
    return [
        'pickings.*.location' => [
            function ($attribute, $value, $fail) {
                $index = explode('.', $attribute)[1];
                $currentId = $this->input("pickings.$index.id");

                // 构建唯一性规则
                $uniqueRule = Rule::unique('warehouse_pickings')
                    ->where('warehouse_id', $this->input('id'));

                // 如果存在当前id,排除该记录
                if ($currentId) {
                    $uniqueRule->ignore($currentId);
                }

                // 用临时验证器校验当前字段
                $validator = \Validator::make([$attribute => $value], [$attribute => $uniqueRule]);
                if ($validator->fails()) {
                    $fail($validator->errors()->first($attribute));
                }
            }
        ]
    ];
}

核心逻辑总结

  • 通过拆分验证属性名(如pickings.0.location)提取出当前元素的索引
  • 利用索引从pickings数组中获取对应元素的id
  • 将单个id传入ignore()方法,避免传入数组导致类型错误

内容的提问来源于stack exchange,提问作者Sharif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 10:12:20