Form Validation Archives - developed.be

I spent a crazy amount of time on figuring out how to validate form arrays in Laravel. There is some official documentation, but like most official documentation of Laravel, it only covers the bare minimum of what you need to know. This is an advanced article on how to validate form arrays in Laravel.

I have a form where people can enter 3 iban and bic numbers (these are EU bankaccounts). That makes 3 pairs of textboxes:

iban[]

bic[]

iban[]

bic[]

iban[]

bic[]

… and other form elements …

My desired form validation rules:

  • Maximum 3 couples of iban & bic can be submitted
  • The user is not obligated to fill in any of the iban & bic numbers.
  • When an IBAN is filled in, the user also needs to fill in the BIC.
  • The IBAN and BIC can only contain alphanumerics and spaces.

Out of the box, Laravel can validate form arrays with the dot character. The next form rule will work out of the box:

'iban.0' => 'required'

in your views, you can check for the error:

$errors->has('iban.0')

The same goes for the second iban:

'iban.1' => 'required'

But I don’t need that in my setup. The fields are not mandatory.

To check for alpha+num+spaces you have to create a new ValidationRule. The most decent way is to extend the default Validator class and add your own rules. Then you have to create a service provider that returns the ExtendedProvider. Finally you have to add the serviceprovider to app.php and run composer update.

I’ll walk you through each of the files you have to create:

File: ExtendedValidator.php

<?php namespace App\Services\Validators;
 
use Illuminate\Validation\Validator;
 
class ExtendedValidator extends Validator {
 
    public function validateAlphaNumSpaces($attribute, $value, $parameters)
    {
        return preg_match('/^[\pL\s0-9]+$/u', $value);
    }
}

The function validateAlphaNumSpaces() will listen to the rule alpha_num_spaces. The three parameters are standard parameters for validate-functions. We only use $value and not the other parameters because this is a simple rule. $value is what the user entered in the form field.

The function checks $value with a regex and returns true if it matches.

File: ExtendedServiceProvider.php

<?php namespace App\Services\Validators;
 
use Illuminate\Support\ServiceProvider;
 
class ExtendedValidatorServiceProvider extends ServiceProvider {
 
    public function register(){}
 
    public function boot()
    {
        $this->app->validator->resolver(function($translator, $data, $rules, $messages)
        {
            return new ExtendedValidator($translator, $data, $rules, $messages);
        });
    }
}

Then in app.php, at the end of the providers array:

'providers' => array(
...
'App\Services\Validators\ExtendedValidatorServiceProvider',
),

Run composer dump-autoload -o

(the -o is for faster performance)

Now we can change the validation rule to:

'iban.0' => 'required|alpha_num_spaces'

Array max size

I want to make sure that a hacker/user can submit no more than 3 iban numbers. There is no boilerplate code for that so we have to write it ourselves. I continue with the files I created in the previous steps.

In ExtendedValidator.php:

     public function validateArraySize($attribute, $value, $parameters){
        $data = array_get($this->data, $attribute);
 
        if(!is_array($data)){
            return true;
        }else{
            $sizeIsOk = count($data) <= $parameters[0];
            return $sizeIsOk;
        }
    }

This function will listen to the rule array_size. You can use it like this:

'iban' => 'array|array_size:3'

This makes sure that the iban field is an array, and can only contain 3 keys.

To create a nice error message to the user, go to app/lang/en/validation.php

app/lang/en/validation.php

return array(
...
    "array_size"     => "You can only enter :array_size different values for :attribute.",
);

You might wonder how the system knows what :array_size is. Well, it doesn’t. We have to tell Laravel what it is.

Go to ExtendedValidator.php

Enter the following:

    /**
     * Replace all place-holders for the min rule.
     *
     * @param  string  $message
     * @param  string  $attribute
     * @param  string  $rule
     * @param  array   $parameters
     * @return string
     */
    protected function replaceArraySize($message, $attribute, $rule, $parameters)
    {
        return str_replace(':array_size', $parameters[0], $message);
    }

This will replace :array_size with the value you entered in the validation rules.

Almost there.

I also want the following condition: if a user enters an iban, he also has to enter a bic.

You can use this out of the box working rule:

'bic.0' => 'alpha_num_spaces|required_with:iban.0',

required_if with form arrays

For the real daredevils: what if the user first has to check a box before he can enter the iban?

That makes:

checkbox[]

iban[]

bic[]

checkbox[]

iban[]

bic[]

checkbox[]

iban[]

bic[]

… and other form elements …

To be able to do this we need a multi_required_if that we have to write ourselves. I based it loosely on validationRequiredIf from Validator.php

In ExtendedValidator.php

   /**
     *<!--DVFMTSC--> "Required if" element corresponds in an array
     */
    protected function validateMultiRequiredIf($attribute, $value, $parameters){
 
        $this->requireParameterCount(2, $parameters, 'multi_required_if');
 
        $parameterKey = substr($parameters[0], strpos($parameters[0], '.') + 1);
        $parameterName = substr($parameters[0], 0, strpos($parameters[0], '.'));
 
        $data = array_get($this->data, $parameterName);
 
        if(!is_array($data)){
            return true;
        }
 
        $values = array_slice($parameters, 1);
 
        if (in_array($data[$parameterKey], $values))
        {
            $isEmpty = $this->validateRequired($attribute, $value[$parameterKey]);
            return $isEmpty;
        }
 
        return true;
    }

You can use it like this:

'iban.0' => 'multi_required_if:checkbox-element.0,1',

This means that the first iban textfield (iban.0) must be filled id when the first checkbox element (checkbox-element.0) is checked.

At last, you have to make a rule for each form array element. Too bad I didn’t have the time to figure out how the validation rules can work on each element in the array. With the last example, you have to write a rule for each array element:

'iban.0' => 'multi_required_if:checkbox-element.0,1',

'iban.1' => 'multi_required_if:checkbox-element.1,1',

'iban.2' => 'multi_required_if:checkbox-element.2,1',