How to Validate Complex Arrays and Nested Data in Laravel

In Laravel, the popular PHP framework, data validation plays a crucial role in ensuring the integrity and reliability of your application. While validating simple input fields is straightforward, dealing with arrays and nested values can be a bit more complex. In this tutorial, we’ll explore how to validate arrays and nested values in Laravel validator, using practical examples to help you grasp the concepts effectively.

In many scenarios, we have encountered form submissions that include dynamic fields or nested data structures. For instance, consider an online shopping cart where a user can add multiple products with varying quantities. Validating such an array of products and their respective quantities becomes essential to maintain data consistency and prevent any potential issues.

Why is Array Validation in Laravel Important?

In contemporary web development, it has become a rarity that data is flat; whether you are working on a JSON API, dealing with multi-row form submissions, or validating nested configuration settings, data is almost always an array. Effective Laravel array validation is essential in ensuring data integrity, as it guarantees that each and every item, from email lists to product IDs, is in the format you expect.

In addition to simple validation, dot notation, and wildcards help to eliminate typical runtime errors such as Undefined index and improve the security of your application by preventing malicious mass-assignment attacks. In the end, validating nested data enables you to offer your users a superior experience with item-specific error messages that allow them to correct their errors instantly.

Validating Array Data in Laravel 12

Let’s say we have an e-commerce platform where users can submit an order with multiple products. Each product consists of a name, price, and quantity. We want to ensure that all the products have a valid name, a positive price, and a non-zero quantity.

Creating Request for Validation

Let’s start by creating a validation request and adding logic to it. Open the terminal and enter the below command in it:

php artisan make:request ProductStoreRequest

This will create a new file in the app/Http/Requests directory. Let’s modify it as per the example:

<?php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreOrderRequest extends FormRequest
{
    public function rules()
    {
        return [
            'products' => 'required|array',
            'products.*.name' => 'required|string',
            'products.*.price' => 'required|numeric|min:0',
            'products.*.quantity' => 'required|integer|min:1',
        ];
    }
}

Here, we have defined rules for our validation. First of all, we have checked the request has a product key and it must be an array. After that, we will validate sub-elements of the array like the name’s value is required and the data type for the name will be a string. Price and quantity need to be numeric and we have also set minimum values for both.

Implementing the Request Class in the Controller

Next, we will utilize the StoreOrderRequest class in our controller to handle the validation and processing of the submitted data.

Let’s assume we have an OrderController with a store method to handle the order submission. Let’s modify it for storing further:

<?php
namespace App\Http\Controllers;

use App\Http\Requests\StoreOrderRequest;

class OrderController extends Controller
{
    public function store(StoreOrderRequest $request)
    {
        $validatedData = $request->validated();

        foreach ($validatedData['products'] as $productData) {
            Order::create($productData);
        }

        return back();
    }
}

Here, we have bound request class into our store method. The request will handle validation and if validation fails it will return with validation errors and if everything goes well then execute the code further.  If validation passes the loop inserted it into the Order model.

When validation fails, Laravel automatically redirects back to the previous page with the errors available for display. You can access these errors in your views or controllers to provide meaningful feedback to the user, highlighting the specific fields that need attention.

Validating Nested Objects

When your frontend sends data in a structured object (e.g., user[profile][phone]), you can validate these nested keys directly. This keeps your request payload organized and ensures that related data is validated as a single unit.

public function rules()
{
    return [
        'name' => 'required|string|max:255',
        'email' => 'required|email|unique:users,email',
        'profile' => 'required|array',
        'profile.bio' => 'nullable|string|max:500',
        'profile.twitter_handle' => 'nullable|string|starts_with:@',
        'profile.birthday' => 'required|date|before:today',
    ];
}

It will validate different type of profile data with proper validation rules with help of dot notation.

Customizing Validation Error Messages

To provide a better user experience, you can override Laravel’s default error messages. This is especially useful for nested arrays, where the default message (like “The products.0.name field is required”) is not very helpful to an end-user. Let’s merge our example and set it to return custom validation message in Laravel 12:

<?php
namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

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

    public function rules()
    {
        return [
            'customer_name' => 'required|string|max:255',
            'profile' => 'required|array',
            'profile.email' => 'required|email',
            'profile.phone' => 'nullable|string',
            'products' => 'required|array|min:1',
            'products.*.name' => 'required|string',
            'products.*.price' => 'required|numeric|min:0',
            'products.*.quantity' => 'required|integer|min:1',
        ];
    }

    public function messages()
    {
        return [
            'products.*.name.required' => 'Every product must have a name.',
            'products.*.price.numeric' => 'The price must be a valid number.',
            'profile.email.email' => 'Please provide a valid corporate email address.',
        ];
    }

    public function attributes()
    {
        return [
            'products.*.name' => 'product name',
            'products.*.price' => 'price',
            'products.*.quantity' => 'quantity',
        ];
    }
}

Now it will print proper validation message for these errors.

Conclusion

Validating arrays and nested values in Laravel is crucial for maintaining data integrity in complex scenarios. By following the practical example, you can ensure that your application’s array-based input data is validated according to your defined rules.

For more control over error handling, see Customize Laravel Request Validation Response to learn how to tailor validation responses to fit your application’s needs.