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

Gavrisimo's avatar

How to validate array fields?

So I have two fields that looks like this <input type="text" name="email-address[]" /> on my page and I want to validate both of them with email validation rule.

If I do the usual thing, i.e. something like this:

'email-address' => [
    'email',
],

Then I get following error htmlentities() expects parameter 1 to be string, array given.

Is there some nice and easy way of doing this?

0 likes
37 replies
anaxamaxan's avatar

The email validation rule, like nearly all the built-in rules, is available in Laravel's Illuminate\Validation\Validator class (in 4.2 at least). Looking at the specific method, it looks like this:

/**
  * Validate that an attribute is a valid e-mail address.
  *
  * @param string $attribute
  * @param mixed $value
  * @return bool
  */
 protected function validateEmail($attribute, $value)
 {
  return filter_var($value, FILTER_VALIDATE_EMAIL) !== false;
 }

As you can see, it's simply a convenience method for the filter_var PHP method. One simple solution then would be to create a custom validation rule something like this:

Validator::extend('isArrayOfEmails', function($attribute, $value, $parameters)
{
    // define closure separately for readability
    $checkEmails = function($carry, $email){
        return $carry && filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
    };
    // validate that the value given is an array of valid email addresses
    return (is_array($attribute) and array_reduce($values,$checkEmails,true));
});

minor edit: corrected Laravel version to 4.2 in first para.

jr2wolfgang's avatar

I need some answer on this. As this is now my current problem. Can't find any solution on the net right now. Why is it so hard with array form element ?

Mort's avatar

What was wrong with the solution @anaxamaxan posted? Custom validation rules would be the way to go here.

Gavrisimo's avatar

@WookieMonster

I do something like this:

$v = \Validator::make(Input::all(), $rules);

$v->each('email-address', ['required', 'email']);

if ($v->fails()) {
    // Throw exception
}
8 likes
bestmomo's avatar

And with dot notation like this :

'email-address.0' => 'email',
'email-address.1' => 'email',
5 likes
Gavrisimo's avatar

The issue is that you don't know how many email addresses user will submit, like when you have a edit page where user can add 1 or many email addresses.

pmall's avatar

Each is cool but not suited to FormRequest as the validator instance is not usable from the rule method.

bestmomo's avatar

@pmall just to create a validator method in form request :

public function validator($factory)
{ 
  $v = $factory->make($this->all(), $this->rules()); 
  $v->each('email-address', ['required', 'email']);
  return $v;
}
2 likes
bestmomo's avatar

On L5 I've made some tests and if I use array input like :

<input type="text" name="email-address[]" />
<input type="text" name="email-address[]" />

And use this rule :

return ['email-address' => 'email'];

I dont get "htmlentities() expects parameter 1 to be string, array given." but casual validation with "The email-address must be a valid email address." as error.

pmall's avatar

@bestmomo Lets say I want to add many users that have name, email, etc. So my input data are :

['users' => [
    'name' => 'username1',
    'email' => 'email1',
  ],
  [
    'name' => 'username2',
    'email' => 'email2',
]];

I cant figure out how to apply your method on this ?

bestmomo's avatar

@pmall it would but seems that array_get doesn't work with 3 levels of keys so validation breaks.

pmall's avatar

@bestmomo you cant even write the rules. What would it be ? 'users.name' => 'required' won't work even if array_get could handle 3 levels of keys.

bestmomo's avatar

@pmall would work with something like this :

public function validator($factory)
{ 
  $v = $factory->make($this->all(), $this->rules());
  $users = $this->get('users');
  for($i = 0; $i < count($users); ++$i) {
        $v->mergeRules('users.' . $i . '.name', ['required']);
        $v->mergeRules('users.' . $i . '.email', ['email']);
  }
  return $v;
}
nolros's avatar

don't know about L4.2, but in L5 array validation is broken when injected FormRequest validation so I do file array uploads manually.

foreach ($allFiles as $file) {
    $validator = Validator::make(
        array('file' => $file),
        array('file' => 'required|mimes:jpeg,png|image|max:1000')
    );

    if ($validator->passes()) 
   {
        // Do something
    } else {
        // Collect error messages
        $error_messages[] = 'File "' . $upload->getClientOriginalName() . '":' .$validator->messages()->first('file');
    }
}

 Redirect, return JSON, whatever...

return $error_messages;
// add an conditional statement ... etc. 
    } else {
    // No files have been uploaded//
}


I assume email would be the same

foreach ($allEmails as $email) {

    $validator = Validator::make(
        array('email' => $email),
        array('email' => 'required | email')
    );

    if ($validator->passes()) 
    {
        // Do something
    } else {
        // Collect error messages
       $error_messages[] = 'email'. $email. blah blah some sort of error message
    }
}

Redirect, return JSON, whatever...
return $error_messages;

some sort of condition to return a better error message 
    } else {
    // No emails have been uploaded//
}


1 like
thepsion5's avatar

With form requests, you define a rules() method to return the set of validation rules, correct? In that case, wouldn't it be easy to check the number of submitted emails and add a rule for each? Something like:

function rules()
{
    $rules = $this->rules;
    $numEmails = count( $this->get('email') );
    foreach( range(0, $numEmails), as $index) {
        $rules['email.' . $index] = 'email';
    }
    return $rules;
}
2 likes
pmall's avatar

Thanks @bestmomo but Im not looking for a solution Im just complaining that laravel validator doesn't handle this out of the box :p

leon13's avatar

I don't have access to try it currently, but could you use a wildcard with the dot notation?

    'email-address.*' => 'email'
1 like
uxweb's avatar

I will add some little thing to the @thepsion5 solution:

function rules()
{
    $rules = $this->rules;
    $numEmails = count( $this->get('email') );

    // Added a -1 to generate the right number of rules, otherwise, the validation will fail
    foreach( range(0, $numEmails - 1), as $index) {
        $rules['email.' . $index] = 'email';
    }
    return $rules;
}
1 like
gdespirito's avatar

This article explains one way to doit: http://ericlbarnes.com/laravel-array-validation/

public function rules()
{
  $rules = [
    'name' => 'required|max:255',
  ];

  foreach($this->request->get('items') as $key => $val)
  {
    $rules['items.'.$key] = 'required|max:10';
  }

  return $rules;
}


And for messages: 

public function messages()
{
  $messages = [];
  foreach($this->request->get('items') as $key => $val)
  {
    $messages['items.'.$key.'.max'] = 'The field labeled "Book Title '.$key.'" must be less than :max characters.';
  }
  return $messages;
}
DarioCorno's avatar

The article by Eric Barnes is a great resource, but the validation with arrays has a problem with the unique:tablename rule, it doesn't remove the index while looking in the database for duplicates so it gives a "Unknown column 'email.7' error. (email and 7 are just examples). Or maybe I'm missing something?

tjphippen's avatar

It made more sense for me to just extend my validator:

public function validateEachExists($attribute, $value, $parameters)
{
    $this->setCustomMessages(['each_exists' => 'One or more :attribute values are invalid.']);
    $items = is_array($value) ? $value : explode(',', $value);
    foreach($items as $item) {
        $value = $this->validateExists($attribute, $item, $parameters);
    }
    return $value;
}

That way I can write my rules like:

    'permissions' => 'each_exists:permissions,id',

Also you might notice the explode in there. That's so I can pass an array or a sting like:

    {"permissions":"2,4,6"}
Next

Please or to participate in this conversation.