Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

rublopweb's avatar

validation rules to an array field

Hi, I have a colum called sections in a table which holds a number and aother column called sections_details of JSON type. I want to save in that column an array of associative arrays. I would like to add some validation and I am using the rules method:

I would like the lenght of the sections details array to be equal to the sections value and i also want each associative array of the section_details to have certain keys.

Now i have this but i does not work:

    public function rules()
    {
        return [
       ...
            'sections'=> 'required|numeric',
            'sections_details'=> 'required|array|size:$this->sections'
        ];

For example, if the sections value is 2 the sections_details should contain two associative arrays, and each assoxiative array must contain certain keys. I would like to keep it simple and use the rules method and not implement my own rules. How can i achieve it ?

0 likes
6 replies
LaryAI's avatar
Level 58

You can achieve this by creating a custom validation rule. Laravel provides a way to create custom validation rules using the Rule facade. Here's how you can do it:

First, create a new rule using the make:rule artisan command:

php artisan make:rule SectionsDetailsRule

This command will create a new rule class in the app/Rules directory. In this class, you can define your custom validation logic. Here's an example:

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class SectionsDetailsRule implements Rule
{
    private $sections;

    public function __construct($sections)
    {
        $this->sections = $sections;
    }

    public function passes($attribute, $value)
    {
        if (count($value) != $this->sections) {
            return false;
        }

        foreach ($value as $section) {
            if (!isset($section['key1']) || !isset($section['key2'])) {
                return false;
            }
        }

        return true;
    }

    public function message()
    {
        return 'The :attribute does not match the sections or does not contain the required keys.';
    }
}

In the passes method, we first check if the count of the array matches the sections value. Then we loop through each item in the array and check if the required keys exist.

Now you can use this rule in your validation rules method:

public function rules()
{
    return [
        'sections' => 'required|numeric',
        'sections_details' => ['required', 'array', new SectionsDetailsRule($this->sections)],
    ];
}

This will ensure that the sections_details array has the same count as the sections value and each item in the array contains the required keys.

ModestasV's avatar

You can solve your issue by using a prepareForValidation() method on your request. For example:

  public function rules(): array
    {
        return [
            'sections' => 'required|numeric',
            'sections_details' => 'required|array|size:' . $this->input('sections'),
            'sections_details.*.title' => 'required|string',
            'sections_details.*.description' => 'required|string',
            'sections_details.*.image' => 'required|image',
        ];
    }

    protected function prepareForValidation()
    {
        $this->merge([
            'sections_details' => json_decode($this->input('sections_details'), true)
        ]);
    }

This way you can validate your json as it was an array. If you need to have a JSON saved to database you can use model casting https://laravel.com/docs/10.x/eloquent-mutators#array-and-json-casting or transform it back to json in the request:

protected function passedValidation()
{
    $this->merge(['sections_details' => json_encode($this->input('sections_details'))]);
}
krisi_gjika's avatar

this is not valid string interpolation in php

'sections_details'=> 'required|array|size:$this->sections'

you are looking for one of these

'sections_details'=> 'required|array|size:' . $this->input('sections')
'sections_details'=> "required|array|size:{$this->input('sections')}"
rublopweb's avatar

Than you, that worked. Now I would like to add some more validation. My json field should look like this:

"sections :3 ,
"sections_details":[    
        {"section": "1",
        "hours": "3 a 9",
        },
        {
        "section": "2",
        "hours": "10 a 14",
        }
	     {
        "section": "3",
        "hours": "15 a 17",
        }
    ]

Inside the section_details, the section has to be one value larger than the previous section and with the hours i want to achieve the same. It is possible to do that?

krisi_gjika's avatar

@rublopweb you can add a closure validation rule to your field

'sections_details'=> [
    'bail', 'required', 'array', 'size:'. $this->sections,
    function (string $attribute, mixed $value, Closure $fail) {
      // $value is the input array, loop each section and check the keys/values
      // if a key is not found or a value is wrong call: $fail($message)
    },
]
rublopweb's avatar

I am using this code in the rules method

'sections'=> 'required|numeric',
            'sections_details.*.section'=> 'bail|required',
            'sections_details.*.hours'=> 'bail|required',
            'sections_details.*.base'=> 'bail|required',
            'sections_details' => [
                'required',
                'array',
                'size:' . $this->sections,
                function ($attribute, $value, Closure $fail) {
                    $firstSection =1;
                    if ($value[0]['section']!== $firstSection){
                        $fail('The first section has to be '. $firstSection);
                    }
                    $arrayLength = count($value);
                    for ($i=1; $i< $arrayLength; $i++){
                        if ($value[$i]['section']!== $value[$i-1]['section']+ 1){
                            $fail ('The range with index '.$i.' should be '.($value[$i-1]['section']+ 1));
                        }
                    }                   
                },
            ],

The closure is working but the problem now is that

 'sections_details.*.section'=> 'bail|required'

is not working like i want., because when i do not send the "section" key in one of the associative arrays of the "section_details" array it enters the closure instead of failing because of the section is required.

Please or to participate in this conversation.