Документация для разработчиков
Темная тема

Использование валидации

Введение

Иногда необходимо проверить, что данные корректны, не привязываясь к бизнес-логике. Например, идентификатор пользователя не должен быть меньше 1.

public function __construct(int $userId)
{
    if ($userId <= 0)
    {
        throw new \Exception();
    }
    
    $this->userId = $userId;
}

Такие проверки могут увеличивать объем кода, поэтому для упрощения используется валидация через атрибуты.


Установка правил валидации

Рассмотрим пример класса пользователя.

final class User
{
    private ?int $id;
    private ?string $email;
    private ?string $phone;
        
    // getters & setters ...
}

Ограничения:

  • id больше 0,
  • email — валидный адрес,
  • phone — валидный телефон,
  • заполнен email или phone.

Добавим атрибуты валидации.

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 ...
}

Теперь можно валидировать через \Bitrix\Main\Validation\ValidationService, который доступен через локатор по ключу main.validation.service. Это позволяет валидировать класс в том месте, где нужно. Например, при сохранении в базу данных.

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 ...
    }
}

ValidationService предоставляет метод validate(), возвращающий ValidationResult. Результат валидации содержит ошибки всех сработавших валидаторов.

Важно:
  • Модификаторы доступа у свойств не учитываются, валидация происходит через рефлексию.
  • Если атрибут nullable и его значение не установлено, он будет пропущен при валидации.

Валидация вложенных объектов

Если объект сложный и содержит вложенные объекты, можно использовать атрибут \Bitrix\Main\Validation\Rule\Recursive\Validatable. Это укажет, что объект также должен быть провалидирован.

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;
}

Валидация в контроллерах

Пример валидации в контроллере.

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,
    )
    {}
}

В коде класс будет выглядеть так:

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:

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, который спрячет повторяющуюся логику валидации.

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 не выполнится. Контроллер вернет ошибку.

{
    data: null,
    errors:
    [
        {
            code: "name",
            customData: null,
            message: "Значение поля не должно быть пустым",
        },
    ],
    status: "error"
}

Валидаторы без атрибутов

Валидаторы можно использовать и без атрибутов.

use Bitrix\Main\Validation\Validator\EmailValidator;

$email = 'bitrix@bitrix.com';
$validator = new EmailValidator();
$result = $validator->validate($email);
if (!$result->isSuccess())
{
    // ...
}

Собственное сообщение об ошибке

Можно указать свой текст ошибки, который будет возвращен после валидации.

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!'

Стандартная ошибка валидатора:

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, которые содержат свойство $this->failedValidator.

$errors = $service->validate($dto)->getErrors();
foreach ($errors as $error)
{
    $failedValidator = $error->getFailedValidator();
    // ...
}

Список доступных атрибутов

Свойства:

  • ElementsType — все элементы перечисляемого свойства должны быть заданного типа
  • Email
  • InArray — значение свойства является одним из элементов массива (для случаев, когда по какой-то причине не удалось использовать Enum)
  • Length
  • Max
  • Min
  • NotEmpty
  • Phone
  • PhoneOrEmail — свойство является либо телефоном, либо почтой
  • PositiveNumber
  • Range
  • RegExp
  • Url
  • Json — свойство (строка) является Json

Классы:

  • AtLeastOnePropertyNotEmpty — проверяет, что хотя бы одно свойство из заданных не пустое (названия свойств прокидываются в конструктор)

Список доступных валидаторов

  • AtLeastOnePropertyNotEmpty — проверяет, что хотя бы одно свойство из заданных не пустое (названия свойств прокидываются в конструктор)
  • Email
  • InArray — значение свойства является одним из элементов массива (для случаев, когда по какой-то причине не удалось использовать Enum)
  • Length
  • Max
  • Min
  • NotEmpty
  • Phone
  • RegExp
  • Url
  • Json — значение переменной (строка) является Json


Пользовательские комментарии

Мы будем рады, если разработчики добавят свои комментарии по практическому использованию методов системы.

Для этого нужно всего лишь авторизоваться на сайте

Но помните, что Пользовательские комментарии, несмотря на модерацию, не являются официальной документацией. Ответственность за их использование несет сам пользователь.

Также Пользовательские комментарии не являются местом для обсуждения функционала. По подобным вопросам обращайтесь на форумы.
© «Битрикс», 2001-2025, «1С-Битрикс», 2025
Наверх