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.
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' ]; } }
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.
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.' ]; }
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’, ]; }
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.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
3 Replies to "Validating arrays and nested values in Laravel"
So helpful thank you!
I’m using input name attr like data[route_title][] but got the data[][route_title] is also fine in this article. thank you for detailed article.
how do you do the message()/translation of arrays and nested attributes, with the rule `required_if:field.*.filed,value`? because you have :other, :attribute, :value..