Skip to content

Validation Contexts: Conditionally apply rules based on context#1149

Open
CWAscend wants to merge 4 commits intospatie:mainfrom
CWAscend:feature/validation-context
Open

Validation Contexts: Conditionally apply rules based on context#1149
CWAscend wants to merge 4 commits intospatie:mainfrom
CWAscend:feature/validation-context

Conversation

@CWAscend
Copy link
Copy Markdown
Contributor

@CWAscend CWAscend commented Feb 14, 2026

Summary

  • Adds ability to define validation contexts (e.g., store, update, import) that conditionally apply validation rules
  • All 85+ validation attributes now accept an optional context parameter
  • Includes comprehensive test coverage for variadic parameter extraction

Backwards Compatibility

This is a fully backwards-compatible, non-breaking change:

  • All validation attributes continue to work exactly as before without the context parameter
  • Rules without a context defined apply to all contexts (existing behavior preserved)
  • No existing method signatures have changed
  • The context parameter is optional on all attributes
  • Existing codebases require zero modifications to upgrade

Features

Context-specific validation rules:

class UserData extends Data
{
    public function __construct(
        #[Min(2)]
        public string $name,

        #[Email]
        public string $email,

        #[Min(8)]
        #[Confirmed]
        #[Required(context: 'store')] // Conditionally required only in 'store' context
        public ?string $password = null,

        public ?string $password_confirmation = null,
    ) {}
}

Controller integration with #[DataValidationContext]:

use Spatie\LaravelData\Attributes\DataValidationContext;

class UserController extends Controller
{
    #[DataValidationContext('store')]
    public function store(UserData $data): RedirectResponse
    {
        // Password is required and must be min 8 characters
    
        return redirect()->route('users.index')
            ->with('success', 'User created successfully.');
    }
    
    #[DataValidationContext('update')]
    public function update(UserData $data, User $user): RedirectResponse
    {
        // Password is optional - context-specific rules don't apply
    
        return redirect()->route('users.index')
            ->with('success', 'User updated successfully.');
    }
}

Multiple contexts per rule:

#[Required(context: ['store', 'import'])]
public ?string $email = null;

Fluent API:

UserData::withContext('store')->from($request);

Variadic rules with context:

#[In('draft', 'pending', context: 'store')]
#[In('published', 'archived', context: 'admin')]
public ?string $status = null;

You also have access to the string in your manual rules:

public static function rules(ValidationContext $context): array
{
    if ($context->contextName === 'store') {
        return [
            // Rules specific to the 'store' context
        ];
    }

    return [];
}

Test plan

  • ValidationContextManager singleton registration
  • Context setting, getting, and clearing
  • Rules without context apply to all contexts
  • Context-specific rules only apply in matching context
  • Array of contexts support
  • Fluent API with context restoration
  • Variadic parameter extraction for all rule types (ArrayType, In, NotIn, StartsWith, EndsWith, Mimes, etc.)

I'm open to discussions on the naming of things - i'm not 100% convinced myself there :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant