Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.7] Add new conditional rule for validation #25103

Closed
wants to merge 2 commits into from
Closed

[5.7] Add new conditional rule for validation #25103

wants to merge 2 commits into from

Conversation

krve
Copy link
Contributor

@krve krve commented Aug 6, 2018

This PR adds a new conditional rule that allows conditionally adding rules based on a closure or boolean.

Example

$this->validate($data, [
    'user_id' => [
        'numeric',
        Rule::conditional(Auth::user()->isAdmin())->passes(['required', 'numeric'])->fails('nullable'),
    ],
]);

This also allows complex nested rules if you want. (These can be nested as far as you need - even with a conditional rule inside a conditional rule)

$this->validate($data, [
    'user_id' => [
        'numeric',
        Rule::conditional(Auth::user()->isAdmin())->passes(new MyCustomRule),
    ],
]);

This would also replace some of the usage of $v->sometimes.

Sometimes approach:

$v->sometimes('reason', 'required|max:500', function ($input) {
    return $input->games >= 100;
});

Conditional Rule approach:

$this->validate($input, [
    'reason' => Rule::conditional($input->games >= 100)->passes('required', 'max:500'),
]);

This to me is a much simpler way of doing conditional validation and looks alot cleaner too.

@garygreen
Copy link
Contributor

What's the benefit of doing:

$this->validate($data, [
    'user_id' => [
        'numeric',
        Rule::conditional(Auth::user()->isAdmin())->passes('required')->fails('nullable'),
    ],
]);

vs

$this->validate($data, [
    'user_id' => [
        'numeric',
        Auth::user()->isAdmin() ? 'required' : 'nullable',
    ],
]);

To me the latter is more readable.

Why must we have a wrapper around every little thing like this? Just branch code as and when you need to.

@krve
Copy link
Contributor Author

krve commented Aug 6, 2018

In your example there is no benefit. But as soon as you need more than 1 rule to be merged in, you are going to run into trouble.

$this->validate($data, [
    'user_id' => [
        'numeric',
        Rule::conditional(Auth::user()->isAdmin())
            ->passes('required', Rule::exists('users', 'id'))
            ->fails('nullable'),
    ],
]);

Without the conditional rule, this would not be possible without merging the entire array of rules conditionally.

Just branch code as and when you need to.

I have something similar to this in my own project, but I cannot use it in the same way because of the way the Laravel Validator parses rules. So instead I have to do as stated above - conditionally merge the rules-array - which quickly becomes a huge burden to maintain.

@garygreen
Copy link
Contributor

garygreen commented Aug 6, 2018

Instead of:

$this->validate($data, [
    'user_id' => [
        'numeric',
        Rule::conditional(Auth::user()->isAdmin())
            ->passes('required', Rule::exists('users', 'id'))
            ->fails('nullable'),
    ],
]);

I would write:

$this->validate($data, [
    'user_id' => collect([
        'numeric',
        Auth::user()->isAdmin() ? ['required', Rule::exists('users', 'id')] : 'nullable'
    ])->flatten()
]);

The conditional wrapper might add a bit of syntax sugar but imo it's not as readable and adds Yet Another Wrapper™. I try to avoid "clever code" like this and just keep things simple. Meh.

@krve
Copy link
Contributor Author

krve commented Aug 6, 2018

I see your point and yes the code you wrote is simple and readable, but I still think this is more readable to me - especially without the need to wrap the rules in a collection and then flatten them.

What about renaming some of the methods to be more readable? Something like Rule::if()

@garygreen
Copy link
Contributor

garygreen commented Aug 6, 2018

But why? This PR is essentially just flattening the rules in the same sort of way. If you want that kind of syntax sugar, maybe a package would be better - all you need to do is flatten the rules.

You could just add a helper method which calls collect(..)->flatten()

$this->validate($data, [
    'user_id' => conditionalRules([
        'numeric',
        Auth::user()->isAdmin() ? ['required', Rule::exists('users', 'id')] : 'nullable'
    ])
]);

I would prefer to see a PR that adds a flatten rule that will flatten all the rules before processing, IMO.

$this->validate($data, [
    'user_id' => [
        'flatten',
        'numeric',
        Auth::user()->isAdmin() ? ['required', Rule::exists('users', 'id')] : 'nullable'
    ]
]);

But it's not really needed. This is kind of an edge case.

@krve
Copy link
Contributor Author

krve commented Aug 6, 2018

Thanks for the discussion. After giving it some thought I think I will make it a package instead. I never really liked the way it works right now, because it always has to loop through every rule to check if a conditional rule exist.

The whole collect(..)->flatten() gave me an idea on how to handle the conditional rules in my package - so thanks! 👍

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