A FormRequest that declares both a schema() builder and a rules() method now
merges the two instead of schema() silently shadowing rules(). This lets an
abstract base request — or a trait — supply shared fields through one method while
each concrete request overrides or extends them through the other, without either
side being quietly dropped.
Changed
-
schema()andrules()are merged, not either-or. When a request declares
both aFluentSchema-typedschema()and arules()— anywhere in its class
hierarchy — their rule sets are combined. On a shared field the more specific
declaration wins: the deeper class in the hierarchy, or a body definition over a
trait import, so overriding a base field behaves exactly like a plain method
override, while non-colliding fields from both layers survive. When both are
declared on the same class the tie resolves toschema(). Detection still keys
off theFluentSchema-typed parameter, so an unrelatedschema()method is never
hijacked, and either method may still return a plain array or aRuleSet.This changes behavior only for a request that already declared both methods:
previouslyschema()won outright andrules()was ignored; now the two merge,
with the more specific declaration winning shared fields. A request that declares
only one of the two is unaffected.
Benchmark results
| Scenario | Optimizations | Native Laravel | Optimized | Speedup |
|---|---|---|---|---|
| Product import — 500 items, simple rules | Wildcard, fast-check | 229.0ms | 3.4ms | ~68x |
| Nested order lines — 1000 orders × 5 line items | Wildcard, fast-check (nested) | 2847.7ms | 19.4ms | ~147x |
| Event scheduling — 100 items, field-ref dates | Wildcard, partial fast-check | 32.1ms | 1.0ms | ~31x |
| Article submission — 50 items, custom Rule objects | Wildcard only | 10.3ms | 2.9ms | ~4x |
| Conditional import — 100 items, 47 conditional fields | Wildcard, pre-evaluation | 3412.0ms | 51.1ms | ~67x |
| Login form — 3 fields, no wildcards | Fast-check (flat) | 0.2ms | 0.0ms | ~16x |