From a860a9510f8af058478543269bdebf77a7f2574a Mon Sep 17 00:00:00 2001 From: Andrei Nikolaev Date: Mon, 26 Jan 2026 21:01:44 +0300 Subject: [PATCH 1/2] Split validator into several pages --- pages/framework/validation.md | 467 ----------------------- pages/framework/validation/controller.md | 116 ++++++ pages/framework/validation/main.md | 256 +++++++++++++ pages/framework/validation/own.md | 256 +++++++++++++ 4 files changed, 628 insertions(+), 467 deletions(-) create mode 100644 pages/framework/validation/controller.md create mode 100644 pages/framework/validation/main.md create mode 100644 pages/framework/validation/own.md diff --git a/pages/framework/validation.md b/pages/framework/validation.md index 6c3c797..0f5e327 100644 --- a/pages/framework/validation.md +++ b/pages/framework/validation.md @@ -1,317 +1,3 @@ ---- -title: Валидация -description: 'Валидация. Документация по Bitrix Framework: принципы работы, архитектура и примеры использования.' ---- - -Валидация данных -- это проверка информации на соответствие заданным правилам. Например, числовой идентификатор должен быть положительным, а email -- соответствовать формату адреса. - -## Как использовать валидацию - -В Bitrix Framework валидацию можно реализовать разными способами. Самый простой способ -- ручные проверки в конструкторе или методах. Такой подход приводит к дублированию кода и усложняет его поддержку. - -```php -public function __construct(int $userId) -{ - if ($userId <= 0) - { - throw new \Exception(); - } - $this->userId = $userId; -} -``` - -Чтобы сократить код, используйте систему валидации на основе атрибутов. Она позволяет: - -- задавать правила в классах, - -- автоматически проверять данные при создании объектов, - -- централизованно обрабатывать ошибки. - -### Как создать правила в классе - -1. Создайте класс. Например, `User` со свойствами `id`, `email` и `phone`. - - ```php - final class User - { - private ?int $id; - private ?string $email; - private ?string $phone; - - // getters & setters ... - } - ``` - -2. Добавьте атрибуты валидации: `#[PositiveNumber]`, `#[Email]` и `#[Phone]`. - - ```php - use Bitrix\Main\Validation\Rule\AtLeastOnePropertyNotEmpty; - use Bitrix\Main\Validation\Rule\Email; - use Bitrix\Main\Validation\Rule\Phone; - use Bitrix\Main\Validation\Rule\PositiveNumber; - - #[AtLeastOnePropertyNotEmpty(['email', 'phone'])] - final class User - { - #[PositiveNumber] - private ?int $id; - - #[Email] - private ?string $email; - - #[Phone] - private ?string $phone; - - // getters & setters... - } - ``` - -3. Проверьте валидацию. Объект можно проверить через `\Bitrix\Main\Validation\ValidationService` по ключу `main.validation.service`. - - `ValidationService` предоставляет метод `validate()`, который возвращает `ValidationResult`. Результат валидации содержит ошибки всех сработавших валидаторов. - - ```php - use Bitrix\Main\DI\ServiceLocator; - use Bitrix\Main\Validation\ValidationService; - class UserService - { - private ValidationService $validation; - - public function __construct() - { - $this->validation = ServiceLocator::getInstance()->get('main.validation.service'); - } - - public function create(?string $email, ?string $phone): Result - { - $user = new User(); - $user->setEmail($email); - $user->setPhone($phone); - - $result = $this->validation->validate($user); - if (!$result->isSuccess()) - { - return $result; - } - - // save logic ... - } - } - ``` - -{% note warning "" %} - -- Валидация работает через рефлексию, модификаторы доступа не учитываются. - -- Если свойство помечено как `nullable` и не заполнено, валидация пропускает его. - -{% endnote %} - -### Вложенные объекты - -Используйте атрибут `#[Validatable]` для вложенных объектов. - -```php -use Bitrix\Main\Validation\Rule\Composite\Validatable; -use Bitrix\Main\Validation\Rule\NotEmpty; -use Bitrix\Main\Validation\Rule\PositiveNumber; -class Buyer -{ - #[PositiveNumber] - public ?int $id; - #[Validatable] - public ?Order $order; -} -class Order -{ - #[PositiveNumber] - public int $id; - #[Validatable] - public ?Payment $payment; -} -class Payment -{ - #[NotEmpty] - public string $status; - #[NotEmpty(errorMessage: 'Custom message error')] - public string $systemCode; -} -// validation -/** @var \Bitrix\Main\Validation\ValidationService $validationService */ -$validationService = \Bitrix\Main\DI\ServiceLocator::getInstance()->get('main.validation.service'); -$buyer = new Buyer(); -$buyer->id = 0; -$result1 = $validationService->validate($buyer); -// "id: Значение поля меньше допустимого" -foreach ($result1->getErrors() as $error) -{ - echo $error->getCode() . ': ' . $error->getMessage(). PHP_EOL; -} -echo PHP_EOL; -$buyer->id = 1; -$order = new Order(); -$order->id = -1; -$buyer->order = $order; -$result2 = $validationService->validate($buyer); -// "order.id: Значение поля меньше допустимого" -foreach ($result2->getErrors() as $error) -{ - echo $error->getCode() . ': ' . $error->getMessage(). PHP_EOL; -} -echo PHP_EOL; -$buyer->order->id = 123; -$payment = new Payment(); -$payment->status = ''; -$payment->systemCode = ''; -$buyer->order->payment = $payment; -$result3 = $validationService->validate($buyer); -// "order.payment.status: Значение поля не может быть пустым" -// "order.payment.systemCode: Custom message error" -foreach ($result3->getErrors() as $error) -{ - echo $error->getCode() . ': ' . $error->getMessage(). PHP_EOL; -} -``` - -### Валидация в контроллерах - -В контроллерах валидация помогает убедиться в корректности данных из запроса. - -```php -use Bitrix\Main\Validation\Rule\NotEmpty; -use Bitrix\Main\Validation\Rule\PhoneOrEmail; -final class CreateUserDto -{ - public function __construct( - #[PhoneOrEmail] - public ?string $login, - - #[NotEmpty] - public ?string $password, - - #[NotEmpty] - public ?string $passwordRepeat, - ) - {} -} -``` - -В коде класс будет выглядеть следующим образом: - -```php -class UserController extends Controller -{ - private ValidationService $validation; - - protected function init() - { - parent::init(); - - $this->validation = ServiceLocator::getInstance()->get('main.validation.service'); - } - - public function createAction(): Result - { - $dto = new CreateUserDto(); - $dto->login = (string)$this->getRequest()->get('login'); - $dto->password = (string)$this->getRequest()->get('password'); - $dto->passwordRepeat = (string)$this->getRequest()->get('passwordRepeat'); - - $result = $this->validation->validate($dto); - if (!$result->isSuccess()) - { - $this->addErrors($result->getErrors()); - - return false; - } - - // create logic ... - } -} -``` - -Создайте фабричный метод в DTO, чтобы избежать повторения кода. - -```php -final class CreateUserDto -{ - public function __construct( - #[PhoneOrEmail] - public ?string $login = null, - - #[NotEmpty] - public ?string $password = null, - - #[NotEmpty] - public ?string $passwordRepeat = null, - ) - {} - - public static function createFromRequest(\Bitrix\Main\HttpRequest $request): self - { - return new static( - login: (string)$request->getRequest()->get('login'), - password: (string)$request->getRequest()->get('password'), - passwordRepeat: (string)$request->getRequest()->get('passwordRepeat'), - ); - } -} -``` - -Класс `Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter` устранит повторяющуюся логику валидации. - -```php -class UserController extends Controller -{ - public function getAutoWiredParameters() - { - return [ - new \Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter( - CreateUserDto::class, - fn() => CreateUserDto::createFromRequest($this->getRequest()), - ), - ]; - } - - public function createAction(CreateUserDto $dto): Result - { - // create logic ... - } -} -``` - -Если объект `CreateUserDto` невалиден, метод `createAction` не будет выполнен. Контроллер вернет ошибку. - -```php -{ - data: null, - errors: - [ - { - code: "name", - customData: null, - message: "Значение поля не должно быть пустым", - }, - ], - status: "error" -} -``` - -### Валидаторы без атрибутов - -Валидаторы можно применять без атрибутов для разовой проверки данных, когда нет необходимости описывать правила в объекте. Это подходит для старого кода с массивами и нетипизированными переменными. - -```php -use Bitrix\Main\Validation\Validator\EmailValidator; -$email = 'bitrix@bitrix.ru'; -$validator = new EmailValidator(); -$result = $validator->validate($email); -if (!$result->isSuccess()) -{ - // ... -} -``` ### Сообщение об ошибке после валидации @@ -434,156 +120,3 @@ Bitrix Framework предоставляет готовые атрибуты и - `JsonValidator` -- валидация JSON. -## Как создать собственные валидаторы - -Каждый валидатор реализует интерфейс `\Bitrix\Main\Validation\Validator\ValidatorInterface` с методом `public function validate(mixed $value): ValidationResult`. - -Валидатор выполняет простую задачу -- проверяет значение. Он не определяет, относится ли значение к свойству или классу, и не зависит от атрибутов. - -### Пример валидатора Min - -1. Класс `Min` реализует интерфейс `ValidatorInterface`. - -2. Конструктор принимает минимальное значение. - -3. Метод `validate` создает объект `ValidationResult`, который хранит результаты проверки. - - - Сначала он проверяет, является ли значение числом. Если нет, добавляется ошибка. - - - Затем проверяет, меньше ли значение заданного минимума. Если да, добавляется соответствующая ошибка. - -4. В конце метод возвращает объект `ValidationResult` с результатами проверки. - -```php -namespace Bitrix\Main\Validation\Validator; -use Bitrix\Main\Localization\Loc; -use Bitrix\Main\Validation\ValidationError; -use Bitrix\Main\Validation\ValidationResult; -use Bitrix\Main\Validation\Validator\ValidatorInterface; -final class Min implements ValidatorInterface -{ - public function __construct( - private readonly int $min - ) - { - } - public function validate(mixed $value): ValidationResult - { - $result = new ValidationResult(); - if (!is_numeric($value)) - { - $result->addError( - new ValidationError( - Loc::getMessage('MAIN_VALIDATION_MIN_NOT_A_NUMBER'), - failedValidator: $this - ) - ); - return $result; - } - if ($value < $this->min) - { - $result->addError( - new ValidationError( - Loc::getMessage('MAIN_VALIDATION_MIN_LESS_THAN_MIN'), - failedValidator: $this - ) - ); - } - return $result; - } -} -``` - -## Как создать атрибуты валидации - -Атрибуты валидации разделены на два типа: для свойств и для классов. - -### Атрибуты свойств - -Атрибуты свойств реализуют интерфейс `\Bitrix\Main\Validation\Rule\PropertyValidationAttributeInterface`. Они используют метод `validateProperty(mixed $propertyValue): ValidationResult` для проверки значений свойств. - -Пример простого атрибута для проверки значения свойства: - -```php -use Bitrix\Main\Validation\Rule\PropertyValidationAttributeInterface; -use Bitrix\Main\Validation\ValidationError; -use Bitrix\Main\Validation\ValidationResult; - -#[Attribute(Attribute::TARGET_PROPERTY)] -class NotOne implements PropertyValidationAttributeInterface -{ - public function validateProperty(mixed $propertyValue): ValidationResult - { - $result = new ValidationResult(); - if ($propertyValue === 1) { - $result->addError(new ValidationError('Значение не должно быть равно 1')); - } - return $result; - } -} -``` - -Этот атрибут проверяет, что значение свойства не равно 1. Если условие нарушено, возвращается ошибка. - -Для сложных проверок используйте абстрактный класс `\Bitrix\Main\Validation\Rule\AbstractPropertyValidationAttribute`. Реализуйте метод `getValidators(): array`, чтобы вернуть список валидаторов. - -Пример атрибута `Range`, который проверяет, что значение находится в заданном диапазоне: - -```php -use Attribute; -use Bitrix\Main\Validation\Rule\AbstractPropertyValidationAttribute; -use Bitrix\Main\Validation\Validator\Implementation\Max; -use Bitrix\Main\Validation\Validator\Implementation\Min; - -#[Attribute(Attribute::TARGET_PROPERTY)] -final class Range extends AbstractPropertyValidationAttribute -{ - public function __construct( - private readonly int $min, - private readonly int $max, - protected ?string $errorMessage = null - ) {} - - protected function getValidators(): array - { - return [ - new Min($this->min), - new Max($this->max), - ]; - } -} -``` - -### Атрибуты класса - -Атрибуты класса реализуют интерфейс `\Bitrix\Main\Validation\Rule\ClassValidationAttributeInterface`. Они используют метод `validateObject(object $object): ValidationResult` для проверки объектов. - -Пример атрибута для проверки количества свойств: - -```php -use Bitrix\Main\Validation\ValidationResult; -use Bitrix\Main\Validation\ValidationError; -use Bitrix\Main\Validation\Rule\AbstractClassValidationAttribute; -use ReflectionClass; - -#[Attribute(Attribute::TARGET_CLASS)] -class NotOne extends AbstractClassValidationAttribute -{ - public function validateObject(object $object): ValidationResult - { - $result = new ValidationResult(); - $properties = (new ReflectionClass($object))->getProperties(); - - if (count($properties) > 2) { - $result->addError(new ValidationError('Класс содержит слишком много свойств')); - } - return $result; - } -} -``` - -Этот атрибут проверяет, что в классе не больше двух свойств. Если условие нарушено, метод вернет ошибку. - -### Сообщение об ошибке для атрибута - -Если вы наследуетесь от `AbstractClassValidationAttribute` или `AbstractPropertyValidationAttribute`, можно задать собственное сообщение об ошибке через свойство `$errorMessage`. Это позволит вернуть одну ошибку с вашим текстом вместо стандартных ошибок валидаторов. diff --git a/pages/framework/validation/controller.md b/pages/framework/validation/controller.md new file mode 100644 index 0000000..6a3545c --- /dev/null +++ b/pages/framework/validation/controller.md @@ -0,0 +1,116 @@ +--- +title: Валидация. Контроллеры +description: 'Валидация. Документация по Bitrix Framework: использование в контроллерах' +--- + +В контроллерах валидация используется для проверки корректности данных, поступающих из HTTP-запроса. Это позволяет убедиться, что входные параметры соответствуют ожидаемым форматам и бизнес-правилам до выполнения основной логики действия. + +Для валидации в контроллере создаётся DTO-класс с атрибутами правил: + +```php +use Bitrix\Main\Validation\Rule\NotEmpty; +use Bitrix\Main\Validation\Rule\PhoneOrEmail; + +final class CreateUserDto +{ + public function __construct( + #[PhoneOrEmail] + public ?string $login = null, + + #[NotEmpty] + public ?string $password = null, + + #[NotEmpty] + public ?string $passwordRepeat = null, + ) {} +} +``` + +Затем в контроллере данные из запроса передаются в DTO, и выполняется валидация: + +```php +use Bitrix\Main\DI\ServiceLocator; +use Bitrix\Main\Validation\ValidationService; + +class UserController extends Controller +{ + private ValidationService $validation; + + protected function init() + { + parent::init(); + $this->validation = ServiceLocator::getInstance()->get('main.validation.service'); + } + + public function createAction(): Result + { + $dto = new CreateUserDto(); + $dto->login = (string)$this->getRequest()->get('login'); + $dto->password = (string)$this->getRequest()->get('password'); + $dto->passwordRepeat = (string)$this->getRequest()->get('passwordRepeat'); + + $result = $this->validation->validate($dto); + if (!$result->isSuccess()) + { + $this->addErrors($result->getErrors()); + return false; + } + + // Логика создания пользователя... + } +} +``` + +## Автоматическая валидация через AutoWire + +Чтобы избежать дублирования кода преобразования запроса в DTO, рекомендуется добавить статический фабричный метод в DTO: + +```php +use Bitrix\Main\HttpRequest; +final class CreateUserDto +{ + // ... свойства и конструктор + + public static function createFromRequest(HttpRequest $request): self + { + return new static( + login: (string) $request->get('login'), + password: (string) $request->get('password'), + passwordRepeat: (string) $request->get('passwordRepeat'), + ); + } +} +``` + +Bitrix Framework предоставляет механизм автоматической инъекции и валидации параметров с помощью `ValidationParameter`: + +```php +use Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter; +class UserController extends Controller +{ + public function getAutoWiredParameters() + { + return [ + new ValidationParameter( + CreateUserDto::class, + fn() => CreateUserDto::createFromRequest($this->getRequest()), + ), + ]; + } + + public function createAction(CreateUserDto $dto): Result + { + // Метод вызовется только если $dto прошёл валидацию. + // В противном случае контроллер вернёт ошибку в формате JSON: + // { + // "data": null, + // "errors": [...], + // "status": "error" + // } + + // Логика создания пользователя... + } +} +``` + +Если объект DTO не проходит валидацию, метод действия **не выполняется**, а контроллер автоматически возвращает клиенту список ошибок. Это позволяет полностью отделить логику валидации от бизнес-кода и упростить обработку некорректных запросов. \ No newline at end of file diff --git a/pages/framework/validation/main.md b/pages/framework/validation/main.md new file mode 100644 index 0000000..4cf4909 --- /dev/null +++ b/pages/framework/validation/main.md @@ -0,0 +1,256 @@ +--- +title: Валидация. Основное +description: 'Валидация. Документация по Bitrix Framework: принципы работы, архитектура и примеры использования.' +--- + +Валидация данных — это проверка входной информации на соответствие ожидаемым правилам. Например, числовой идентификатор должен быть положительным, а email соответствовать формату адреса. + +В Bitrix Framework валидацию можно выполнять вручную, но такой подход быстро приводит к дублированию кода и усложняет поддержку. Гораздо удобнее использовать встроенную систему валидации на основе php-атрибутов: она позволяет описать правила прямо в классе и централизованно их проверить. + +## Разбираемся на примере + +Представим, что нужно создать пользователя с полями email и телефон, при этом должны выполняться условия: +- Если указан email — он должен быть корректным. +- Если указан телефон — он должен быть корректным. +- Хотя бы одно из этих полей обязательно для заполнения. + +Это может выглядть следующим образом: +```php +use Bitrix\Main\Result; +use Bitrix\Main\Error; + +class UserService +{ + public function create(array $userData): Result + { + $createResult = new Result(); + + $emailOrPhoneExist = false; + if ( + array_key_exists('email', $userData) + && !is_null($userData['email']) + && is_string($userData['email']) + )) { + $emailOrPhoneExist = true; + if (!check_email($userData['email'])) { + $createResult->addError(new Error( + "E-mail заполнен некорректно" + )); + } + } + + if ( + array_key_exists('phone', $userData) + && !is_null($userData['phone']) + && is_string($userData['phone']) + )) { + $emailOrPhoneExist = true; + // Обратите внимание: функции check_phone() в ядре Bitrix нет — + // это лишь условный пример ручной проверки. + if (!check_phone($userData['phone'])) { + $createResult->addError(new Error( + "Телефон заполнен некорректно" + )); + } + } + + if (!$emailOrPhoneExist) { + $createResult->addError(new Error( + "Телефон или email обязателен к заполнению" + )); + } + + if (!$createResult->isSuccess()) { + return $createResult; + } + + // other logic ... + } +} +``` + +Этот код работает, но: +- содержит повторяющуюся логику, +- сложно расширять, +- смешивает проверку данных и бизнес-логику. + +Перепишем этот код с использованием валидаторов. + +{% note warning %} + +Система валидации Bitrix Framework работает с объектами, а не с массивами, нам придется создать и использовать специальный класс для передачи данных — его называют DTO (Data Transfer Object). + +{% endnote %} + +Создадим простую структуру для передачи данных в нашу функцию: + +```php +class CreateUser +{ + private ?string $email; + private ?string $phone; + + // getters & setters ... +} +``` + +Теперь добавим атрибуты с правилами валидации: `#[AtLeastOnePropertyNotEmpty]`, `#[Email]` и `#[Phone]`. +В системе существуют и другие правила валидации, но для демонстрации нашего примера достаточно и этих. +```php +use Bitrix\Main\Validation\Rule\AtLeastOnePropertyNotEmpty; +use Bitrix\Main\Validation\Rule\Email; +use Bitrix\Main\Validation\Rule\Phone; + +#[AtLeastOnePropertyNotEmpty(['email', 'phone'])] +class CreateUser +{ + #[Email] + private ?string $email; + + #[Phone] + private ?string $phone; + + // getters & setters... +} +``` + +Теперь вся наша проверка сведется к передачи значения сервису валидации: +```php +use Bitrix\Main\Result; +use Bitrix\Main\DI\ServiceLocator; +use Bitrix\Main\Validation\ValidationService; + +class UserService +{ + private ValidationService $validation; + + public function __construct() + { + $this->validation = ServiceLocator::getInstance()->get('main.validation.service'); + } + + public function create(CreateUser $userData): Result + { + $result = $this->validation->validate($userData); + if (!$result->isSuccess()) { + return $result; + } + + // other logic ... + } + +} +``` + +{% cut "Примечание к переходу на валидацию с использованием сервиса" %} + +Если вы не хотите или не можете изменять сигнатуру метода, можно создавать DTO внутри: +```php +public function create(array $userData): Result +{ + $createUser = new CreateUser(); + $createUser->setEmail($userData['email']); + $createUser->setPhone($userData['phone']); + + // ... +} +``` +{% endcut %} + + +## Как это работает? + +В системе валидации используются два ключевых понятия: + +- **Валидатор** — это объект реализующий интерфейс `\Bitrix\Main\Validation\Validator\ValidatorInterface`, который проверяет конкретное значение. Он не зависит от имени свойства или класса. Например, `EmailValidator` проверяет, соответствует ли значение формату email. +- **Правило** — это php-аттрибут, применяющийся к свойству или классу и определяющий: *какие валидаторы использовать*, *в каком контексте* и *с какими настройками*. + +Таким образом: +- валидатор — это механизм проверки, +- правило — это инструкция, где и как его использовать. + +{% note warning %} + +- Валидация работает через рефлексию: модификаторы доступа игнорируются. + +- Если свойство помечено как `nullable` и не было явно установлено, оно пропускается при валидации. + +- Если вы присвоили `null` явно — свойство считается инициализированным, и валидация к нему **применяется**. + +{% endnote %} + +## Рекурсивная валидация + +Используйя атрибут `#[Validatable]` можно так же добиться проверки вложенных объектов. + +```php +use Bitrix\Main\Validation\Rule\Recursive\Validatable; +use Bitrix\Main\Validation\Rule\NotEmpty; +use Bitrix\Main\Validation\Rule\PositiveNumber; +use Bitrix\Main\DI\ServiceLocator; +use Bitrix\Main\Validation\ValidationService; + +class Buyer +{ + #[PositiveNumber] + public ?int $id; + #[Validatable] + public ?Order $order; +} + +class Order +{ + #[PositiveNumber] + public int $id; + #[Validatable] + public ?Payment $payment; +} + +class Payment +{ + #[NotEmpty] + public string $status; + #[NotEmpty(errorMessage: 'Custom message error')] + public string $systemCode; +} + +$payment = new Payment(); +$payment->status = ''; +$payment->systemCode = ''; + +$order = new Order(); +$order->id = -1; +$order->payment = $payment; + +$buyer = new Buyer(); +$buyer->id = 0; +$buyer->order = $order; + +$result = ServiceLocator::getInstance() + ->get('main.validation.service') + ->validate($buyer); + +// id: Значение поля должно быть не меньше, чем 1 +// order.id: Значение поля должно быть не меньше, чем 1 +// order.payment.status: Значение поля не может быть пустым +// order.payment.systemCode: Custom message error +foreach ($result->getErrors() as $error) { + echo $error->getCode() . ': ' . $error->getMessage(). PHP_EOL; +} +``` + +## Валидаторы без атрибутов + +Валидаторы можно применять без атрибутов для разовой проверки данных, когда нет необходимости описывать правила в объекте. Это подходит для старого кода с массивами и нетипизированными переменными. + +```php +use Bitrix\Main\Validation\Validator\EmailValidator; + +$email = 'bitrix@bitrix.ru'; + +$validator = new EmailValidator(); +$result = $validator->validate($email); +if (!$result->isSuccess()) { + // ... +} +``` diff --git a/pages/framework/validation/own.md b/pages/framework/validation/own.md new file mode 100644 index 0000000..11eda19 --- /dev/null +++ b/pages/framework/validation/own.md @@ -0,0 +1,256 @@ +--- +title: Валидация. Собственные правила и валидаторы +description: 'Валидация. Документация по Bitrix Framework: собственные правила валидации' +--- + +Иногда встроенных валидаторов и правил недостаточно для реализации специфической бизнес-логики. В таких случаях Bitrix Framework позволяет расширять возможности системы создавая собственные валидаторы и правила. + +## Валидатор + +Валидатор выполняет простую задачу - проверяет значение. Он не определяет, относится ли значение к свойству или классу, и не зависит от атрибутов. + +Создать валидатор очень просто: создайте класс реализующий интерфейс `\Bitrix\Main\Validation\Validator\ValidatorInterface` с публичный методом `validate(mixed $value): ValidationResult`. +Вы можете добавить в конструктор класса необходимые параметры, если ваш валидатор это подразумевает. +В методе `validate` создать объект `ValidationResult`, который будет хранить результаты проверки. +Выполнить необходимые проверки и добавить в `ValidationResult` ошибки. + +Пример валидатора для определения является ли значение UUID версии 4: + +```php +use Bitrix\Main\Localization\Loc; +use Bitrix\Main\Validation\ValidationError; +use Bitrix\Main\Validation\ValidationResult; +use Bitrix\Main\Validation\Validator\ValidatorInterface; + +class UUIDv4Validator implements ValidatorInterface +{ + public function validate(mixed $value): ValidationResult + { + $result = new ValidationResult(); + + if ( + !is_string($uuid) + || preg_match('/^[a-f\d]{8}(-[a-f\d]{4}){4}[a-f\d]{8}$/i', $uuid) !== 1 + ) { + $result->addError(new ValidationError( + message: Loc::getMessage('FUSION_VALIDATION_VALIDATOR_UUIDV4_NOT_VALID'), + failedValidator: $this + )); + return $result; + } + + return $result; + } +} +``` + +> В разработке валидаторов старайтесь придерживаться правила fail fast - не ждите пока будут выполнены все проверки, если хотя бы один из критериев не соответствует добавляйте ошибку и возвращайте не успешный результат валидации. + + +## Правила + +В отличии от валидаторов правила не так универсальны и просты, поскольку зависят от применения: к свойствам и к классу. В Bitrix Framework для этого требуется реализация разных интерфейсов. + +### Правила для свойства + +Правило для свойства реализует интерфейс `Bitrix\Main\Validation\Rule\PropertyValidationAttributeInterface` с публичным методом `validateProperty(mixed $propertyValue): ValidationResult;`. + +Пример простого правила для свойства объекта: +```php +use Attribute; +use Bitrix\Main\Validation\Rule\PropertyValidationAttributeInterface; +use Bitrix\Main\Validation\ValidationError; +use Bitrix\Main\Validation\ValidationResult; + +#[Attribute(Attribute::TARGET_PROPERTY)] +class UUIDv4 implements PropertyValidationAttributeInterface +{ + public function validateProperty(mixed $propertyValue): ValidationResult + { + $result = new ValidationResult(); + + if ( + !is_string($propertyValue) + || preg_match('/^[a-f\d]{8}(-[a-f\d]{4}){4}[a-f\d]{8}$/i', $propertyValue) !== 1 + ) { + $result->addError(new ValidationError('Значение не должно быть равно 1')); + return $result; + } + + return $result; + } +} +``` + +Что происходит в этом коде? +Мы описывает класс `UUIDv4`, реализующий интерфейрс `PropertyValidationAttributeInterface` (валидация свойства), который проверяет что указанное значение явялется UUID версии 4. Мы указали что данный класс может являться php-атрибутом чтобы была возможность использовать его в качестве атрибута на проверяемом объекте. + +И хотя этого кода вполне достаточно для расширения системы использовать его все равно не очень удобно. +Что не так с этим правилом? +1. Отсутствие валидаторов. Без валидаторов мы переносим проблему дублирования кода контроллеров/сервисов в правила. +2. Отсутствие возможности изменить текст ошибки. + +Для удобства разработчиков был создан абстрактный класс `Bitrix\Main\Validation\Rule\AbstractPropertyValidationAttribute` позволяющий легко избавиться от этих недостатков. + +```php +use Attribute; +use Bitrix\Main\Localization\LocalizableMessageInterface; +use UUIDv4Validator; + +#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)] +class UUIDv4 extends AbstractPropertyValidationAttribute +{ + public function __construct( + protected string|LocalizableMessageInterface|null $errorMessage = null + ) { + } + + protected function getValidators(): array + { + return [ + new UUIDv4Validator(), + ]; + } +} + +``` + +> Наследование `AbstractPropertyValidationAttribute` позволяет вам вернуть одно сообщение об ошибке errorMessage, вместо стандартных ответов валидатора + +### Правила для класса + +Подобно атрибуту, правило для класса так же имеет свой интерфейс `Bitrix\Main\Validation\Rule\ClassValidationAttributeInterface` с похожим методом `public function validateObject(object $object): ValidationResult;` и так же имеет абстрактный класс для упрощения создания своих правил - `Bitrix\Main\Validation\Rule\AbstractClassValidationAttribute`. + +Рассмотрим пример создания правила для класса реализующего логику интервала дат + +```php +use Attribute; +use Bitrix\Main\Validation\Rule\AbstractClassValidationAttribute; +use Bitrix\Main\Localization\LocalizableMessage; +use Bitrix\Main\Localization\LocalizableMessageInterface; +use Bitrix\Main\Validation\ValidationError; +use Bitrix\Main\Validation\ValidationResult; +use Bitrix\Main\Type\DateTime; +use ReflectionClass; +use ReflectionProperty; +use Bitrix\Main\Localization\Loc; + +Loc::loadMessages(__FILE__); + +#[Attribute(Attribute::TARGET_CLASS)] +class DateInterval extends AbstractClassValidationAttribute +{ + public function __construct( + private readonly string $startDateProperty, + private readonly string $endDateProperty, + private readonly bool $allowSameDate = false, + private readonly bool $allowNullDate = false, + protected string|LocalizableMessageInterface|null $errorMessage = null + ) {} + + public function validateObject(object $object): ValidationResult + { + $result = new ValidationResult(); + + $properties = $this->getProperties($object); + + if (empty($properties)) { + $result->addError(new ValidationError( + new LocalizableMessage( + "FUSION_INTRANET_VALIDATION_RULE_DATE_INTERVAL_EMPTY_PROPERTIES", + ), + )); + + return $this->replaceWithCustomError($result); + } + + $values = $this->getValues($object, ...$properties); + + $leftValue = $values[$this->startDateProperty] ?? null; + $rightValue = $values[$this->endDateProperty] ?? null; + + if ($leftValue === null) { + if ($this->allowNullDate) { + $leftValue = DateTime::createFromTimestamp(0); + } else { + $result->addError(new ValidationError( + new LocalizableMessage( + "FUSION_INTRANET_VALIDATION_RULE_DATE_INTERVAL_LEFT_BORDER_REQUIRED", + ), + )); + } + } + + if ($rightValue === null) { + if ($this->allowNullDate) { + $rightValue = DateTime::createFromTimestamp( + strtotime("9999-12-31 23:59:59"), + ); + } else { + $result->addError(new ValidationError( + new LocalizableMessage( + "FUSION_INTRANET_VALIDATION_RULE_DATE_INTERVAL_RIGHT_BORDER_REQUIRED", + ), + )); + } + } + + if (!$result->isSuccess()) { + return $this->replaceWithCustomError($result); + } + + $cmp = $rightValue <=> $leftValue; + if ($cmp < 0) { + $result->addError(new ValidationError( + new LocalizableMessage( + "FUSION_INTRANET_VALIDATION_RULE_DATE_INTERVAL_DATE_INTERVAL_INVALID", + ), + )); + } elseif ($cmp === 0 && !$this->allowSameDate) { + $result->addError(new ValidationError( + new LocalizableMessage( + "FUSION_INTRANET_VALIDATION_RULE_DATE_INTERVAL_DATE_INTERVAL_SAME_NOT_ALLOWED", + ), + )); + } + + return $this->replaceWithCustomError($result); + } + + private function getProperties(object $object): array + { + $reflection = new ReflectionClass($object); + + return array_filter( + $reflection->getProperties(), + fn(ReflectionProperty $property): bool => in_array( + $property->getName(), + [$this->startDateProperty, $this->endDateProperty], + true, + ), + ); + } + + private function getValues( + object $object, + ReflectionProperty ...$properties, + ): array { + $values = []; + foreach ($properties as $property) { + if ($property->isInitialized($object)) { + $values[$property->getName()] = $property->getValue($object); + } else { + $values[$property->getName()] = null; + } + } + + return $values; + } +} + + +``` + +> Наследование `AbstractClassValidationAttribute` позволяет вам вернуть одно сообщение об ошибке errorMessage, вместо стандартных ответов валидатора + + From 7c43fa8823428fca2a60606baa2db2ba99df7b54 Mon Sep 17 00:00:00 2001 From: Andrei Nikolaev Date: Tue, 27 Jan 2026 22:26:45 +0300 Subject: [PATCH 2/2] Small changes and new article with exist rules --- pages/framework/validation.md | 122 ----- pages/framework/validation/controller.md | 139 ++++-- pages/framework/validation/exist.md | 588 +++++++++++++++++++++++ pages/framework/validation/main.md | 44 ++ pages/framework/validation/own.md | 10 +- toc.yaml | 10 +- 6 files changed, 732 insertions(+), 181 deletions(-) delete mode 100644 pages/framework/validation.md create mode 100644 pages/framework/validation/exist.md diff --git a/pages/framework/validation.md b/pages/framework/validation.md deleted file mode 100644 index 0f5e327..0000000 --- a/pages/framework/validation.md +++ /dev/null @@ -1,122 +0,0 @@ - -### Сообщение об ошибке после валидации - -Можно указать свой текст ошибки, который будет возвращен после валидации. - -```php -use Bitrix\Main\Validation\Rule\PositiveNumber; -class User -{ - public function __construct( - #[PositiveNumber(errorMessage: 'Invalid ID!')] - public readonly int $id - ) - { - } -} -$user = new User(-150); -/** @var \Bitrix\Main\Validation\ValidationService $service */ -$result = $service->validate($user); -foreach ($result->getErrors() as $error) -{ - echo $error->getMessage(); -} -// output: 'Invalid ID!' -``` - -Стандартный текст ошибки валидатора: - -```php -use Bitrix\Main\Validation\Rule\PositiveNumber; -class User -{ - public function __construct( - #[PositiveNumber] - public readonly int $id - ) - { - } -} -$user = new User(-150); -/** @var \Bitrix\Main\Validation\ValidationService $service */ -$result = $service->validate($user); -foreach ($result->getErrors() as $error) -{ - echo $error->getMessage(); -} -// output: 'Значение поля меньше допустимого' -``` - -### Получить сработавший валидатор - -Результат валидации хранит ошибки `\Bitrix\Main\Validation\ValidationError`. Каждая ошибка содержит свойство `failedValidator`. - -```php -$errors = $service->validate($dto)->getErrors(); -foreach ($errors as $error) -{ - $failedValidator = $error->getFailedValidator(); - // ... -} -``` - -### Доступные атрибуты и валидаторы - -Bitrix Framework предоставляет готовые атрибуты и валидаторы для самых частых сценариев проверки данных. - -Свойства: - -- `ElementsType` -- проверка типа элементов массива, - -- `Email` -- валидация email, - -- `InArray` -- значение входит в массив допустимых значений, - -- `Length` -- проверка длины строки, - -- `Max` -- максимальное значение, - -- `Min` -- минимальное значение, - -- `NotEmpty` -- не пустое значение, - -- `Phone` -- валидация телефона, - -- `PhoneOrEmail` -- телефон или email, - -- `PositiveNumber` -- положительное число, - -- `Range` -- значение в диапазоне, - -- `RegExp` -- регулярное выражение, - -- `Url` -- валидный URL, - -- `Json` -- валидный JSON. - -Класс: - -`AtLeastOnePropertyNotEmpty` -- хотя бы одно свойство не пусто. - -Валидаторы: - -- `EmailValidator` -- валидация email, - -- `InArrayValidator` -- проверка вхождения в массив, - -- `LengthValidator` -- проверка длины строки, - -- `MaxValidator` -- максимальное значение, - -- `MinValidator` -- минимальное значение, - -- `NotEmptyValidator` -- не пустое значение, - -- `PhoneValidator` -- валидация телефона, - -- `RegExpValidator` -- проверка по регулярному выражению, - -- `UrlValidator` -- валидация URL, - -- `JsonValidator` -- валидация JSON. - diff --git a/pages/framework/validation/controller.md b/pages/framework/validation/controller.md index 6a3545c..ddcf470 100644 --- a/pages/framework/validation/controller.md +++ b/pages/framework/validation/controller.md @@ -1,75 +1,112 @@ --- -title: Валидация. Контроллеры +title: Валидация в контроллерах description: 'Валидация. Документация по Bitrix Framework: использование в контроллерах' --- -В контроллерах валидация используется для проверки корректности данных, поступающих из HTTP-запроса. Это позволяет убедиться, что входные параметры соответствуют ожидаемым форматам и бизнес-правилам до выполнения основной логики действия. +Валидация входящих данных — обязательный этап не только для слоя бизнес-функций, но и обработки HTTP-запроса. Она гарантирует, что запрос с несоответствующими ожидаемым форматам и правилам параметры будет остановлен с ошибкой раньше. -Для валидации в контроллере создаётся DTO-класс с атрибутами правил: +Передача валидации на уровень контроллера (с использованием PHP-атрибутов и встроенного механизма) позволяет избавиться от ручных проверок внутри методов. Это делает код чище, снижает риск ошибок и гарантирует унифицированный формат ответов с ошибками для клиентской части. -```php -use Bitrix\Main\Validation\Rule\NotEmpty; -use Bitrix\Main\Validation\Rule\PhoneOrEmail; +## Валидация простых типов данных -final class CreateUserDto +Для проверки скалярных значений (чисел, строк) достаточно добавить атрибут валидации к аргументу метода действия. +Рассмотрим пример, где идентификатор пользователя должен быть положительным числом. + +**Ручная проверка (устаревший подход):** + +```php +class AwardController extends Controller { - public function __construct( - #[PhoneOrEmail] - public ?string $login = null, - - #[NotEmpty] - public ?string $password = null, - - #[NotEmpty] - public ?string $passwordRepeat = null, - ) {} + public function getByUserIdAction(int $userId): array + { + $awards = []; + + if ($userId <= 0) { + throw new Exception("Неправильно указан идентификатор пользователя"); + } + + // Логика получения данных... + + return $awards; + } } ``` -Затем в контроллере данные из запроса передаются в DTO, и выполняется валидация: +**Валидация через атрибуты:** + +Используйте атрибут `#[PositiveNumber]`. Контроллер автоматически проверит значение до выполнения метода. ```php -use Bitrix\Main\DI\ServiceLocator; -use Bitrix\Main\Validation\ValidationService; +use Bitrix\Main\Validation\Rule\PositiveNumber; -class UserController extends Controller +class AwardController extends Controller { - private ValidationService $validation; - - protected function init() + public function getByUserIdAction( + #[PositiveNumber] + int $userId + ): array { - parent::init(); - $this->validation = ServiceLocator::getInstance()->get('main.validation.service'); + $awards = []; + + // Логика получения данных... + + return $awards; } - - public function createAction(): Result +} +``` + +Если валидация не пройдет, метод не выполнится, а клиент получит ошибку в стандартном формате. + +Пример AJAX-запроса с невалидными данными: + +```js +BX.ajax.runAction( + 'fusion:badge.Award.getByUserId', { - $dto = new CreateUserDto(); - $dto->login = (string)$this->getRequest()->get('login'); - $dto->password = (string)$this->getRequest()->get('password'); - $dto->passwordRepeat = (string)$this->getRequest()->get('passwordRepeat'); - - $result = $this->validation->validate($dto); - if (!$result->isSuccess()) - { - $this->addErrors($result->getErrors()); - return false; + data: { + userId: -1 } - - // Логика создания пользователя... } +); +``` + +Ответ сервера: + +```json +{ + "status":"error", + "data":null, + "errors":[ + { + "message":"Invalid value to match parameter: [userId] Значение поля должно быть не меньше, чем 1.", + "code":100, + "customData":null + } + ] } ``` ## Автоматическая валидация через AutoWire -Чтобы избежать дублирования кода преобразования запроса в DTO, рекомендуется добавить статический фабричный метод в DTO: +Чтобы избежать ручного заполнения DTO (Data Transfer Object) из запроса в каждом действии, используется механизм `AutoWire` с параметром `ValidationParameter`. ```php use Bitrix\Main\HttpRequest; +use Bitrix\Main\Validation\Rule\NotEmpty; +use Bitrix\Main\Validation\Rule\PhoneOrEmail; + final class CreateUserDto { - // ... свойства и конструктор + public function __construct( + #[PhoneOrEmail] + public ?string $login = null, + + #[NotEmpty] + public ?string $password = null, + + #[NotEmpty] + public ?string $passwordRepeat = null, + ) {} public static function createFromRequest(HttpRequest $request): self { @@ -82,10 +119,11 @@ final class CreateUserDto } ``` -Bitrix Framework предоставляет механизм автоматической инъекции и валидации параметров с помощью `ValidationParameter`: +Затем подключите фабричный метод в контроллере через `getAutoWiredParameters`: ```php use Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter; + class UserController extends Controller { public function getAutoWiredParameters() @@ -97,20 +135,15 @@ class UserController extends Controller ), ]; } - - public function createAction(CreateUserDto $dto): Result + + public function createAction(CreateUserDto $dto): ?array { - // Метод вызовется только если $dto прошёл валидацию. - // В противном случае контроллер вернёт ошибку в формате JSON: - // { - // "data": null, - // "errors": [...], - // "status": "error" - // } + // Метод выполнится только если $dto прошел валидацию. + // Иначе контроллер автоматически вернет ошибку. // Логика создания пользователя... } } ``` -Если объект DTO не проходит валидацию, метод действия **не выполняется**, а контроллер автоматически возвращает клиенту список ошибок. Это позволяет полностью отделить логику валидации от бизнес-кода и упростить обработку некорректных запросов. \ No newline at end of file +Если данные в запросе не соответствуют правилам валидации, указанным в DTO, действие `createAction` не будет вызвано, а клиенту сразу вернется JSON с перечнем ошибок. \ No newline at end of file diff --git a/pages/framework/validation/exist.md b/pages/framework/validation/exist.md new file mode 100644 index 0000000..39b5291 --- /dev/null +++ b/pages/framework/validation/exist.md @@ -0,0 +1,588 @@ +--- +title: Валидация. Существующие правила +description: 'Валидация. Документация по Bitrix Framework: собственные правила валидации' +--- + +Bitrix Framework предоставляет готовые атрибуты и валидаторы для самых частых сценариев проверки данных. + +## Для классов + +### `AtLeastOnePropertyNotEmpty` + +Проверяет заполненность хотя бы одного аттрибута. + +Полный класс правила: `Bitrix\Main\Validation\Rule\AtLeastOnePropertyNotEmpty` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\AtLeastOneNotEmptyValidator` + +Сигнатура метода: +```php +__construct( + private readonly array $propertyNames, + private readonly bool $allowZero = false, + private readonly bool $allowEmptyString = false, + protected string|LocalizableMessageInterface|null $errorMessage = null, +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\AtLeastOnePropertyNotEmpty; +use Bitrix\Main\Validation\Rule\PositiveNumber; + +#[AtLeastOnePropertyNotEmpty( + propertyNames: ['id', 'uuid'], + errorMessage: "Нельзя однозначно идентифицировать пользователя" +)] +class UpdateUserName +{ + public function __construct( + #[PositiveNumber] + public ?int $id = null, + public ?string $uuid = null, + public string $fullName + ) {} +} +``` + +### `OnlyOneOfPropertyRequired` + +Проверяет, что ровно одно из указанных свойств объекта содержит непустое значение (пустыми считаются: `null`, `0`, `''`, `false` и пустые массивы). + +Полный класс правила: `Bitrix\Main\Validation\Rule\OnlyOneOfPropertyRequired` +Используемые валидаторы: Отсутствуют (валидация реализована внутри правила) + +Сигнатура метода: +```php +__construct( + private readonly array $propertyNames, + protected string|LocalizableMessageInterface|null $errorMessage = null, +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\OnlyOneOfPropertyRequired; +use Bitrix\Main\Validation\Rule\PositiveNumber; +use Bitrix\Main\Validation\Rule\Email; + +#[OnlyOneOfPropertyRequired( + propertyNames: ['userId', 'email'], + errorMessage: 'Укажите ТОЛЬКО один идентификатор: либо userId, либо email' +)] +class UserLookupRequest +{ + #[PositiveNumber] + public ?int $userId = null; + + #[Email] + public ?string $email = null; + + public ?string $newEmail = null; +} +``` + +Поле `newEmail` не участвует в проверке, так как не указано в `propertyNames`. + + +## Для свойств и параметров + +### `ElementsType` + +Проверяет, что все элементы в массиве (или итерируемом объекте) соответствуют указанному скалярному типу данных или являются экземплярами определенного класса. + +Полный класс правила: `Bitrix\Main\Validation\Rule\ElementsType` +Используемые валидаторы: +- Встроенная логика проверки (без подключения отдельных классов валидаторов) + +Сигнатура метода: +```php +__construct( + private readonly ?Type $typeEnum = null, + private readonly ?string $className = null, + string|LocalizableMessageInterface|null $errorMessage = null +) +``` + +Доступные типы: +- `Type::Integer` - проверка осуществляется php функцией `is_int` +- `Type::String` - проверка осуществляется php функцией `is_string` +- `Type::Float` - проверка осуществляется php функцией `is_float` +- `Type::Numeric` - проверка осуществляется php функцией `is_numeric` + +Пример использования (проверка скалярного типа): +```php +use Bitrix\Main\Validation\Rule\ElementsType; +use Bitrix\Main\Validation\Rule\Enum\Type; + +class UpdateUserRolesDto +{ + public function __construct( + public int $userId, + + #[ElementsType( + typeEnum: Type::Integer, + errorMessage: "ID ролей должны быть целыми числами" + )] + public array $roleIds + ) {} +} +``` + +Пример использования (проверка класса): +```php +use Bitrix\Main\Validation\Rule\ElementsType; +use Bitrix\Tasks\Item\Task; + +class ProcessTasksDto +{ + public function __construct( + #[ElementsType( + className: Task::class, + errorMessage: "Все элементы должны быть объектами Task" + )] + public array $tasks + ) {} +} +``` + +### `Email` + +Проверяет, что значение является корректным адресом электронной почты. + +Полный класс правила: `Bitrix\Main\Validation\Rule\Email` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\EmailValidator` + +Сигнатура метода: +```php +__construct( + private readonly bool $strict = false, + private readonly bool $domainCheck = false, + protected string|LocalizableMessageInterface|null $errorMessage = null +) +``` + +Параметры: +- `strict` (bool) - проверяет что значение содержит только почтовый адрес. Например "Ivan Ivanov " при нестрогой проверке успешно ее пройдет, а при строгой проверке даже пробел перед адресом электронной почты будет ошибкой. +- `domainCheck` (bool) - дополнительно проверят MX и A запись для почтового домена при помощи php функции `checkdnsrr` + + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\Email; + +class UserProfileDto +{ + public function __construct( + public string $name, + + #[Email( + errorMessage: "Пожалуйста, укажите корректный email" + )] + public string $email + ) {} +} +``` + +### `InArray` + +Проверяет, что значение присутствует в списке допустимых значений. + +Полный класс правила: `Bitrix\Main\Validation\Rule\InArray` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\InArrayValidator` + +Сигнатура метода: +```php +__construct( + private readonly array $validValues, + private readonly bool $strict = false, + protected string|LocalizableMessageInterface|null $errorMessage = null, +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\InArray; + +class TaskDto +{ + public function __construct( + public string $title, + + #[InArray( + validValues: ['low', 'medium', 'high'], + errorMessage: "Укажите корректный приоритет задачи" + )] + public string $priority + ) {} +} +``` + +### `Json` + +Проверяет, что значение является строкой, содержащей корректный JSON. + +Полный класс правила: `Bitrix\Main\Validation\Rule\Json` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\JsonValidator` + +Сигнатура метода: +```php +__construct() +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\Json; + +class WidgetSettingsDto +{ + public function __construct( + public string $widgetCode, + + #[Json] + public string $settings + ) {} +} +``` + +### `Length` + +Проверяет, что длина строки находится в заданном диапазоне. Можно задать только минимальную, только максимальную границу или обе сразу. + +Полный класс правила: `Bitrix\Main\Validation\Rule\Length` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\LengthValidator` + +Сигнатура метода: +```php +__construct( + private readonly ?int $min = null, + private readonly ?int $max = null, + protected string|LocalizableMessageInterface|null $errorMessage = null, +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\Length; + +class ChangePasswordDto +{ + public function __construct( + #[Length( + min: 8, + max: 50, + errorMessage: "Пароль должен содержать от 8 до 50 символов" + )] + public string $newPassword + ) {} +} +``` + +### `Max` + +Проверяет, что числовое значение меньше или равно указанному максимуму. + +Полный класс правила: `Bitrix\Main\Validation\Rule\Max` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\MaxValidator` + +Сигнатура метода: +```php +__construct( + private readonly int $max, + protected string|LocalizableMessageInterface|null $errorMessage = null +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\Max; + +class DiscountDto +{ + public function __construct( + public string $name, + + #[Max( + max: 100, + errorMessage: "Скидка не может превышать 100%" + )] + public int $discountPercent + ) {} +} +``` + +### `Min` + +Проверяет, что числовое значение больше или равно указанному минимуму. + +Полный класс правила: `Bitrix\Main\Validation\Rule\Min` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\MinValidator` + +Сигнатура метода: +```php +__construct( + private readonly int $min, + protected string|LocalizableMessageInterface|null $errorMessage = null +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\Min; + +class OrderDto +{ + public function __construct( + public string $itemName, + + #[Min( + min: 10, + errorMessage: "Минимальная сумма заказа должна быть не меньше 10" + )] + public int $totalAmount + ) {} +} +``` + +### `NotEmpty` + +Проверяет, что значение не является пустым. Позволяет настраивать допустимость нулевого значения и строк, содержащих только пробелы. + +Полный класс правила: `Bitrix\Main\Validation\Rule\NotEmpty` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\NotEmptyValidator` + +Сигнатура метода: +```php +__construct( + private readonly bool $allowZero = false, + private readonly bool $allowSpaces = false, + protected string|LocalizableMessageInterface|null $errorMessage = null +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\NotEmpty; + +class TaskDto +{ + public function __construct( + #[NotEmpty( + allowSpaces: false, + errorMessage: "Название задачи не может быть пустым") + ] + public string $title + ) {} +} +``` + +### `Phone` + +Проверяет, что значение является корректным номером телефона. + +Полный класс правила: `Bitrix\Main\Validation\Rule\Phone` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\PhoneValidator` + +Сигнатура метода: +```php +__construct( + protected string|LocalizableMessageInterface|null $errorMessage = null +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\Phone; + +class UserContactDto +{ + public function __construct( + public string $name, + + #[Phone(errorMessage: "Пожалуйста, укажите корректный номер телефона")] + public string $workPhone + ) {} +} +``` + +### `PhoneOrEmail` + +Проверяет, что значение является корректным номером телефона или адресом электронной почты. Валидация проходит успешно, если значение соответствует хотя бы одному из двух форматов. + +Полный класс правила: `Bitrix\Main\Validation\Rule\PhoneOrEmail` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\PhoneValidator` +- `Bitrix\Main\Validation\Validator\EmailValidator` + +Сигнатура метода: +```php +__construct( + private readonly bool $strict = false, + private readonly bool $domainCheck = false, + string|LocalizableMessageInterface|null $errorMessage = null +) +``` + +Параметры: +- `strict` (bool) - проверяет что значение содержит только почтовый адрес. Например "Ivan Ivanov " при нестрогой проверке успешно ее пройдет, а при строгой проверке даже пробел перед адресом электронной почты будет ошибкой. +- `domainCheck` (bool) - дополнительно проверят MX и A запись для почтового домена при помощи php функции `checkdnsrr` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\PhoneOrEmail; + +class UserContactDto +{ + public function __construct( + public string $name, + + #[PhoneOrEmail( + errorMessage: "Укажите корректный номер телефона или email" + )] + public string $contact, + ) {} +} +``` + +### `PositiveNumber` + +Проверяет, что числовое значение является строго положительным (больше нуля). + +Полный класс правила: `Bitrix\Main\Validation\Rule\PositiveNumber` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\MinValidator` + +Сигнатура метода: +```php +__construct( + protected string|LocalizableMessageInterface|null $errorMessage = null +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\PositiveNumber; + +class AwardController extends Controller +{ + public function getByUserIdAction( + #[PositiveNumber(errorMessage: "Идентификатор пользователя должен быть положительным числом")] + int $userId + ): array { + // ... + } +} +``` + +### `Range` + +Проверяет, что числовое значение находится в заданном диапазоне (от `min` до `max` включительно). + +Полный класс правила: `Bitrix\Main\Validation\Rule\Range` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\MinValidator` +- `Bitrix\Main\Validation\Validator\MaxValidator` + +Сигнатура метода: +```php +__construct( + private readonly int $min, + private readonly int $max, + protected string|LocalizableMessageInterface|null $errorMessage = null +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\Range; + +class OrderDto +{ + public function __construct( + public string $itemName, + + #[Range( + min: 1, + max: 100, + errorMessage: "Количество товара должно быть от 1 до 100" + )] + public int $quantity + ) {} +} +``` + +### `RegExp` + +Проверяет, что значение строки соответствует указанному регулярному выражению. + +Полный класс правила: `Bitrix\Main\Validation\Rule\RegExp` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\RegExpValidator` + +Сигнатура метода: +```php +__construct( + private readonly string $pattern, + private readonly int $flags = 0, + private readonly int $offset = 0, + protected string|LocalizableMessageInterface|null $errorMessage = null +) +``` + +Параметры `flags` и `offset` передаются в php-функицю `preg_match`. + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\RegExp; + +class ProductDto +{ + public function __construct( + public string $name, + + #[RegExp( + pattern: '/^[A-Z]{2}\d{4}$/', + errorMessage: "Артикул должен быть в формате: две заглавные буквы и четыре цифры" + )] + public string $sku + ) {} +} +``` + +### `Url` + +Проверяет, что значение является корректным URL-адресом. + +Полный класс правила: `Bitrix\Main\Validation\Rule\Url` +Используемые валидаторы: +- `Bitrix\Main\Validation\Validator\UrlValidator` + +Сигнатура метода: +```php +__construct( + protected string|LocalizableMessageInterface|null $errorMessage = null, +) +``` + +Пример использования: +```php +use Bitrix\Main\Validation\Rule\Url; + +class SiteFeedbackDto +{ + public function __construct( + public string $userName, + + #[Url(errorMessage: "Пожалуйста, укажите корректный адрес сайта")] + public string $userSite, + ) {} +} +``` diff --git a/pages/framework/validation/main.md b/pages/framework/validation/main.md index 4cf4909..bfdb07e 100644 --- a/pages/framework/validation/main.md +++ b/pages/framework/validation/main.md @@ -254,3 +254,47 @@ if (!$result->isSuccess()) { // ... } ``` + +## Сообщение об ошибке после валидации + +Можно указать свой текст ошибки, который будет возвращен после валидации. + +```php +use Bitrix\Main\Validation\Rule\PositiveNumber; + +class User +{ + public function __construct( + #[PositiveNumber(errorMessage: 'Invalid ID!')] + public readonly int $id, + #[PositiveNumber] + public readonly int $departmentId + ) + {} +} + +$user = new User( + id: -150, + departmentId: -1 +); + +/** @var \Bitrix\Main\Validation\ValidationService $service */ +$result = $service->validate($user); +foreach ($result->getErrors() as $error) { + echo $error->getMessage(); +} +// output: 'Invalid ID!' +// output: 'Значение поля меньше допустимого' +``` + +## Получить сработавший валидатор + +Результат валидации хранит ошибки `\Bitrix\Main\Validation\ValidationError`. Каждая ошибка содержит свойство `failedValidator`. + +```php +$errors = $service->validate($dto)->getErrors(); +foreach ($errors as $error) { + $failedValidator = $error->getFailedValidator(); + // ... +} +``` \ No newline at end of file diff --git a/pages/framework/validation/own.md b/pages/framework/validation/own.md index 11eda19..50a0fa8 100644 --- a/pages/framework/validation/own.md +++ b/pages/framework/validation/own.md @@ -29,8 +29,8 @@ class UUIDv4Validator implements ValidatorInterface $result = new ValidationResult(); if ( - !is_string($uuid) - || preg_match('/^[a-f\d]{8}(-[a-f\d]{4}){4}[a-f\d]{8}$/i', $uuid) !== 1 + !is_string($value) + || preg_match('/^[a-f\d]{8}(-[a-f\d]{4}){4}[a-f\d]{8}$/i', $value) !== 1 ) { $result->addError(new ValidationError( message: Loc::getMessage('FUSION_VALIDATION_VALIDATOR_UUIDV4_NOT_VALID'), @@ -62,7 +62,7 @@ use Bitrix\Main\Validation\Rule\PropertyValidationAttributeInterface; use Bitrix\Main\Validation\ValidationError; use Bitrix\Main\Validation\ValidationResult; -#[Attribute(Attribute::TARGET_PROPERTY)] +#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)] class UUIDv4 implements PropertyValidationAttributeInterface { public function validateProperty(mixed $propertyValue): ValidationResult @@ -73,7 +73,7 @@ class UUIDv4 implements PropertyValidationAttributeInterface !is_string($propertyValue) || preg_match('/^[a-f\d]{8}(-[a-f\d]{4}){4}[a-f\d]{8}$/i', $propertyValue) !== 1 ) { - $result->addError(new ValidationError('Значение не должно быть равно 1')); + $result->addError(new ValidationError('Значение должно быть корректным UUID v4')); return $result; } @@ -251,6 +251,6 @@ class DateInterval extends AbstractClassValidationAttribute ``` -> Наследование `AbstractClassValidationAttribute` позволяет вам вернуть одно сообщение об ошибке errorMessage, вместо стандартных ответов валидатора +> Наследование `AbstractClassValidationAttribute` позволяет вам вернуть одно сообщение об ошибке `errorMessage`, вместо стандартных ответов валидатора diff --git a/toc.yaml b/toc.yaml index bf67e27..5e55520 100644 --- a/toc.yaml +++ b/toc.yaml @@ -109,7 +109,15 @@ items: - name: Cookie-файлы href: /pages/framework/cookies.md - name: Валидация - href: /pages/framework/validation.md + items: + - name: Основное + href: /pages/framework/validation/main.md + - name: Существующие правила + href: /pages/framework/validation/exist.md + - name: Контроллеры + href: /pages/framework/validation/controller.md + - name: Свои правила + href: /pages/framework/validation/own.md - name: Результат работы и ошибки href: /pages/framework/results-and-errors.md - name: Компоненты