Repository navigation
One meaning for an empty input: null, and a "" the field's type cannot hold, are no value. The factory reads such a "" as null before validation, the validator checks a null like a missing field — by presence rules alone — and the field holds null, keeps its default or reports required.
#[Validate('string|max:100')]
#[EmptyToNull]
public ?string $q = null, // ?q= → null (1.0: `string` error)
#[Validate('boolean')]
public ?bool $done = null, // ?done= → null (1.0: `boolean` error)
#[Validate('integer|min_value:1')]
public int $page = 1, // ?page= → 1 (1.0: `integer` error)Fixes
#[EmptyToNull]gives what it documents for a field with rules: a blank string isnull, the default orrequired, where it failed the field's first rule —stringfor a string the client sent.- A rule matching the type is harmless for no value, as documented:
?done=for aboolean?boolisnull.
Behavior changes
- A nullable field takes
nullwithoutnullablein its rules, so in a partial update anullclears it. Keep it from being cleared withfilled:#[Validate('filled|string|max:255')] public ?string $name = null. - A field with a default keeps it for no value — in a partial update a
nullresets it; one with neither reportsrequiredinstead of the first rule it failed. - A
""for anarrayor#[Items]field, or for a custom caster's array type, is no value instead of[]. Only a[]the client sends is the empty list. - Rules of your own run on no value only as presence rules:
#[Validate('accepted')] public bool $terms = falsetakesfalsefornull— drop the default and writerequired|accepted. - A
nullin#[FromContext]is no value too; rules that read another field seenullwhere they saw"". - Rule strings reach the validator verbatim; a
nullableyou write keeps working.
Requirements
solophp/contracts^1.8, whoseValidatorInterfacechecks a missing ornullfield by presence rules alone and conflicts withsolophp/validatorbelow 4.2 — so solophp/validator 4.2 or a validator of your own that keeps the rule.
See the upgrade guide for a 1.0 / 1.1 table of each change, and the full diff.