The #[FluentRules] attribute
A per-method opt-in, defined in sandermuller/laravel-fluent-validation. It says: convert this method's rule array even though the class is not a FormRequest, a trait user, or a Livewire component.
Use it when:
- A non-qualifying class holds rules under a name other than
rules(), such as a custom Validator subclass'srulesWithoutPrefix(). The attribute qualifies the class and points the converter at that method. - An abstract class has
rules()and you have audited its subclasses to confirm none mergesparent::rules()as a plain array. The attribute is that audit assertion, and it lifts the abstract-class guard for the attributed method.
Do not use it on:
- Methods named after framework hooks:
casts(),messages(),attributes(),toArray(),jsonSerialize(). The denylist drops the attribute for both qualification and conversion, and logs a warning so the mistake surfaces. - Abstract methods whose subclasses you have not audited. Converting the parent silently breaks a subclass doing
array_merge(parent::rules(), [...]). The attribute is per-method: putting it on a sibling helper does not lift the guard forrules().
What it does not lift
Three guards the attribute has no effect on
- Cross-class parent safety. If any subclass manipulates
parent::rules()with array functions (array_merge,array_search, bracket assignment,collect()->merge*(), or the+union operator), the parent refuses conversion even with#[FluentRules]on it. The attribute is a claim about your own method, not a licence to override the cross-class scan. Refactor the merge points first. - Shape-changing transforms on Validator subclasses. When a class qualifies only via the attribute and extends a Validator, the converters run but
GroupWildcardRulesToEachRectorskips with a logged message. Folding'*.foo'+'*.bar'into'*' => array()->children([...])is equivalent under FormRequest dispatch, but breaks a Validator parent that postprocesses the rules array. One that walks it and prepends a per-key prefix, for instance, cannot round-trip the nested shape. Fold by hand if you have audited the parent. - The denylist, above. It always wins.
Scoping is per method. The attribute on rulesWithoutPrefix() converts that method and qualifies the class; it does not turn on class-wide detection of other rule-shaped helpers. Each needs its own attribute. That narrowing is what stops a stray rule token in an unrelated helper from being rewritten as validation rules.