Skip to content

Validation

Learn how to validate DTOs using automatic rule inferring and validation attributes.

Validation ensures that data meets specific requirements before being processed. SimpleDTO provides:

  • Automatic rule inferring from types and attributes
  • 30+ validation attributes
  • Framework integration (Laravel, Symfony)
  • Custom validation rules
  • Validation caching (198x faster)
use Event4u\DataHelpers\SimpleDTO;
use Event4u\DataHelpers\SimpleDTO\Attributes\Required;
use Event4u\DataHelpers\SimpleDTO\Attributes\Email;
use Event4u\DataHelpers\SimpleDTO\Attributes\Between;
class UserDTO extends SimpleDTO
{
public function __construct(
#[Required]
public readonly string $name,
#[Required, Email]
public readonly string $email,
#[Required, Between(18, 120)]
public readonly int $age,
) {}
}
// Validate and create
$dto = UserDTO::validateAndCreate([
'name' => 'John Doe',
'email' => 'john@example.com',
'age' => 30,
]);
use Event4u\DataHelpers\SimpleDTO\Exceptions\ValidationException;
try {
$dto = UserDTO::validateAndCreate([
'name' => '', // ❌ Required
'email' => 'invalid', // ❌ Invalid email
'age' => 15, // ❌ Too young
]);
} catch (ValidationException $e) {
echo $e->getMessage();
print_r($e->errors());
}
use Event4u\DataHelpers\SimpleDTO\Attributes\Required;
class UserDTO extends SimpleDTO
{
public function __construct(
#[Required]
public readonly string $name,
#[Required]
public readonly string $email,
// Optional - no Required attribute
public readonly ?string $phone = null,
) {}
}
use Event4u\DataHelpers\SimpleDTO\Attributes\StringType;
use Event4u\DataHelpers\SimpleDTO\Attributes\Min;
use Event4u\DataHelpers\SimpleDTO\Attributes\Max;
use Event4u\DataHelpers\SimpleDTO\Attributes\Between;
class PostDTO extends SimpleDTO
{
public function __construct(
#[Required, StringType, Min(3)]
public readonly string $title,
#[Required, StringType, Between(10, 1000)]
public readonly string $content,
#[StringType, Max(100)]
public readonly ?string $excerpt = null,
) {}
}
use Event4u\DataHelpers\SimpleDTO\Attributes\IntegerType;
use Event4u\DataHelpers\SimpleDTO\Attributes\Numeric;
use Event4u\DataHelpers\SimpleDTO\Attributes\Min;
use Event4u\DataHelpers\SimpleDTO\Attributes\Max;
class ProductDTO extends SimpleDTO
{
public function __construct(
#[Required, IntegerType, Min(1)]
public readonly int $quantity,
#[Required, Numeric, Min(0)]
public readonly float $price,
#[IntegerType, Between(0, 100)]
public readonly ?int $discount = null,
) {}
}
use Event4u\DataHelpers\SimpleDTO\Attributes\Email;
class ContactDTO extends SimpleDTO
{
public function __construct(
#[Required, Email]
public readonly string $email,
#[Email]
public readonly ?string $alternativeEmail = null,
) {}
}
use Event4u\DataHelpers\SimpleDTO\Attributes\Url;
class WebsiteDTO extends SimpleDTO
{
public function __construct(
#[Required, Url]
public readonly string $website,
#[Url]
public readonly ?string $blog = null,
) {}
}
AttributeDescriptionExample
RequiredField is required#[Required]
EmailValid email address#[Email]
UrlValid URL#[Url]
MinMinimum value/length#[Min(3)]
MaxMaximum value/length#[Max(100)]
BetweenValue between min and max#[Between(18, 120)]
StringTypeMust be string#[StringType]
IntegerTypeMust be integer#[IntegerType]
NumericMust be numeric#[Numeric]
BooleanTypeMust be boolean#[BooleanType]
ArrayTypeMust be array#[ArrayType]
InValue in list#[In(['active', 'inactive'])]
NotInValue not in list#[NotIn(['banned'])]
RegexMatches regex#[Regex('/^[A-Z]/')]
AlphaOnly letters#[Alpha]
AlphaNumLetters and numbers#[AlphaNum]
AlphaDashLetters, numbers, dashes#[AlphaDash]
UuidValid UUID#[Uuid]
JsonValid JSON#[Json]
DateValid date#[Date]
DateFormatDate in format#[DateFormat('Y-m-d')]
BeforeDate before#[Before('2024-12-31')]
AfterDate after#[After('2024-01-01')]
IpValid IP address#[Ip]
Ipv4Valid IPv4#[Ipv4]
Ipv6Valid IPv6#[Ipv6]
MacAddressValid MAC address#[MacAddress]
TimezoneValid timezone#[Timezone]
UniqueUnique in database#[Unique('users', 'email')]
ExistsExists in database#[Exists('users', 'id')]
use Event4u\DataHelpers\SimpleDTO\Contracts\ValidationRule;
class EvenNumber implements ValidationRule
{
public function passes(mixed $value): bool
{
return is_int($value) && $value % 2 === 0;
}
public function message(): string
{
return 'The value must be an even number.';
}
}
class NumberDTO extends SimpleDTO
{
public function __construct(
#[EvenNumber]
public readonly int $number,
) {}
}
// Automatic Laravel validation
class UserController extends Controller
{
public function store(Request $request)
{
$dto = UserDTO::validateAndCreate($request->all());
// Automatically uses Laravel's validator
}
}
// Automatic Symfony validation
class UserController
{
public function store(Request $request)
{
$dto = UserDTO::validateAndCreate($request->request->all());
// Automatically uses Symfony's validator
}
}

SimpleDTO caches validation rules for 198x faster performance:

// First call - builds and caches rules
$dto1 = UserDTO::validateAndCreate($data1); // ~10ms
// Subsequent calls - uses cached rules
$dto2 = UserDTO::validateAndCreate($data2); // ~0.05ms (198x faster!)
// ✅ Good - multiple validation rules
#[Required, StringType, Min(3), Max(50), Alpha]
public readonly string $name;
// ✅ Good - type hint + validation
#[Required, Between(18, 120)]
public readonly int $age;
// ❌ Bad - no type hint
#[Required, Between(18, 120)]
public readonly $age;
// ✅ Good - validate at creation
$dto = UserDTO::validateAndCreate($data);
// ❌ Bad - create then validate
$dto = UserDTO::fromArray($data);
$dto->validate();

The following working examples demonstrate this feature:

All examples are fully tested and can be run directly.

The functionality is thoroughly tested. Key test files:

Run the tests:

Terminal window
# Run tests
task test:unit -- --filter=Validation