Laravel 10 - validation rule "string or array"

629 views Asked by At

I need to add validation for a dynamic array whose elements can be strings or subsequent arrays containing strings, but I can't find a way to save this rule.

My current validation:

        $data = $request->validate([
            'answers' => ['required', 'array'],
            'answers.*' => ['required', 'max:255'],
            'answers.*.*' => ['nullable', 'string', 'max:255']
        ]);

Sample input:

  "answers" => array:3 [▼
    4 => array:1 [▼
      0 => "Tests"
      1 => "Tests 2"
    ]
    5 => "Test"
    6 => "Yes"
  ]
3

There are 3 answers

0
ZeNix On BEST ANSWER

So, I created something that might solve your problem, I've tried it and works.

First you might want to create a new Rule class, as I commented in your question, maybe using artisan command php artisan make:rule DynamicArray for example to give it a name.

You'll need to do something like this inside app/Rules/DynamicArray:

public function validate(string $attribute, mixed $value, Closure $fail): void
{
    $this->checkArray($attribute, $value, $fail);
}

private function checkArray(string $attribute, mixed $value, Closure $fail): void
{
    if(!is_array($value)){
        $fail("The $attribute must be an array.");
        return;
    }

    foreach ($value as $element){
        if (is_array($element)) {
            $this->checkArray($attribute, $element, $fail);
        } else if (!is_string($element)) {
            $fail("The $attribute is invalid.");
            return;
        }
    }
}

then in your controller you'll end with this:

    $data = $request->validate([
        'answers' => ['required', 'array', new DynamicArray]
    ]);

    dd($data);

you might debug $data to check if it's working:

    dd($data);

It will pass the valildation:

validation pass

Hope that solve your problem!

0
Ali Özen On

Create custom rule class. For ex: NestedArrayWithStrings

then check:

if (is_array($item))

in a for loop etc.

add to validation like this:

'answers' => ['required', 'array', new NestedArrayWithStrings],
0
nice_dev On

You would need to define a custom rule in Laravel for this.

  • Run the command php artisan make:rule NestedArrayWithStrings and this will create a new file in App\Rules directory.

  • To add this rule, first import the rule class namespace in your controller.

    use App\Rules\NestedArrayWithStrings;
    
  • Now, use it in your validator as:

$v = Validator::make($request->all(),[
        'answers' => [
            'required',
            'array',
            new NestedArrayWithStrings
        ]
    ]);

if($v->fails()){
    echo $v->errors()->first(); // or similar
}

Below will be your recursive code to judge the values and if it passes:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class NestedArrayWithStrings implements Rule{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value){
        if(!is_array($value)) return false;
        foreach($value as $v){
            if(is_array($v)){
               if(!$this->passes($attribute, $v)){
                 return false;
               }
            }else if(!is_string($v)){
                return false;
            }
        }

        return true;
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message(){
        return 'Answers can only contain arrays filled with strings.';
    }
}

Note: Technically, everything given from the frontend will come as a string, so you will need to add additional checks along with is_string to filter out the kind of data you wish to have.