Skip to content

Validation Attributes

lpachecob edited this page Mar 24, 2026 · 1 revision

βœ… Validation Attributes: Comprehensive Data Validation

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.


πŸ“ Location

Infrastructure\FrameworkCore\Attributes\Validation\

πŸ”§ Type Validators

Validate data types and formats.

#[Email]

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"

#[Url]

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)

#[IpAddress]

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;

#[Uuid]

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"

#[Json]

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'}"

#[IsoDate]

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"

#[Timezone]

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)

#[ColorHex]

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"

#[Slug]

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"

#[MacAddress]

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"

πŸ”’ Numeric Validators

Validate numeric values and ranges.

#[Min]

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

#[Max]

Validates 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 <= 120

#[Between]

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

#[Positive]

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 > 0

#[Negative]

Validates 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 < 0

#[Integer]

Validates 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"

#[Decimal]

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"

πŸ“ String Validators

Validate string content and format.

#[MinLength]

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 characters

#[MaxLength]

Validates 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 characters

#[LengthBetween]

Validates 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 characters

#[Alpha]

Validates 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"

#[Alphanumeric]

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"

#[Numeric]

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"

#[Pattern]

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"

#[StartsWith]

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"

#[EndsWith]

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"

#[Contains]

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"

πŸ“ž Format Validators

Validate specific data formats.

#[Phone]

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;

#[CreditCard]

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)

#[PostalCode]

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; // 12345

#[Coordinates]

Validates 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"

πŸ—„οΈ Database Validators

Validate data against database records.

#[Unique]

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;

#[Exists]

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 countries

#[Enum]

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

πŸ“ File Validators

Validate file uploads and attachments.

#[Image]

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;

#[Mimes]

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;

#[FileSize]

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;

πŸ”— Compound Validators

Validate multiple fields or complex conditions.

#[Required]

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;

#[Confirmed]

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!" }

#[Password]

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 required

#[StrongPassword]

Shorthand 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, !@#$

#[SameAs]

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;

#[DifferentFrom]

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;

🎯 Complete Usage Example

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
}

πŸ”„ Validation Processing Flow

When a request comes in, BOUNDLY:

  1. Scans the Entity for #[Column] attributes
  2. Collects all Validation attributes on each property
  3. Builds validation rules dynamically
  4. Validates the incoming payload
  5. 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 πŸ”Ž

Clone this wiki locally