Smart Validation Rules - Grazulex/laravel-arc GitHub Wiki

🛡️ Smart Validation Rules

Smart Validation Rules automatically generate Laravel validation rules based on field names, types, and common patterns. This feature saves time and ensures consistent validation across your application.

🎯 Overview

The smart validation system analyzes property names and types to intelligently suggest appropriate validation rules, reducing manual configuration while maintaining security and data integrity.

Key Benefits

  • Intelligent Detection: Recognizes common field patterns automatically
  • Security-First: Generates secure validation rules by default
  • Pattern Recognition: 20+ built-in patterns for common fields
  • Customizable: Add your own patterns and rules
  • Strict Mode: Enhanced security for sensitive applications

🛠️ How It Works

Pattern Matching Process

  1. Field Analysis: Examines property names and types
  2. Pattern Recognition: Matches against built-in patterns
  3. Rule Generation: Creates appropriate Laravel validation rules
  4. Security Enhancement: Applies strict mode rules if enabled

Recognition Levels

Level Description Examples
Exact Match Field name exactly matches pattern email, password, phone
Suffix Match Field ends with pattern *_email, *_phone, *_url
Prefix Match Field starts with pattern is_*, has_*, can_*
Contains Match Field contains pattern *password*, *secret*
Type-Based Based on PHP type hints int, float, bool, string

🚀 Usage

Basic Smart Validation

# Generate DTO with smart validation
php artisan make:dto User --model=User --with-validation

Strict Mode (Enhanced Security)

# Generate with strict security rules
php artisan make:dto User --model=User --with-validation --validation-strict

Custom Patterns

# Generate with custom validation patterns
php artisan make:dto User --model=User --with-validation --validation-patterns=custom

📋 Built-in Patterns

Email Fields

// Detected patterns: email, *_email, *email*
class UserDTO extends LaravelArcDTO
{
    #[Property(type: 'string', required: true, validation: 'required|string|email|max:254')]
    public string $email;
    
    #[Property(type: 'string', required: false, validation: 'nullable|string|email|max:254')]
    public ?string $backup_email;
}

Password Fields

// Detected patterns: password, *_password, *password*
class UserDTO extends LaravelArcDTO
{
    // Standard mode
    #[Property(type: 'string', required: true, validation: 'required|string|min:8')]
    public string $password;
    
    // Strict mode
    #[Property(type: 'string', required: true, validation: 'required|string|min:12|regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/')]
    public string $password_strict;
}

Phone Fields

// Detected patterns: phone, mobile, telephone, *_phone
class ContactDTO extends LaravelArcDTO
{
    #[Property(type: 'string', required: false, validation: 'nullable|string|regex:/^[+]?[0-9\s\-\(\)]+$/|max:20')]
    public ?string $phone;
    
    #[Property(type: 'string', required: false, validation: 'nullable|string|regex:/^[+]?[0-9\s\-\(\)]+$/|max:20')]
    public ?string $mobile_phone;
}

Age and Numeric Fields

// Detected patterns: age, year, count, quantity, amount
class PersonDTO extends LaravelArcDTO
{
    #[Property(type: 'int', required: true, validation: 'required|integer|min:0|max:150')]
    public int $age;
    
    #[Property(type: 'int', required: true, validation: 'required|integer|min:1900|max:2100')]
    public int $birth_year;
    
    #[Property(type: 'int', required: true, validation: 'required|integer|min:1')]
    public int $quantity;
}

URL Fields

// Detected patterns: url, website, link, *_url
class ProfileDTO extends LaravelArcDTO
{
    #[Property(type: 'string', required: false, validation: 'nullable|string|url|max:2048')]
    public ?string $website;
    
    #[Property(type: 'string', required: false, validation: 'nullable|string|url|max:2048')]
    public ?string $portfolio_url;
}

Boolean Fields

// Detected patterns: is_*, has_*, can_*, *_active, *_enabled
class UserDTO extends LaravelArcDTO
{
    #[Property(type: 'bool', required: false, default: true, validation: 'boolean')]
    public bool $is_active;
    
    #[Property(type: 'bool', required: false, default: false, validation: 'boolean')]
    public bool $has_verified_email;
    
    #[Property(type: 'bool', required: false, default: false, validation: 'boolean')]
    public bool $can_login;
}

Country and Language Codes

// Detected patterns: country_code, language_code, locale, *_code
class AddressDTO extends LaravelArcDTO
{
    #[Property(type: 'string', required: true, validation: 'required|string|size:2|alpha')]
    public string $country_code;
    
    #[Property(type: 'string', required: false, validation: 'nullable|string|size:2|alpha')]
    public ?string $language_code;
}

Date and Time Fields

// Detected patterns: *_at, *_date, *_time, birth_date, created_at
class EventDTO extends LaravelArcDTO
{
    #[Property(type: 'date', required: false, validation: 'nullable|date')]
    public ?Carbon $created_at;
    
    #[Property(type: 'date', required: false, validation: 'nullable|date|before:today')]
    public ?Carbon $birth_date;
    
    #[Property(type: 'date', required: true, validation: 'required|date|after:now')]
    public Carbon $event_date;
}

🕰️ Complete Pattern Library

All Built-in Patterns

Pattern Field Examples Validation Rules
Email email, user_email `email
Password password, new_password min:8 (standard), `min:12
Phone phone, mobile, telephone `regex:/^[+]?[0-9\s-()]+$/
URL url, website, portfolio_url `url
Age age `integer
Year year, birth_year `integer
Count count, quantity, amount `integer
Price price, cost, amount `numeric
Boolean is_*, has_*, can_* boolean
Country Code country_code, cc `size:2
Language language_code, locale `size:2
Postal Code postal_code, zip_code `max:20
Name name, first_name, last_name `max:255
Username username, login `min:3
Slug slug, permalink `max:255
IP Address ip, ip_address ip
UUID uuid, *_uuid uuid
JSON json, *_json, metadata json
Date *_at, *_date date
Color color, *_color regex:/^#[a-fA-F0-9]{6}$/
Rating rating, score `integer

🔧 Custom Patterns

Adding Custom Patterns

// In a service provider
public function boot()
{
    ValidationRuleGenerator::addPattern('api_key', [
        'validation' => 'required|string|size:32|alpha_num',
        'security_level' => 'high'
    ]);
    
    ValidationRuleGenerator::addPattern('*_token', [
        'validation' => 'required|string|min:40|max:255',
        'strict_validation' => 'required|string|size:60|regex:/^[a-zA-Z0-9]+$/'
    ]);
}

Pattern Configuration

class CustomValidationPatterns
{
    public static function getPatterns(): array
    {
        return [
            // Exact matches
            'iban' => [
                'validation' => 'required|string|iban',
                'description' => 'International Bank Account Number'
            ],
            
            // Regex patterns
            '/.*_reference$/' => [
                'validation' => 'required|string|max:50|alpha_num',
                'description' => 'Reference numbers'
            ],
            
            // Type-based patterns
            'float:price' => [
                'validation' => 'required|numeric|min:0|max:999999.99',
                'description' => 'Price fields'
            ]
        ];
    }
}

🛡️ Strict Mode Features

Enhanced Security Rules

// Standard vs Strict mode comparison
class SecurityDTO extends LaravelArcDTO
{
    // Standard mode
    #[Property(type: 'string', validation: 'required|string|min:8')]
    public string $password;
    
    // Strict mode - Enhanced security
    #[Property(type: 'string', validation: 'required|string|min:12|regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/')]
    public string $password_strict;
    
    // Standard email
    #[Property(type: 'string', validation: 'required|email|max:254')]
    public string $email;
    
    // Strict email - Additional checks
    #[Property(type: 'string', validation: 'required|email:rfc,dns|max:254|not_regex:/\+.*@/')]
    public string $email_strict;
}

Strict Mode Patterns

Field Type Standard Strict Mode Enhancement
Password min:8 min:12 + complexity regex
Email email email:rfc,dns + disposable email blocking
Phone Basic regex International format validation
URL url url + domain whitelist
Name max:255 + XSS prevention regex
Username alpha_num_dash + reserved word blocking

🎨 Advanced Examples

Complete User Registration DTO

class UserRegistrationDTO extends LaravelArcDTO
{
    #[Property(
        type: 'string',
        required: true,
        validation: 'required|string|max:255|regex:/^[\pL\s\-]+$/u',
        transform: [TrimTransformer::class]
    )]
    public string $first_name;
    
    #[Property(
        type: 'string',
        required: true,
        validation: 'required|string|max:255|regex:/^[\pL\s\-]+$/u',
        transform: [TrimTransformer::class]
    )]
    public string $last_name;
    
    #[Property(
        type: 'string',
        required: true,
        validation: 'required|string|email:rfc,dns|max:254|unique:users,email',
        transform: [TrimTransformer::class, LowercaseTransformer::class]
    )]
    public string $email;
    
    #[Property(
        type: 'string',
        required: true,
        validation: 'required|string|min:12|regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/',
        transform: [HashTransformer::class]
    )]
    public string $password;
    
    #[Property(
        type: 'string',
        required: false,
        validation: 'nullable|string|regex:/^[+]?[1-9]\d{1,14}$/',
        transform: [TrimTransformer::class]
    )]
    public ?string $phone;
    
    #[Property(
        type: 'date',
        required: false,
        validation: 'nullable|date|before:today|after:1900-01-01'
    )]
    public ?Carbon $birth_date;
    
    #[Property(
        type: 'string',
        required: true,
        validation: 'required|string|size:2|alpha|in:US,CA,GB,DE,FR,ES,IT',
        transform: [UppercaseTransformer::class]
    )]
    public string $country_code;
}

E-commerce Product DTO

class ProductDTO extends LaravelArcDTO
{
    #[Property(
        type: 'string',
        required: true,
        validation: 'required|string|max:255|min:3',
        transform: [TrimTransformer::class]
    )]
    public string $name;
    
    #[Property(
        type: 'string',
        required: true,
        validation: 'required|string|max:255|regex:/^[a-z0-9]+(?:-[a-z0-9]+)*$/|unique:products,slug',
        transform: [TrimTransformer::class, LowercaseTransformer::class, SlugTransformer::class]
    )]
    public string $slug;
    
    #[Property(
        type: 'float',
        required: true,
        validation: 'required|numeric|min:0.01|max:999999.99'
    )]
    public float $price;
    
    #[Property(
        type: 'string',
        required: true,
        validation: 'required|string|max:20|alpha_num|unique:products,sku',
        transform: [TrimTransformer::class, UppercaseTransformer::class]
    )]
    public string $sku;
    
    #[Property(
        type: 'int',
        required: true,
        validation: 'required|integer|min:0|max:99999'
    )]
    public int $stock_quantity;
    
    #[Property(
        type: 'float',
        required: false,
        validation: 'nullable|numeric|min:0|max:100'
    )]
    public ?float $discount_percentage;
}

🛠️ Testing Validation Rules

Unit Testing

class ValidationRulesTest extends TestCase
{
    public function test_email_validation_rules(): void
    {
        $rules = UserDTO::rules();
        
        $this->assertStringContainsString('email', $rules['email']);
        $this->assertStringContainsString('max:254', $rules['email']);
    }
    
    public function test_password_strict_validation(): void
    {
        $this->expectException(ValidationException::class);
        
        new UserDTO([
            'email' => '[email protected]',
            'password' => 'weak' // Should fail strict validation
        ]);
    }
    
    public function test_phone_format_validation(): void
    {
        $user = new UserDTO([
            'email' => '[email protected]',
            'password' => 'StrongPass123!',
            'phone' => '+1-555-123-4567'
        ]);
        
        $this->assertEquals('+1-555-123-4567', $user->phone);
    }
}

Integration Testing

class SmartValidationIntegrationTest extends TestCase
{
    public function test_generates_appropriate_rules_for_common_fields(): void
    {
        $generator = new ValidationRuleGenerator();
        
        $rules = $generator->generateRules([
            'email' => 'string',
            'password' => 'string',
            'age' => 'int',
            'is_active' => 'bool'
        ]);
        
        $this->assertStringContainsString('email', $rules['email']);
        $this->assertStringContainsString('min:', $rules['password']);
        $this->assertStringContainsString('integer', $rules['age']);
        $this->assertStringContainsString('boolean', $rules['is_active']);
    }
}

📚 Best Practices

1. Use Appropriate Security Levels

// ✅ Good - Use strict mode for sensitive data
class AdminUserDTO extends LaravelArcDTO
{
    #[Property(type: 'string', validation: 'required|string|min:12|regex:/complex_pattern/')]
    public string $admin_password;
}

// ✅ Good - Standard mode for regular fields
class ProfileDTO extends LaravelArcDTO
{
    #[Property(type: 'string', validation: 'required|string|max:255')]
    public string $display_name;
}

2. Combine with Transformations

class UserDTO extends LaravelArcDTO
{
    #[Property(
        type: 'string',
        validation: 'required|email|max:254',
        transform: [TrimTransformer::class, LowercaseTransformer::class]
    )]
    public string $email;
}

3. Document Custom Rules

class ApiKeyDTO extends LaravelArcDTO
{
    /**
     * API key must be exactly 32 characters, alphanumeric only
     * Pattern: Custom API key validation for security
     */
    #[Property(
        type: 'string',
        required: true,
        validation: 'required|string|size:32|alpha_num'
    )]
    public string $api_key;
}

🔗 Related Pages


⬅️ Back to: Auto-Discovery Relations | ➡️ Next: Debug & Analysis Tools