Laravel 5.5 validation ruleception (rule inside rule)

Juampi Barreto
2 min readJan 31, 2018

--

Laravel’s 5.5 Validation Rules are awesome. They let you add custom validations into your code in an organized way.
I think they were made as a simple atomic validation rule, that only deals with one validation at a time. That’s why they only take one attribute — value; they are supposed to replace the string validations.
For more complicated and heavy attribute linked validations, they recommend creating a custom Request and add validation there…

But I still needed to make a simple validation: validate the user’s phone if it had changed.
I could fetch the user and append to the validation string: 'unique:users' to the ‘phone’ attribute only if the request()->input('phone’) is defined and not equal to auth()->user()->phonedefined, but it’d get messy really soon.

Laravel’s validation Rule

With php artisan make:rule PhoneUniquewe can now start to build this rule. We’ll have to take the user in the constructor, and then, on passes(), call the ‘unique:users’ rule only if the user’s phone changed.

use Illuminate\Support\Facades\Validator;
use Illuminate\Contracts\Validation\Rule;

class PhoneUnique implements Rule
{
protected $user;

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

public function passes($attribute, $value)
{
// If it hasn't changed, then it passes
if (!$value || $this->player->phone === $value) {
return true;
}
$validator = Validator::make([
'phone' => $value
], [
'phone' => 'unique:users'
]);

return $validator->passes();

}

public function message()
{
return 'Player\'s phone must be unique.';
}
}

So now it’s simpler. 🙂

$user = auth()->user();
request()->validate([
'phone' => ['required', new PhoneUnique($user)]
]);

Make it prettier

As I know that I’ll be using a lot of this in my project (the ValidPhone rule can be digits + check for country code + check for length + not duplicated), I figured it’d be nice to have it reusable. That’s why I made BaseRule that has that functionality:

Now extending this base class on your rules, you’ll be able to nest validations with other rules.

Example:

use Illuminate\Contracts\Validation\Rule;

class PlayerPhoneUnique extends BaseRule implements Rule
{
protected $user;

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

public function passes($attribute, $value)
{
if (!$value || $this->player->phone === $value) {
return true;
}

// Needs the attribute parameter cause that's the field used by unique
return $this->validate($value, 'unique:players', $attribute);
}

public function message()
{
return 'Player\'s phone must be unique.';
}
}

Now, as validate stores the validator, you can add multiple rules, and then on message() you can access it and check for it’s errors.

More examples

  • Multiple rules on same validator
public function passes($attribute, $value)
{
return $this->validate($value, [
'required', 'digits:10', new PhoneUnique($this->user)
], $attribute);
}
  • Multiple validators
public function passes($attr, $value)
{
if (! $this->validate($value, ['required', 'digits:10'], $attr)
) {
return false;
}
return $this->validate($value,
new PhoneUnique($this->user), $attr);
}

Custom message using validation errors
The last invalid should be the one returned on getValidator()

public function message()
{
$errors = $this->getValidator()->errors();

if ($errors->any()) {
return $errors->all();
}

return 'Some other error';
}

Hope it was useful.

--

--

Responses (1)