Kontrolio — простая библиотека валидации данных без дополнительных зависимостей, вдохновленная Laravel и Symfony, совместимая с PHP 8.1+. [ Пост на Medium ]
Предпочтительный метод создания валидатора:
// В средах без контейнера сервисов
$valid = Factory::getInstance()->make($data, $rules, $messages)->validate();
// Пример использования с контейнером сервисов
$container->singleton('validation', static fn() => new Factory());
$container->get('validation')->make($data, $rules, $messages)->validate();
Конечно, вы можете не использовать фабрику, но тогда вам придётся самостоятельно подключать правила валидации по умолчанию:
$validator = new Validator($data, $rules, $messages)->extend($custom)->validate();
Данные представляют из себя пары ключ-значение, т.е. атрибуты и их значения:
$data = [
'foo' => 'bar',
'bar' => 'baz',
'baz' => 'taz'
];
Правила валидации могут задаваться тремя разными способами:
- Строки в стиле Laravel
- Объекты классов-правил
- Замыкания / функции обратного вызова
Вы можете совмещать объекты-правила и коллбэки, когда задаете несколько правил валидации для одного атрибута. Пример:
$rules = [
'one' => 'not_empty|length:5,15',
'two' => new Email,
'three' => static fn ($value) => $value === 'taz',
'four' => [
static fn ($value) => is_numeric($value),
new GreaterThan(5),
]
];
Когда вы устанавливаете правила валидации в виде строки, валидатор, перед тем как применить правила к атрибуту, распарсит переданную строку в обычный массив правил. Поэтому когда вы пишете 'some' => 'not_empty|length:5,15'
, на выходе получается следующее:
'some' => [
new NotEmpty,
new Length(5, 15)
]
Как видите, это довольно просто, но помните, что все аргументы, которые вы передаете после двоеточия, разделяя их запятыми, становятся аргументами конструктора для объекта правила валидации.
Когда вы пишете правило валидации в виде коллбэека, валидатор оборачивает его в специальный объект класса Kontrolio\Rules\CallbackRuleWrapper
, чтобы следовать интерфейсу Kontrolio\Rules\RuleInterface
.
Отдельное правило валидации может быть пропущено, если валидируемое значение атрибута пусто. Если вы решили, что вам необходима данная опция, вы создаете объект правила используя именованный конструктор allowingEmptyValue()
или вызывая метод allowEmptyValue()
на уже существующем объекте:
'some' => [
MyRule::allowingEmptyValue(),
// (new MyRule())->allowEmptyValue()
]
Создавая новое правило валидации вы можете воспользоваться возможностью пропуска его валидации при соблюдении какого-то условия. Вы можете определить такое поведение, используя метод canSkipValidation()
:
class MyRule extends AbstractRule
{
public function canSkipValidation($input = null)
{
return $input === 'bar';
}
// ...
}
Правило-коллэк — это ничто иное как простое замыкание или функция обратного вызова, которая принимает единственным аргументом значение валидируемого атрибута и возвращает либо булевый результат валидации, либо массив опций, эквивалентных тем, что используются классами-правилами:
'foo' => static fn ($value) => is_string($value),
'bar' => static function ($value) {
return [
// required when array
'valid' => $value === 'taz',
// optionals
'name' => 'baz', // rule identifier
'empty_allowed' => true, // allowing empty value
'skip' => false // don't allow skipping current rule validation,
'violations' => [] // rule violations
];
}
Естественно, ничто не мешает вам создавать собственные правила. Просто помните, что любое правило должно реализовывать интерфейс Kontrolio\Rules\RuleInterface
, метод isValid()
и иметь идентификатор. По умолчанию, для удобства идентификатор выводится абстрактным классом Kontrolio\Rules\AbstractRule
и основывается на имени класса без учета пространства имён. Тем не менее, вы можете переопределить идентификатор, переопределив метод getName()
.
Добавить правила в валидатор вы можете как через фабрику, так и непосредственно через сервис валидации:
$factory = (new Factory())->extend([CustomRule::class]);
// с кастомным идентификатором
$factory = (new Factory())->extend(['some_custom' => CustomRule::class]);
$validator = $factory->make([], [], []);
// если вы не используете фабрику
$validator = new Validator([], [], []);
$validator->extend([CustomRule::class]);
// с кастомным идентификатором
$validator->extend(['custom' => CustomRule::class]);
$validator->validate();
Это не то же самое, что использование методов allowEmptyValue()
или canSkipValidation()
на правиле. Используя их, вы можете пропустить это конкретное правило. Тем не менее вы можете полностью отменить валидацию атрибута используя правило Kontrolio\Rules\Core\Sometimes
. Sometimes
указывает валидатору пропустить валидацию атрибута по другим правилам, если значение артибута пустое или null
. Вот и всё. Вы можете поставить Sometimes
в начало массива правил для атрибута или использовать соответствующий идентификатор в строке:
$rules = [
'one' => 'sometimes|length:5,15',
// 'one' => [
// new Sometimes(),
// new Length(5, 15)
// ]
];
Вы можете дать команду валидатору полностью остановиться при возникновении первой же ошибки:
$validator->shouldStopOnFailure()->validate();
Используя правило UntilFirstFailure
, вы можете останавливать валидацию отдельного атрибута без остановки валидации полностью:
$data = [
'attr' => '',
'attr2' => 'value2'
];
$rules = [
'attr' => [
new UntilFirstFailure(),
new NotBlank(),
new NotFooBar()
]
];
$messages = [<...>];
$validator = new Validator($data, $rules, $messages)->validate();
В данном случае атрибут attr
не пройдет проверку на правило NotBlank
, его валидация на этом завершится, однако валидатор перейдет к следующему правилу attr2
и продолжит проверки.
Ошибки валидации задаются в едином и простом формате, который вам понравится:
$messages = [
'foo' => 'Foo cannot be null',
'foo.length' => 'Wrong length of foo',
'foo.length.min' => 'Foo is less than 3'
// '[attribute].[rule].[violation]
];
Ключ каждого из сообщений может состоять из одного-трех сегментов, разделенных точками:
- Имя атрибута
- Идентификатор правила
- Ключ «нарушения» правила
Имея эти параметры, вы можете кастомизировать свои ошибки валидации от самой общей до самой специфичной. Каждое «нарушение» устанавливается соответствующим правилом при проверке валидности значения атрибута. Поэтому когда вы создаете собственные правила валидации, не возбраняется, а даже поощряется определять такие «нарушения», чтобы предоставить широкие возможности кастомизации результатов валидации и соответствующих ошибок.