Loading...
Loading...
Covers input data validation in Bitrix — ValidationService (main.validation.service), attributes #[NotEmpty], #[Email], #[Length], #[Range], #[RegExp], #[InArray], Request DTO via ValidationParameter autowire, rule attributes on controller action parameters, custom validators via AbstractPropertyValidationAttribute + ValidatorInterface, aggregation of errors in ErrorCollection. Applied when checking input of controllers, services and CLI commands, validation of forms, DTO and action method parameters. Key terms — ValidationService, NotEmpty, Email, Length, ValidationParameter, Request DTO, validator, constraint.
npx skill4agent add bxmaximum/bitrix-framework-skills bitrix-validationBitrix\Main\Validation\ValidationServiceValidationResultServiceLocatormain.validation.servicevalidation.settings.php| Attribute | What it checks |
|---|---|
| Not empty ( |
| String length |
| Numeric constraints |
| Numeric value >= 1 (internally |
| Format; |
| URL (no options besides |
| Regular expression (attribute name is |
| Value is one of the allowed list items; options |
| String is valid JSON |
| Recursively validate nested object; |
| Type of array elements: |
| At least one of the fields is filled (on class) |
| Exactly one of the listed fields is filled (on class) |
errorMessagemessageBitrix\Main\Localization\LocalizableMessage('PHRASE_CODE', phraseSrcFile: __FILE__)lang/<code>/phraseSrcFilegroups: [...]ValidationService::validate($object, $group)MAIN_VALIDATION_EMPTY_PROPERTYnullnull<?php declare(strict_types=1);
namespace Vendor\Module\Application\Dto;
use Bitrix\Main\Validation\Rule\NotEmpty;
use Bitrix\Main\Validation\Rule\Length;
use Bitrix\Main\Validation\Rule\Email;
use Bitrix\Main\Validation\Rule\Range;
use Bitrix\Main\Validation\Rule\InArray;
final class CreateUserDto
{
public function __construct(
#[NotEmpty, Length(min: 2, max: 64)]
public readonly string $name,
#[NotEmpty, Email]
public readonly string $email,
#[Range(min: 18, max: 120)]
public readonly int $age,
#[InArray(['user', 'admin'])]
public readonly string $role,
) {}
}use Bitrix\Main\DI\ServiceLocator;
use Bitrix\Main\Validation\ValidationService;
final class UserService
{
private readonly ValidationService $validator;
public function __construct()
{
$this->validator = ServiceLocator::getInstance()->get('main.validation.service');
}
public function register(CreateUserDto $dto): \Bitrix\Main\Result
{
$result = new \Bitrix\Main\Result();
$validation = $this->validator->validate($dto);
if (!$validation->isSuccess())
{
foreach ($validation->getErrors() as $error)
{
// getCode() holds the property path: 'email', 'items.0.name'
$result->addError(new \Bitrix\Main\Error(
$error->getMessage(),
$error->getCode(),
));
}
return $result;
}
// ...
return $result;
}
}constructorParams'main.validation.service'use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Validation\Rule\PositiveNumber;
final class User extends Controller
{
public function getAction(#[PositiveNumber] int $id): array
{
return ['id' => $id];
}
}ValidationParametergetAutoWiredParameters()Bitrix\Main\Validation\Engine\AutoWire\ValidationParameteruse Bitrix\Main\Engine\Controller;
use Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter;
use Vendor\Module\Application\Service\PostService;
final class Post extends Controller
{
public function getAutoWiredParameters(): array
{
return [
new ValidationParameter(
CreatePostRequest::class,
fn () => CreatePostRequest::createFromRequest($this->getRequest()),
),
];
}
public function createAction(CreatePostRequest $request, PostService $postService): array
{
// We only get here if validation was successful.
// Otherwise, the controller will return errors automatically.
$result = $postService->create($request);
if (!$result->isSuccess())
{
$this->addErrors($result->getErrors());
return [];
}
return ['id' => $result->getId()];
}
}namespace Vendor\Blog\Application\Request;
use Bitrix\Main\Validation\Rule\NotEmpty;
use Bitrix\Main\Validation\Rule\Length;
final class CreatePostRequest
{
public function __construct(
#[NotEmpty, Length(min: 1, max: 255)]
public readonly ?string $title = null,
public readonly ?string $body = null,
) {}
public static function createFromRequest(\Bitrix\Main\Request $request): self
{
return new self(
$request->get('title'),
$request->get('body'),
);
}
}nullNotEmptyphp bitrix/bitrix.php make:request CreatePost -m vendor.blog --fields=title,bodyuse Bitrix\Main\Validation\Rule\AtLeastOnePropertyNotEmpty;
#[AtLeastOnePropertyNotEmpty(['email', 'phone'])]
final readonly class ContactRequest
{
public function __construct(
public ?string $email = null,
public ?string $phone = null,
) {}
}use Bitrix\Main\Validation\Rule\Recursive\Validatable;
use Bitrix\Main\Validation\Rule\ElementsType;
final class OrderDto
{
/**
* @var OrderItemDto[]
*/
#[ElementsType(className: OrderItemDto::class)]
#[Validatable(iterable: true)]
public array $items = [];
}.settings.phpvalidationAbstractPropertyValidationAttributegetValidators()getValidators()<?php declare(strict_types=1);
namespace Vendor\Module\Validation\Rule;
use Attribute;
use Bitrix\Main\Localization\LocalizableMessageInterface;
use Bitrix\Main\Validation\Rule\AbstractPropertyValidationAttribute;
use Bitrix\Main\Validation\Validator\ValidatorInterface;
use Bitrix\Main\Validation\ValidationResult;
use Bitrix\Main\Validation\ValidationError;
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_PARAMETER)]
final class EvenNumber extends AbstractPropertyValidationAttribute
{
public function __construct(
// type must match the inherited trait property exactly
protected string|LocalizableMessageInterface|null $errorMessage = null,
) {}
protected function getValidators(): array
{
// $this->errorMessage is applied automatically by the base class
// (replaceWithCustomError from ValidationErrorTrait)
return [
new EvenNumberValidator(),
];
}
}
final class EvenNumberValidator implements ValidatorInterface
{
public function validate(mixed $value): ValidationResult
{
$result = new ValidationResult();
if (!is_int($value) || $value % 2 !== 0)
{
$result->addError(new ValidationError(
'Number must be even',
'EVEN_NUMBER', // the property path is prepended later: 'age.EVEN_NUMBER'
failedValidator: $this,
));
}
return $result;
}
}#[EvenNumber(errorMessage: 'Age must be even')]
public readonly int $age;ValidatorInterface::validate(mixed $value): ValidationResultRule$errorMessagestring|LocalizableMessageInterface|nullValidationErrorTrait?stringAbstractClassValidationAttributeClassValidationAttributeInterface::validateObject(object $object)ValidationResultBitrix\Main\ResultValidationErrorBitrix\Main\ErrorgetMessage()getCode()emailitems.0.nameage.EVEN_NUMBERgetFailedValidator()ValidatorInterfacenullgetField()ValidationParametergetAutoWiredParameters()nullnullAbstractPropertyValidationAttributegetValidators().settings.phpvalidationRegExperrorMessageRegexmessageValidationServicemain.validation.service#[ElementsType(className: ...)]#[Validatable(iterable: true)]