Repository navigation
Releases: SoloPHP/Input
Release list
v1.1.0
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.
v1.0.0
First stable release of Solo Input (solophp/input): typed, validated input DTOs for PHP 8.3+. Mark a class #[Input], declare its fields in the constructor, and the factory reads them from the request, validates and casts the whole input, then calls the constructor.
The API is the one of v1.0.0-RC3, unchanged; two applications have run the release candidates in production.
What 1.0 covers
- Plain
final readonlyinput classes, built once through their constructor after the whole input is valid. - Per-field sources: body or query by factory method,
#[FromRoute],#[FromContext],#[FromBody('a.b')]. - Lossless type casting driven by the declared type, including dates, backed enums, custom casters and
#[Cast('decimal')]. - Every error at once in one
ValidationException, keyed by the input path (customer.id,lines.0.qty). - Processing stages (
#[Normalize],#[Caster],#[PostProcess],#[Generator]), nested inputs and#[Items]lists. - Partial updates:
Inputs::provided(),Inputs::toArray($input, onlyProvided: true)and#[Input(partial: true)]. - Field grouping and a JSON-serializable
schema()of the input contract.
From now on the public API follows semantic versioning; see Versioning for what it covers.
Install
composer require solophp/inputSee the documentation and the full diff since RC3.
v1.0.0-RC3
Third release candidate of Solo Input (solophp/input). It adds #[Input(partial: true)], which moving the first real application to the package called for: the body of a partial update declares itself, and Inputs::toArray() gives its change set without every update endpoint having to ask for it.
This is a prerelease: the 1.0 API is being validated in real applications before the stable 1.0.0 tag.
Changes since 1.0.0-RC2
#[Input(partial: true)]marks the body of a partial update.Inputs::toArray()without anonlyProvidedargument then gives the fields the request supplied, so a default never overwrites what is stored; an absent field still reads as its default, andonlyProvided: falseasks for every field. The flag comes from the nearest#[Input]of the class or its parents, nested DTOs follow the call they are in, and a partial DTO the factory did not build exports nothing. See Attributes →#[Input]andInputs::toArray().Inputs::toArray()'s$onlyProvidedis?boolwith anulldefault; passing a bool works as before.
Install
composer require 'solophp/input:^1.0@RC'See the documentation and the full diff since RC2.
v1.0.0-RC2
Second release candidate of Solo Input (solophp/input). It adds the built-in cast #[Cast('decimal')], which moving the first real application to RC1 called for: a number for a DECIMAL column, bcmath or a decimal library, written in plain decimal notation instead of the exponent notation PHP gives a small float.
This is a prerelease: the 1.0 API is being validated in real applications before the stable 1.0.0 tag.
Changes since 1.0.0-RC1
#[Cast('decimal')]on astring(or untyped /mixed) property writes a number in plain decimal notation:0.00001and'1e-5'as'0.00001', where thestringcast gives'1.0E-5'. An int or a numeric string keeps every digit it has, and a float gives its shortest round-trip digits, so0.1stays'0.1'.""is no value, as forintandfloat. Input that is no number, or a number a float overflows to infinity or rounds to zero although it is not zero, failsnumeric. The schema reports such a field withcast: 'decimal'. See Type Casting → Decimal.- A property typed with a class named like a built-in cast (
json,decimal) fails metadata building withConfigurationException, like any class no input can produce, instead of being cast as that type.
Install
composer require 'solophp/input:^1.0@RC'See the documentation and the full diff since RC1.