-
Notifications
You must be signed in to change notification settings - Fork 0
Validation Attributes
In BOUNDLY, validation attributes are declarative rules that validate data at the API level. They automatically enforce type checking, format validation, and business rules without writing custom validation logic.
Infrastructure\FrameworkCore\Attributes\Validation\Validate data types and formats.
Validates RFC 5322 compliant email format.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Email;
#[Column(type: 'string', length: 255)]
#[Email]
private string $email;
// Valid: "user@example.com"
// Invalid: "not-an-email", "user@", "@domain.com"Validates HTTP, HTTPS, and FTP URLs.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Url;
#[Column(type: 'string', length: 500)]
#[Url]
private string $website;
// Valid: "https://example.com", "http://localhost:3000", "ftp://files.example.com"
// Invalid: "example.com", "www.example.com" (missing protocol)Validates IPv4 and/or IPv6 addresses.
| Parameter | Type | Default | Description |
|---|---|---|---|
allowIpv4 |
bool |
true |
Accept IPv4 addresses |
allowIpv6 |
bool |
true |
Accept IPv6 addresses |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\IpAddress;
#[Column(type: 'string', length: 45)]
#[IpAddress]
private string $ipAddress;
#[Column(type: 'string', length: 45)]
#[IpAddress(allowIpv4: true, allowIpv6: false)]
private string $ipv4Only;Validates UUID format.
| Parameter | Type | Default | Description |
|---|---|---|---|
version |
int |
4 |
UUID version (1, 4, or 5) |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Uuid;
#[Column(type: 'string', length: 36)]
#[Uuid(version: 4)]
private string $correlationId;
// Valid: "550e8400-e29b-41d4-a716-446655440000"
// Invalid: "not-a-uuid"Validates that the value is valid JSON.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Json;
#[Column(type: 'json')]
#[Json]
private string $metadata;
// Valid: '{"key": "value"}', "[1, 2, 3]", "null"
// Invalid: "{key: value}", "{'key': 'value'}"Validates ISO 8601 date format.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\IsoDate;
#[Column(type: 'datetime')]
#[IsoDate]
private string $eventDate;
// Valid: "2024-01-15", "2024-01-15T10:30:00Z", "2024-01-15T10:30:00+05:30"
// Invalid: "01/15/2024", "15-01-2024"Validates valid timezone identifiers.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Timezone;
#[Column(type: 'string', length: 50)]
#[Timezone]
private string $userTimezone;
// Valid: "America/New_York", "Europe/London", "UTC"
// Invalid: "EST", "GMT+5" (use full identifiers)Validates hexadecimal color codes.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\ColorHex;
#[Column(type: 'string', length: 7)]
#[ColorHex]
private string $brandColor;
// Valid: "#FF5733", "#fff", "#ABC123"
// Invalid: "red", "rgb(255,0,0)", "#GGGGGG"Validates URL-friendly slug format (lowercase, alphanumeric, hyphens only).
| Parameter | Type | Default | Description |
|---|---|---|---|
maxLength |
int |
255 |
Maximum slug length |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Slug;
#[Column(type: 'string', length: 200)]
#[Slug(maxLength: 100)]
private string $urlSlug;
// Valid: "hello-world", "python-312-released", "news-about-tech"
// Invalid: "Hello World", "hello_world", "hello@world"Validates MAC address format.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\MacAddress;
#[Column(type: 'string', length: 17)]
#[MacAddress]
private string $deviceMac;
// Valid: "00:1A:2B:3C:4D:5E", "00-1A-2B-3C-4D-5E", "001A2B3C4D5E"Validate numeric values and ranges.
Validates minimum numeric value.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Min;
#[Column(type: 'integer')]
#[Min(0)]
private int $quantity; // Must be >= 0
#[Column(type: 'decimal(10,2)')]
#[Min(0.01)]
private string $price; // Must be >= 0.01Validates maximum numeric value.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Max;
#[Column(type: 'integer')]
#[Max(100)]
private int $percentage; // Must be <= 100
#[Column(type: 'integer')]
#[Max(120)]
private int $age; // Must be <= 120Validates value is within a range (inclusive).
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Between;
#[Column(type: 'integer')]
#[Between(min: 1, max: 10)]
private int $rating; // Must be between 1 and 10
#[Column(type: 'decimal(10,2)')]
#[Between(min: 0.00, max: 999.99)]
private string $discount;Validates the value is strictly greater than zero.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Positive;
#[Column(type: 'integer')]
#[Positive]
private int $population; // Must be > 0Validates the value is strictly less than zero.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Negative;
#[Column(type: 'integer')]
#[Negative]
private int $temperatureCelsius; // Must be < 0Validates the value is a whole number (no decimals).
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Integer;
#[Column(type: 'string')]
#[Integer]
private string $userId; // Must be a whole number
// Valid: "123", "456789"
// Invalid: "12.34", "12.00", "abc"Validates decimal numbers with specific precision.
| Parameter | Type | Default | Description |
|---|---|---|---|
decimals |
int |
2 |
Number of decimal places |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Decimal;
#[Column(type: 'string')]
#[Decimal(decimals: 4)]
private string $exchangeRate; // Must have exactly 4 decimals
// Valid: "1.1234", "99.9999"
// Invalid: "1.12", "1.12345"Validate string content and format.
Validates minimum string length.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\MinLength;
#[Column(type: 'string', length: 100)]
#[MinLength(3)]
private string $username; // Must be at least 3 charactersValidates maximum string length.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\MaxLength;
#[Column(type: 'string', length: 255)]
#[MaxLength(50)]
private string $title; // Must be at most 50 charactersValidates string length is within a range.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\LengthBetween;
#[Column(type: 'string', length: 50)]
#[LengthBetween(min: 8, max: 32)]
private string $password; // Must be 8-32 charactersValidates only alphabetic characters (a-z, A-Z).
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Alpha;
#[Column(type: 'string', length: 50)]
#[Alpha]
private string $firstName; // Letters only
// Valid: "John", "MarΓa"
// Invalid: "John123", "John Doe"Validates only alphanumeric characters (a-z, A-Z, 0-9).
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Alphanumeric;
#[Column(type: 'string', length: 50)]
#[Alphanumeric]
private string $username;
// Valid: "user123", "JohnDoe", "abcXYZ"
// Invalid: "user@123", "john doe"Validates only numeric characters (0-9).
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Numeric;
#[Column(type: 'string', length: 20)]
#[Numeric]
private string $zipCode; // Numbers only
// Valid: "12345", "90210"
// Invalid: "123-45", "abc"Validates against a custom regular expression.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Pattern;
#[Column(type: 'string', length: 20)]
#[Pattern(regex: '^[A-Z]{3}[0-9]{4}$')]
private string $productCode; // ABC1234 format
// Valid: "ABC1234", "XYZ9999"
// Invalid: "abc1234", "AB1234", "ABC12345"Validates the string starts with a specific value.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\StartsWith;
#[Column(type: 'string', length: 50)]
#[StartsWith(value: 'https://')]
private string $callbackUrl;
// Valid: "https://example.com/callback"
// Invalid: "http://example.com/callback"Validates the string ends with a specific value.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\EndsWith;
#[Column(type: 'string', length: 100)]
#[EndsWith(value: '@company.com')]
private string $email;
// Valid: "user@company.com"
// Invalid: "user@gmail.com"Validates the string contains a specific substring.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Contains;
#[Column(type: 'string', length: 100)]
#[Contains(value: 'promo')]
private string $couponCode;
// Valid: "SUMMERPROMO2024", "promo-code"
// Invalid: "DISCOUNT2024"Validate specific data formats.
Validates phone number format.
| Parameter | Type | Default | Description |
|---|---|---|---|
region |
string |
'US' |
ISO 3166-1 alpha-2 country code |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Phone;
#[Column(type: 'string', length: 20)]
#[Phone(region: 'US')]
private string $phoneNumber;
// Valid: "+1 (555) 123-4567", "555-123-4567", "+15551234567"
#[Column(type: 'string', length: 20)]
#[Phone(region: 'ES')]
private string $spainPhone;Validates credit card numbers using the Luhn algorithm.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\CreditCard;
#[Column(type: 'string', length: 20)]
#[CreditCard]
private string $cardNumber;
// Valid: "4111111111111111", "5500000000000004"
// Invalid: "1234567890123456" (fails Luhn check)Validates postal code format by country.
| Parameter | Type | Default | Description |
|---|---|---|---|
country |
string |
'US' |
ISO 3166-1 alpha-2 country code |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\PostalCode;
#[Column(type: 'string', length: 10)]
#[PostalCode(country: 'US')]
private string $zipCode; // 12345 or 12345-6789
#[Column(type: 'string', length: 10)]
#[PostalCode(country: 'ES')]
private string $spainPostalCode; // 12345Validates latitude/longitude coordinates.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Coordinates;
#[Column(type: 'string', length: 30)]
#[Coordinates]
private string $location;
// Valid: "40.7128,-74.0060", "-33.8688,151.2093", "51.5074,-0.1278"
// Invalid: "40.7128" (missing longitude), "999,999"Validate data against database records.
Validates the value is unique in the database table.
| Parameter | Type | Default | Description |
|---|---|---|---|
column |
string |
null |
Column name to check (defaults to property name) |
except |
string |
null |
Ignore this ID when checking (for updates) |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Unique;
#[Column(type: 'string', length: 100)]
#[Unique]
private string $username;
// For updates: ignore current record
#[Column(type: 'string', length: 255)]
#[Unique(column: 'email', except: 'current_id')]
private string $email;Validates the value exists in another entity's table.
| Parameter | Type | Default | Description |
|---|---|---|---|
entity |
string |
Required | Entity class name to check against |
column |
string |
null |
Column name (defaults to entity's primary key) |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Exists;
#[Column(type: 'integer')]
#[Exists(entity: Category::class)]
private int $categoryId; // Must exist in categories table
#[Column(type: 'string', length: 50)]
#[Exists(entity: Country::class, column: 'iso_code')]
private string $countryCode; // Must match iso_code in countriesValidates the value matches one of the PHP Enum cases.
| Parameter | Type | Default | Description |
|---|---|---|---|
class |
string |
Required | PHP Enum class name |
strict |
bool |
true |
Use strict type comparison |
Example:
use App\Enums\OrderStatus;
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Enum;
#[Column(type: 'string', length: 20)]
#[Enum(class: OrderStatus::class)]
private string $status;
// Enum definition
enum OrderStatus: string
{
case PENDING = 'pending';
case PROCESSING = 'processing';
case SHIPPED = 'shipped';
case DELIVERED = 'delivered';
case CANCELLED = 'cancelled';
}Validate file uploads and attachments.
Validates file is a valid image with allowed formats and size.
| Parameter | Type | Default | Description |
|---|---|---|---|
mimes |
array |
['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp'] |
Allowed extensions |
maxSizeKb |
int |
5120 (5MB) |
Maximum file size in KB |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Image;
#[Column(type: 'string', length: 500)]
#[Image(mimes: ['jpg', 'png', 'webp'], maxSizeKb: 2048)]
private string $avatar; // Max 2MB, jpg/png/webp only
#[Column(type: 'string', length: 500)]
#[Image] // All default formats, max 5MB
private string $featuredImage;Validates file has one of the allowed extensions.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Mimes;
#[Column(type: 'string', length: 500)]
#[Mimes(types: ['pdf', 'doc', 'docx'])]
private string $contract; // PDF or Word documents only
#[Column(type: 'string', length: 500)]
#[Mimes(types: ['xls', 'xlsx', 'csv'])]
private string $spreadsheet;Validates file size is within limits.
| Parameter | Type | Default | Description |
|---|---|---|---|
maxMb |
int |
10 |
Maximum size in megabytes |
minMb |
int |
0 |
Minimum size in megabytes |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\FileSize;
#[Column(type: 'string', length: 500)]
#[FileSize(maxMb: 5, minMb: 0.01)] // Between 10KB and 5MB
private string $document;
#[Column(type: 'string', length: 500)]
#[FileSize(maxMb: 100)] // Max 100MB
private string $video;Validate multiple fields or complex conditions.
Marks a field as mandatory.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Required;
#[Column(type: 'string', length: 150)]
#[Required]
private string $name;Validates that a matching confirmation field exists (e.g., password_confirmation).
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\{Required, Password, Confirmed};
#[Column(type: 'string')]
#[Password(minLength: 8)]
#[Confirmed]
private string $password;
// Request must include:
// { "password": "Secret123!", "password_confirmation": "Secret123!" }Validates password strength with configurable rules.
| Parameter | Type | Default | Description |
|---|---|---|---|
minLength |
int |
8 |
Minimum length |
requireUppercase |
bool |
true |
Require uppercase letter |
requireLowercase |
bool |
true |
Require lowercase letter |
requireNumbers |
bool |
true |
Require number |
requireSpecialChars |
bool |
true |
Require special character |
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\Password;
#[Column(type: 'string')]
#[Password(
minLength: 12,
requireUppercase: true,
requireLowercase: true,
requireNumbers: true,
requireSpecialChars: true
)]
private string $password; // Strong password requiredShorthand for full password validation (8+ chars, upper, lower, numbers, special).
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\StrongPassword;
#[Column(type: 'string')]
#[StrongPassword]
private string $password; // Enforces: 8+ chars, A-Z, a-z, 0-9, !@#$Validates the field matches another field's value.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\SameAs;
#[Column(type: 'string', length: 100)]
private string $recoveryEmail;
#[Column(type: 'string', length: 100)]
#[SameAs(field: 'recoveryEmail')]
private string $confirmRecoveryEmail;Validates the field is different from another field's value.
Example:
use Infrastructure\FrameworkCore\Attributes\Schema\Column;
use Infrastructure\FrameworkCore\Attributes\Validation\DifferentFrom;
#[Column(type: 'string', length: 50)]
private string $currentPassword;
#[Column(type: 'string', length: 50)]
#[DifferentFrom(field: 'currentPassword')]
#[Password(minLength: 8)]
private string $newPassword;use Infrastructure\FrameworkCore\Attributes\Schema\{Entity, Column, Id};
use Infrastructure\FrameworkCore\Attributes\Validation\{
Required, Email, Min, Max, MinLength, MaxLength,
Password, Unique, Enum, Confirmed, Between, Pattern
};
use Domain\Shared\Entities\AggregateRoot;
use App\Enums\UserRole;
#[Entity(table: 'users', resource: 'users')]
class User extends AggregateRoot
{
#[Id]
private int $id;
#[Required]
#[MinLength(3)]
#[MaxLength(50)]
#[Alphanumeric]
#[Unique]
#[Column(type: 'string', length: 50)]
private string $username;
#[Required]
#[Email]
#[MaxLength(255)]
#[Unique]
#[Column(type: 'string', length: 255)]
private string $email;
#[Required]
#[Password(minLength: 8, requireUppercase: true, requireNumbers: true)]
#[Confirmed]
#[Column(type: 'string')]
private string $password;
#[Required]
#[Min(18)]
#[Max(120)]
#[Integer]
#[Column(type: 'integer')]
private int $age;
#[Required]
#[Enum(class: UserRole::class)]
#[Column(type: 'string', length: 20)]
private string $role;
#[MinLength(10)]
#[MaxLength(500)]
#[Pattern(regex: '^\+?[1-9]\d{1,14}$')] // E.164 format
#[Column(type: 'string', length: 20)]
private string $phone;
#[Column(type: 'string', length: 10)]
private string $timezone; // Validated by application logic
}When a request comes in, BOUNDLY:
-
Scans the Entity for
#[Column]attributes - Collects all Validation attributes on each property
- Builds validation rules dynamically
- Validates the incoming payload
- Rejects with detailed error messages if invalid
Error Response:
{
"status": "error",
"message": "Validation failed",
"errors": {
"email": ["The email field must be a valid email address."],
"password": [
"The password must be at least 8 characters.",
"The password must contain at least one uppercase letter.",
"The password must contain at least one number."
],
"age": ["The age must be at least 18."]
}
}Next Step: Query-Engine π