Hello!
I’m currently using yiisoft/validator for validating DTOs in a REST API and noticed an issue (or perhaps a missing feature) when validating PATCH requests.
With typed PHP properties there are three possible states:
- Property is uninitialized (field was not present in the request)
- Property is initialized with
null - Property is initialized with a value
For PATCH requests these states have different meanings:
- uninitialized → don’t change the field
-
null→ explicitly set the field tonull - value → update the field
For example:
I can’t use the generic skipOnEmpty: true option because it treats empty strings as empty values as well. As a result, if the client explicitly sends "", the Length rule is skipped and the empty string passes validation instead of failing the minimum length constraint. For PATCH DTOs I only want to skip validation when the field is not provided, not when it is provided with an empty value.
final class UpdateUserRequest
{
#[Length(
min: 3,
max: 255,
skipOnEmpty: new WhenMissing(),
)]
public string $name;
}
When the request body is:
{}
I’d expect the Length rule to be skipped because the field wasn’t provided.
However, validation fails because the uninitialized property is internally treated as null.
Using WhenNull currently works:
#[Length(
min: 3,
max: 255,
skipOnEmpty: new WhenNull(),
)]
public string $name;
but this seems to work only because uninitialized properties are normalized to null before validation. It feels like an implementation detail rather than intended behavior.
I also noticed that empty conditions already receive an $isPropertyMissing parameter:
public function __invoke(mixed $value, bool $isPropertyMissing = false): bool
but it appears to be used only for missing array keys, not for uninitialized object properties.
Is there a recommended way to handle this scenario today?
Would it make sense to either:
- treat uninitialized object properties as “missing” for
WhenMissing, or - introduce a dedicated
WhenUninitializedcondition?
I think this would make PATCH DTO validation much easier while preserving the distinction between:
- field is absent
- field is explicitly
null - field has a value
I’m interested to hear whether this use case has already been discussed or if there’s another recommended approach.