
Laravel: как добавить новое правило в валидатор
18.04.2021 20:42 | Laravel
Ubuntu: 20.02
PHP: 8.0.3
Laravel: 8.36.2
Если заглянем в официальные доки, то найдём там пользовательские объекты правил, замыкания и неявные правила. Но не увидим Validator::extend()
. Возможно потому, что фишка далеко не нова и была добавлена задолго до текущей (8-й) версии фреймворка. Недавно снова столкнулся, потому оставлю короткое напоминание о том, как использовать.
Допустим, мы хотим заставить пользователей использовать пароли посложнее. Т.е. пароль должен включать:
- как минимум одну заглавную букву
- как минимум одну цифру
- как минимум один специальный символ
Плюс пароль должен быть не менее N символов, и, естественно, мы хотим добавить соответствующее сообщение об ошибке.
Начнём с того, что добавим настройку с минимальной длиной пароля в файл config/auth.php
:
<?php
return [
...
'password_min_length' => 10,
...
];
Для валидации будем использовать регулярное выражение (статья не о регулярках, поэтому здесь разбирать его не буду, если есть необходимость, рекомендую, как минимум, ознакомиться со статьёй в вики). Идём в AppServiceProvider
(app/Providers/AppServiceProvider.php
) и редактируем метод boot()
:
<?php
...
class AppServiceProvider extends ServiceProvider
{
...
public function boot()
{
$min = config('auth.password_min_length');
$pattern = '/^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{'.$min.',}$/';
Validator::extend('strong_password',
function ($attributes, $value, $parameters, $validator) use ($pattern) {
return is_string($value) && preg_match($pattern, $value);
}
);
}
}
Вот, собственно, код метода extend()
, который можно посмотреть в vendor/laravel/framework/src/Illuminate/Validation/Factory.php
:
/**
* Register a custom validator extension.
*
* @param string $rule
* @param \Closure|string $extension
* @param string|null $message
* @return void
*/
public function extend($rule, $extension, $message = null)
{
$this->extensions[$rule] = $extension;
if ($message) {
$this->fallbackMessages[Str::snake($rule)] = $message;
}
}
Как видим третьим аргументом можно передать и сообщение. Однако, обратите внимание - это fallback, который будет использоваться в случае, если ключ (в нашем случае strong_password
) не будет найден в языковах файлах переводов проекта. В общем, можно добавить текст ошибки и здесь, но если сайт планируется мультиязычным или может стать таковым в определённый момент, нам всё равно придётся переводить месседж. Поэтому, разместим текст ошибки там, где ей и место, т.е. в resources/lang/en/validation.php
(и аналогичных файлах других языков, если таковые имеются). Допишем пару ключ - значение после переводов из коробки:
<?php
return [
'accepted' => 'The :attribute must be accepted.',
...
'uuid' => 'The :attribute must be a valid UUID.',
'strong_password' => 'The :attribute must be at least :min characters, contain at least one upper case letter, at least one digit and at least one special character.',
...
];
Осталось применить правило в валидации регистрации, скажем так:
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|strong_password:'.config('auth.password_min_length').'|confirmed',
]);
Но тут нас ждёт сюрприз: если :attribute
будет заменён на название поля, то :min
в сообщении об ошибке так и останется. Посему добавим в провайдер ещё и replacer
:
Validator::replacer('strong_password', function ($message, $attribute, $rule, $parameters) {
return str_replace(':min', $parameters[0], $message);
});
Вот, собственно, и всё. Успехов!