Validation Contracts
This page focuses on the behavior you can rely on when integrating the Validation package.
Rule storage contract
Rules are stored per validator instance
A Validator object keeps its configured rules until you call:
deleteRule(...)flushRules()- or overwrite an existing rule with
updateRule()/setRule()
Do not treat one validator instance as stateless shared infrastructure unless you control its full lifecycle.
One field can hold one value per rule name
If you register the same rule name again for the same field, the latest value replaces the previous one.
That matters for calls like:
$validator->setRule('title', [Rule::minLen(5)]);
$validator->updateRule('title', Rule::minLen(10));
Only minLen(10) remains active.
Input normalization contract
Missing validated fields become empty strings
Before validation runs, the package inserts '' for any field that has rules but is absent from the input array.
This has two important effects:
- optional rules like
maxLen(50)can still pass on omitted fields - omitted fields with stricter rules like
email()still return an error
Validation runs only for configured fields
Extra keys in the input array are ignored unless you registered rules for them.
When you use same('other_field'), include that referenced key in the submitted payload too. The validator fills missing validated fields in its working copy, while same() reads from the original input array.
Execution contract
Rules do not short-circuit
When one field has multiple failing rules, the validator keeps evaluating the remaining rules for that field.
Expect getErrors() to contain all failed rule messages for that field, not just the first one.
Built-in rules run before custom-rule lookup exceptions
For each configured rule name, resolution order is:
- built-in validator method
- registered custom closure
BadMethodCallException
If no built-in or custom rule exists for that name, validation stops with an exception.
Custom rules must be callable
addRule() stores the closure under a rule name.
If the stored value is somehow not callable at execution time, validation throws RuntimeException.
Error contract
Errors are grouped by field and keyed by rule
Internally, each field stores one error entry per rule name.
In normal usage, getErrors() returns:
[
'email' => [
'The email field must be valid',
],
]
Error messages are translation-driven
getErrors() builds messages with:
t('common.<field>')for the field labelt('validation.<rule>')for the rule message
If your translation files are incomplete, the final output quality depends on how the Lang package handles missing keys in your application.
Typed rule contract
Some rules require specific value types
Several rule methods are type-hinted, especially:
- string-focused rules like
minLen,url, andcontains - upload rules like
fileSize,fileMimeType,fileExtension, andimageDimensions
If the runtime value does not match the required PHP type, the validator does not convert it for you. PHP raises TypeError.
This is especially relevant for optional file uploads: an omitted field becomes '', which is not an UploadedFile.
Cross-package dependency contract
exists() and unique() depend on the Model package
These rules resolve the provided model class through ModelFactory and query one column with findOneBy().
They are appropriate for "record already exists" and "record must exist" checks, but they are only as reliable as the active model and database configuration.
captcha() depends on the Captcha package
The validator does not own captcha configuration. It simply asks the default captcha adapter to verify the submitted value.
File-image rules may bubble storage exceptions
imageDimensions() delegates to UploadedFile::getDimensions().
If the upload is not a readable image, the validator may raise a storage-layer exception instead of returning a normal validation failure.