在 Laravel 中验证数组和嵌套值

随着前端架构的复杂性增加,开发人员能够向后端提交更复杂的数据比以往任何时候都更加重要。

因为我们正在构建更复杂的表单,所以我们需要更有效的方法来验证和处理来自这些表单的数据。 幸运的是,Laravel 提供了许多方法让我们可以轻松地验证来自前端的数据。

  • Laravel 中的验证基础知识
  • 验证简单的嵌套属性
  • 验证数组和嵌套属性
  • 数组验证的重要规则

下面我们就分别来看一下吧


Laravel 中的验证基础知识

在讨论验证数组和嵌套数组之前,让我们先概述一下 Laravel 验证的基础知识。

通常,进入 Laravel 的 HTTP 请求主要在控制器中处理(还有其他地方处理请求,例如中间件,但这是另一篇文章的讨论)。 正因为如此,许多开发人员也选择在这里存放他们的验证方法。

假设我们正在构建一个非常简单的库存软件。 在这个软件中,我们将我们的项目存储在我们数据库的“product”表中。 我们访问和管理表的模型是 Product,控制器将命名为 ProductController

在我们的表单中,我们有 item_name、SKU 和 price 的字段。 我们需要在发出 POST 请求时验证这些字段。

public function store(Request $request)
{
    $validated = $request->validate([
        'item_name' => 'required|string|max:255',
        'sku' => 'required|string|regex:​​/^[a-zA-Z0-9]+$/',
        'price' => 'required|numeric'
    ]);

    Product::create($validated);
}

上面的代码是我们可以在控制器中验证请求的最简单方法。 请注意,每个key(属性)旁边是一个字符串,其中管道分隔我们要验证属性的所有规则。

令人惊奇的是,通过一些规则,我们可以提供更多关于我们想要的内容的上下文。 例如,在上面的代码块中,我们将看到 max:255,这意味着 item_name 不应超过 255 个字符; regex:/^[a-zA-Z0-9]+$/ 表示我们只需要字母数字字符。 这些只是 Laravel 中预置的众多规则中的一小部分。

验证上述项目后,将使用相关的错误消息进行 HTTP 重定向。 但是,如果发出 XHR 请求(例如来自 API 的请求),则不会进行重定向,而是会使用 JSON 和 422 HTTP 状态代码进行响应。

一些 Laravel 开发人员选择通过使用更复杂的验证方法来扩展此功能。 他们这样做的一种方法是使用 Validator 对象。

public function store(Request $request)
{
    $validator = Validator::make($request->all(), [
        'item_name' => 'required|string|max:255',
        'sku' => 'required|string|regex:​​/^[a-zA-Z0-9]+$/',
        'price' => 'required|numeric'
    ]);

    If ($validator->fails()){
        // Do something
    }
    Product::create($validated);
}

Laravel 开发人员扩展验证的另一种方式是使用表单请求将验证与控制器分离。 这是我个人最喜欢扩展验证的方式,因为当我制定自定义规则、使用 After Validation Hooks 或扩展规则等时,我能够整齐地组织所有内容。

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

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

    public function rules()
    {
        return [
            'item_name' => 'required|string|max:255',
            'sku' => 'required|string|regex:/^[a-zA-Z0-9]+$/',
            'price' => 'required|numeric'
        ];
    }
}

验证简单的嵌套属性

假设我想让我的库存软件更复杂一点,让项目字段包含两个嵌套字段:name 和 description

在前端,它看起来像这样:

<form method="POST">
    <input type="text" name="item['name']" />
    <input type="text" name="item['description']" />
    <input type="text" name="sku" />
    <input type="text" name="price" />
    <button type="submit">Submit</button>
</form>

假设我们现在使用表单请求来验证传入的数据,因为我们希望更好地组织数据。 规则方法将如下所示:

public function rules()
{
    return [
        'item.name' => 'required|string|max:255',
        'item.description' => 'nullable|string|min:60',
        'sku' => 'required|string|regex:​​/^[a-zA-Z0-9]+$/',
        'price' => 'required|numeric'
    ];
}

然后我们可以在控制器中使用经过验证的数据,如下所示:

public function store(ProductRequest $request)
{
    // 做一些事情或直接保存到数据库,如下所示
   Product::create($request->validated());
}

如你所见,我们使用点 . 符号表示嵌套属性。 当我们想要自定义错误消息时,点表示法也很重要。 例如,如果我们想在有人在 price 字段中输入字母字符时自定义错误消息,我们可以这样做:

public function messages()
{
    return [
        'price.required' => 'You must have a price.',
        'price.numeric' => 'You have invalid characters in the price field'
    ];
}

请注意,我们在制作自定义消息时使用语法“field [dot] rule”。


验证数组和嵌套属性

假设我们通过具有可重复部分的表单使数据收集变得更加复杂。 例如,我们想要存储我们项目的不同变体,比如不同颜色和不同价格的项目。

<form method="POST">
    <label>Item 1</label>
    <input type="text" name="item[0][name]" />
    <input type="text" name="item[0][description]" />
    <input type="text" name="sku[0]" />
    <input type="text" name="price[0]" />
    <button type="submit">Submit</button>

    <label>Item 2</label>
    <input type="text" name="item[1][name]" />
    <input type="text" name="item[1][description]" />
    <input type="text" name="sku[1]" />
    <input type="text" name="price[1]" />
    <button type="submit">Submit</button>

    <label>Item 3</label>
    <input type="text" name="item[2][name]" />
    <input type="text" name="item[2][description]" />
    <input type="text" name="sku[2]" />
    <input type="text" name="price[2]" />
    <button type="submit">Submit</button>
</form>

我们要验证我们的项目的三个迭代。 HTML 没有限制我们可以在表单中提交的数组元素的数量,因此如果我们必须单独验证每个元素,那将是一件令人头疼的事情。

幸运的是,Laravel 提供了一种使用点符号和 * 字符来验证数组和嵌套数组输入的简单方法。

public function rules()
{
    return [
        'item.*.name' => 'required|string|max:255',
        'item.*.description' => 'sometimes|nullable|string|min:60',
        'sku' => 'required|array',
        'sku.*' => 'sometimes|required|string|regex:​​/^[a-zA-Z0-9]+$/',
        'sku' => 'required|array',
        'price.*' => 'sometimes|required|numeric'
    ];
}

* 字符替换数组中元素的迭代次数。 当我们进行更复杂的嵌套时,它也非常有用。

假设我们有一个 months_available 字段,每个字段都是可以选择的月份列表。 无需为我们深层嵌套的属性命名,我们可以验证每个月份数组和这个嵌套数组中的每个月份,如下所示:

public function rules()
{
    return [
        'item.*.name' => 'required|string|max:255',
        'item.*.description' => 'sometimes|nullable|string|min:60',
        'sku' => 'required|array',
        'sku.*' => 'sometimes|required|string|regex:​​/^[a-zA-Z0-9]+$/',
        'sku' => 'required|array',
        'price.*' => 'sometimes|required|numeric',
        'months_available' => 'required|array',
        'months_available.*' => 'sometimes|required|array',
        'months_available.*.*' => 'sometimes|required|string',
    ];
}

如果我们要为每个属性编写自定义消息,我们将得到如下所示的内容:

public function messages()
{
    return [
        'item.*.name.required' => 'You must have an item name.',
        'item.*.name.max' => 'The item name must not surpass 255 characters.',
        'item.*.description.min' => 'The description must have a minimum of 60 characters.',
        'sku.*.regex' => 'The SKU must only have alphanumeric characters.',
        'price.*.numeric' => 'You have invalid characters in the price field.'
    ];
}

数组验证的重要规则

既然你正在使用数组,那么有些规则尤其重要。 我们将讨论其中的一些,并为每个提供示例,从而帮助大家更好地理解它们。

array

这确保了输入的值是一个数组。 可以提供一个列表作为此规则的上下文,以告诉 Laravel 确保输入中存在键。

public function rules()
{
   return [
        'item' => 'array:name', // name must be present in input
   ];
}

distinct

这确保了数组中没有元素是重复的。 当我们需要唯一值(例如 ID)时,这很有用。

public function rules()
{
    return [
        'item.*.id' => 'distinct', 
    ];
}

exclude_ifexclude_unlessexclude_without

每个规则将当前字段与另一个字段进行比较,并根据条件将其从返回的数据中排除。 exclude_if 如果另一个字段等于某个值,则排除当前字段, exclude_unless 排除当前字段,除非另一个字段等于某个值,如果另一个字段不存在,则 exclude_without 排除当前字段。

public function rules()
{
    return [
        'tag' => 'exclude_if:product_type,"digital"|required|array', // 
        'item_code' => 'exclude_unless:sku,null|required|array',
        'discount' => 'exclude_without:price|sometimes|array'
    ];
}

required

此规则确保当前字段存在并且有数据,因此它不能为空。

sometimes

这将仅在当前字段存在时验证当前字段。 当验证数组中的嵌套值时,将会经常使用它,因为有时会丢失迭代的属性; 即使存在其他属性。

这与 required 并不相反,因为我们可以将它们一起使用。 例如,因为数据可能有 item.5.name,验证器可能期望有 item.5.description。 有时,它知道当它不存在时,它不必担心,也不会抛出一个讨厌的异常。

public function rules()
{
    return [
       ‘item.*.name’ => ‘required|string|max:255’,
       ‘item.*.description’ => ‘sometimes|nullable|string|min:60’,
    ];
}

总结

尽管我们涵盖了很多内容,但在 Laravel 中验证嵌套数据还有很多事情要做。 它不仅提供了验证数据的方法,还提供了为验证数据制定自己的自定义规则的方法。

库存系统并不是唯一向后端提供复杂嵌套数据进行验证的示例,因为一些具有多页表单的网站以及允许用户使用可重复模块和其他代码块数字项目构建网页和其他数字项目的软件属于这一类。