Found while fixing #172, deliberately left out of scope there.
A style: deepObject OBJECT query param (#131) synthesizes nested rules, so ?filter[active]=true validates against a plain boolean rule at filter.active. The 'true' => '1' literal mapping that fromQuery() emits covers TOP-LEVEL boolean params only, so nothing normalizes the nested value.
It therefore 422s on both the injected and the additive path. #172's fix does not help: it forces a class additive only when it carries a top-level boolean, and a deepObject-only class correctly stays injected.
Verified empirically with a probe spec during #172.
Why this one argues for the other approach
#172 was fixed by avoiding container injection, which is the established answer for path (#113), header (#121) and delimited-array (#132) params. That works because the factory gets a chance to rewrite the payload before validating.
A nested boolean has no such seam today. Options:
- Extend the emitted mapping to walk nested deepObject paths, so
filter.active gets the same treatment as a top-level active. Keeps the existing architecture, more emitter work.
- Emit a tolerant boolean rule for query-location booleans specifically, so the string literals validate directly and no rewriting is needed. Cleaner at runtime, but forks rule derivation by parameter location, which the package currently avoids on purpose (query, path, header and body all run one
RulesBuilder pipeline).
Option 1 is the smaller change and preserves the single-pipeline property. Option 2 is the only approach that would also make the top-level case injectable again, if that were ever wanted.
Low priority: deepObject is itself a recent, narrow feature and a nested boolean inside one is an uncommon shape.
Found while fixing #172, deliberately left out of scope there.
A
style: deepObjectOBJECT query param (#131) synthesizes nested rules, so?filter[active]=truevalidates against a plainbooleanrule atfilter.active. The'true' => '1'literal mapping thatfromQuery()emits covers TOP-LEVEL boolean params only, so nothing normalizes the nested value.It therefore 422s on both the injected and the additive path. #172's fix does not help: it forces a class additive only when it carries a top-level boolean, and a deepObject-only class correctly stays injected.
Verified empirically with a probe spec during #172.
Why this one argues for the other approach
#172 was fixed by avoiding container injection, which is the established answer for path (#113), header (#121) and delimited-array (#132) params. That works because the factory gets a chance to rewrite the payload before validating.
A nested boolean has no such seam today. Options:
filter.activegets the same treatment as a top-levelactive. Keeps the existing architecture, more emitter work.RulesBuilderpipeline).Option 1 is the smaller change and preserves the single-pipeline property. Option 2 is the only approach that would also make the top-level case injectable again, if that were ever wanted.
Low priority: deepObject is itself a recent, narrow feature and a nested boolean inside one is an uncommon shape.