2
votes

I have a form that has an input "array" that, when submitted, is validated using Laravel's Validator.

The validation is working as expected, applying the rule to every array element, and returning a MessageBag of errors accordingly.

The problem: the MessageBag makes no reference to the related index of the array. Is there a way (besides defining a rule for each possible index of the array) to make Laravel reference the related input array index?

Sample HTML

<input name="something[]" type="text" />
<input name="something[]" type="text" />
<input name="something[]" type="text" />
...

Sample validator

Validator::make
(
    Input::all(),
    array
    (
        "something" => array("required", "exists:somewhere")
    )
);

Sample MessageBag after validation (no reference to the input array index)

object(Illuminate\Support\MessageBag)#150 (2)
{
    ["messages":protected]=> array(6)
    {
        ["something"]=> array(1)
        {
            [0]=> string(26) "Some error message"
        }
    }
    ...
}
2
Would you clarify your question more so I would understand what you exactly expect the output to be like! - Waiyl Karim
The output should reference the index of the <input> element, so I can tie the error to a specific form element. Right now Laravel only references the element's name ("something") which could be any of the <input>'s - Telmo Marques
Why would you use <input name="something[]" type="text">? Couldn't you use something like <input name="something_1[]" type="text"> <input name="something_2[]" type="text"> ... ? Can you tell me what are you exactly intending to do maybe I can give you a better idea! - Waiyl Karim
PHP automatically parses inputs whose name ends with [] as arrays. If I add "_1", "_2", etc to the name I'd have to create specific Laravel validator rules for each input (that's what I'm trying to avoid). - Telmo Marques

2 Answers

1
votes

I failed to find any way of doing this using standard Laravel core functionality, so I ended up extending the Validator class to implement a solution.

The code below is documented for better understanding of this solution.

  1. Created app/libraries/MyApp/Validation directories to hold new Classes

  2. Extended \Illuminate\Validation\Validator as per this article ("Resolving from a class" method): Extending the Laravel 4 Validator

  3. Override Validator#passes()

    namespace MyApp\Validation;
    
    use \Illuminate\Validation\Validator;
    
    class ArrayAwareValidator extends Validator
    {
        //Override Validator#passes() method
        public function passes()
        {
            //For each validation rule...
            foreach ($this->rules as $attribute => $rules)
            {
                //Get the value to be validated by this rule
                $values = $this->getValue($attribute);
    
                //If the value is an array, we got an HTML input array in our hands
                if(is_array($values))
                {
                    //Iterate through the values of this array...
                    for($i=0; $i<count($values); $i++)
                    {
                        //...and create a specific validation rule for this array index
                        $this->rules[$attribute.".".$i] = $rules;
                    }
    
                    //Delete original rule
                    unset($this->rules[$attribute]);
                }
            }
    
            //Let Validator do the rest of the work
            return parent::passes();
        }
    }
    
  4. This results in the following MessageBag

    object(Illuminate\Support\MessageBag)#151 (2)
    {
        ["messages":protected]=> array(2)
        {
            ["something.0"]=>
            array(1)
            {
              [0]=> string(26) "Some error message"
            }
            ["something.1"]=>
            array(1)
            {
              [0]=> string(26) "Some error message"
            }
        }
        ...
    }
    
  5. Client-side, using JavaScript, I split the name from the index (for ex. "something.1" to "something" and "1"), and then use this information to identify the correct form input. I'm using Ajax to submit the form, so you might need to use a different approach here.

Note: This is a pragmatic solution that works well for my problem. As you can see the array index is passed along with the input name in the same string. You might want to extends things a bit more to make Validator return an Object or an Array with name and index as separate things.

0
votes

Your Validator should be like this:

       Validator::make
 (
   Input::all(),
array
(
    'something' => 'required,unique:somewhere'
));