4

Validating arrays and nested values in Laravel

 2 years ago
source link: https://blog.logrocket.com/validating-arrays-nested-values-laravel/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

With the added complexity of frontend architecture, it’s more important than ever for developers to be able to submit more complex data to the backend.

Because we are building more complicated forms, we need more effective ways to validate and process the data coming from these forms. Luckily, Laravel provides many ways in which you can easily validate the data coming from your frontend.

Basics of validation in Laravel

Before we discuss validating arrays and nested arrays, let’s do an overview of the basics of Laravel validation.

Usually, HTTP requests coming into Laravel are mostly handled in the controller (there are other places where requests are handled such as middleware, but that’s a discussion for another post). Because of this, many devs choose to house their validation methods here, too.

Let’s say we’re building a very simple inventory software. In this software, we’ll store our items in the “Products” table of our database. Our model to access and manage our table is Product and the controller will be named ProductController

In our form, we have fields for the item name, the SKU, and the price. We need to validate these items when a POST request is made.

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);
}

The code above is the simplest way we could validate requests in our controller. Notice that beside each key (attribute) is a string where pipes separate all the rules that we want to validate the attribute.

It’s amazing that, with some rules, you can provide more context as to what you want. For example, in the code block above, you will see max:255, which means that the item_name should not surpass 255 characters; regex:​​/^[a-zA-Z0-9]+$/ means we only want alphanumeric characters. These are just a few of the many rules that come pre-built into Laravel.

When the items above are validated, an HTTP redirect is made with the related error messages. However, if an XHR request is made (like ones that come from an API), a redirect will not be made but it will instead respond with JSON and a 422 HTTP status code.

Some Laravel devs choose to expand on this by using more complex methods of validation. One way they do this is by using the Validator object.

 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);
}

Another way Laravel devs expand on validation is by separating validation from the controller with the use of form requests. This is personally my favorite way to expand validation, as I am able to neatly organize everything when I make custom rules, use After Validation Hooks, or expand rules, etc.

 <?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'
        ];
    }
}

Validating simple nested attributes

Let’s say that I want to make my inventory software a bit more complicated by having an item field with two nested fields: name and description.

On the frontend, it would look something like this:

<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>

Let’s say we’re now using a form request to validate our incoming data because we want to be better at  organizing our data. The rules method will look like this:

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'
    ];
}

We can then use the validated data in our controller, like so:

public function store(ProductRequest $request)
{
    // Do something or just save straight to the db like below
   Product::create($request->validated());
}

As you can see, we denote nested attributes by using dot notation. Dot notation is also important when we want to customize error messages. For example, if we want to customize the error message when someone enters an alphabetical character in the price field, we can do something like this:

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

Note that we use the syntax ‘field [dot] rule’ when we’re making custom messages.

Validating arrays and nested attributes

Let’s say we’re making data collection even more complex by having a form with repeatable parts. For instance, we want to store different variations of our items, like items that are different colors and have different prices.

<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>

We have three iterations of our items to be validated. HTML doesn’t provide a limit on the number of array elements that you can submit in a form, so if we had to validate each individually, it would be a headache.

Luckily, Laravel provides a simple way to validate arrays and nested array input with the use of dot notation and the * character.

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'
    ];
}

The * character replaces the iteration number of the element in the array. It’s also pretty useful when we have more complex nesting going on.

Let’s say we have a months_available field and each field is a list of months that you can select. Without having to give names to our deeply nested attributes, we can validate each array of months and each month in this nested array like this:

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',
    ];
}

If we were to write custom messages for each attribute, we’ll have something that looks like this:

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.'
    ];
}

Important rules for array validation

There are some rules that are especially important, now that you are working with arrays. We’ll discuss a few of them and provide examples for each to help you understand them better.

array

This ensures that the value of the input is an array. A list can be provided as context to this rule to tell Laravel to ensure that the keys are present in the input.

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

distinct

This ensures that no element is a duplicate in the array. This is useful when you need unique values, such as IDs.

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

exclude_if, exclude_unless, exclude_without

Each rule compares the current field with another field and excludes it from the returned data based on the condition. exclude_if excludes the current field if another field is equal to a certain value, exclude_unless excludes the current field unless another field is equal to a certain value, and exclude_without excludes the current field if another field isn’t present.

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

This rule ensures that the current field is present and has data, hence it can’t be null.

sometimes

This will validate the current field only if it is present. You will use this a lot when you’re validating nested values in arrays, as there will be times when an attribute for an iteration is missing; even though other attributes are present.

This is not the opposite of required, as you can use them together. For example, because the data may have item.5.name, the validator might expect there to be an item.5.description. With sometimes, it knows that when it’s not present, it doesn’t have to worry and it won’t throw a nasty exception.

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

Conclusion

Even though we covered a lot, there is still so much more that you can do with validating nested data in Laravel. Not only does it provide ways to validate your data, but also ways for you to make your own custom rules for validating data.

Inventory systems are not the only examples that will provide complex nested data to the backend for validation, as some websites with multi-page forms fall into this category, as well as software that allows users to build webpages and other digital items with repeatable modules and blocks.

For more amazing things that you can do with Laravel validation, see the Laravel docs.

LogRocket: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

Try it for free.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK