Skip to content

[12.x] Compilable for Validation Contract#54882

Merged
taylorotwell merged 3 commits into
laravel:12.xfrom
peterfox:feature/compiled-rules-contract
Mar 5, 2025
Merged

[12.x] Compilable for Validation Contract#54882
taylorotwell merged 3 commits into
laravel:12.xfrom
peterfox:feature/compiled-rules-contract

Conversation

@peterfox

@peterfox peterfox commented Mar 3, 2025

Copy link
Copy Markdown
Contributor

Changes

  • Adds a new validation contract called Compilable.
  • Updates the ValidationRuleParser to use the Compilable contract instead of the NestedRules class for type checks.
  • Moves some of the code from NestedRules into a static Rule::compile() method so it's easier to reuse.
  • Applies Compilable contract to the original NestedRules class.

No tests added because there's no new logic added. Just refactoring. I don't believe any of the code would be breaking in this situation. The NestedRules class just behaves the same even if someone extended it. I wouldn't expect the RulesParser to be extended but no method signatures have been changed.

Why

NestedRules are great but can't be extended as a class easily without fiddling with the constructor and overloading the compile method while duplicating that logic. This PR seeks to make NestedRules use a contract that developers can use themselves to determine nested rule logic instead of being forced to use a closure.

for instance:

class FooNestedRules extends NestedRules
{
    public function __construct()
    {
        // callable property has to be set with empty closure
        parent::__construct(function() {});
    }

    public function compile($attribute, $value, $data = null, $context = null)
    {
       $rules = // do some logic to make the rules.

        $parser = new ValidationRuleParser(
            Arr::undot(Arr::wrap($data))
        );

        if (is_array($rules) && ! array_is_list($rules)) {
            $nested = [];

            foreach ($rules as $key => $rule) {
                $nested[$attribute.'.'.$key] = $rule;
            }

            $rules = $nested;
        } else {
            $rules = [$attribute => $rules];
        }

        return $parser->explode(ValidationRuleParser::filterConditionalRules($rules, $data));
    }
}

Could become a class like:

class FooNestedRules implements CompilableRules
{
    public function compile($attribute, $value, $data = null, $context = null)
    {
       $rules = // do some logic to make the rules.

       return Rule::compile($attribute, $rules, $data);
    }
}

This will make it possible for people to implement classes to manage their own nested rules.

Validation::make([
    'form' => [
        'fields' => [
           [
               'type' => 'text',
               'text'  => 'foobar',
           ],
           [
               'type'  => 'image',
               'path'  => 'foobar.jpg',
               'alt'     => 'photo of foobar',
           ]
        ]
    ]
], [
    'form.fields.*' => new FormFields();
]);

@crynobone crynobone changed the title Compilable for Validation Contract [12.x] Compilable for Validation Contract Mar 4, 2025
@taylorotwell taylorotwell merged commit 4284e61 into laravel:12.x Mar 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants