Laravel validation OR
Asked Answered
S

2

7

I have some validation that requires a url or a route to be there but not both.

    $this->validate($request, [
        'name'  =>  'required|max:255',
        'url'   =>  'required_without_all:route|url',
        'route' =>  'required_without_all:url|route',
        'parent_items'=>  'sometimes|required|integer'
    ]);

I have tried using required_without and required_without_all however they both get past the validation and I am not sure why.

route is a rule in the route field

Spandau answered 21/1, 2016 at 4:41 Comment(2)
I just tried using required_without and it works. Can you post your route rule? return ['route' => "required_without:url", 'url' => "required_without:route|url"]; Works as expected for me.Presence
@BenSwinburne See the answer below and my comment, required is totally the wrong thing :)Spandau
W
5

I think the easiest way would be creation your own validation rule. It could looks like.

Validator::extend('empty_if', function($attribute, $value, $parameters, Illuminate\Validation\Validator $validator) {

    $fields = $validator->getData(); //data passed to your validator

    foreach($parameters as $param) {
        $excludeValue = array_get($fields, $param, false);

        if($excludeValue) { //if exclude value is present validation not passed
            return false;
        }
    }

    return true;
});

And use it

    $this->validate($request, [
    'name'  =>  'required|max:255',
    'url'   =>  'empty_if:route|url',
    'route' =>  'empty_if:url|route',
    'parent_items'=>  'sometimes|required|integer'
]);

P.S. Don't forget to register this in your provider.

Edit

Add custom message

1) Add message 2) Add replacer

Validator::replacer('empty_if', function($message, $attribute, $rule, $parameters){
    $replace = [$attribute, $parameters[0]];
    //message is: The field :attribute cannot be filled if :other is also filled
    return  str_replace([':attribute', ':other'], $replace, $message);
});
Wharfinger answered 21/1, 2016 at 9:19 Comment(3)
Perfect! I realized this morning that required_if isn't what I needed at all and is in fact to check if another field is empty, appreciate your helpSpandau
There is a slight problem with the rule regarding messages, due to it being a custom validation rule it doesn't do the same replaces in the messages, and if I just set it as a static message then it will appear twice due to both fields using the same role, would you be able to extend your answer to include the message? For example The field :attribute cannot be filled if :other is also filled"? The closest I have tracked down the method is in \vendor\laravel\framework\src\Illuminate\Validation\Validator.php line 1680Spandau
perfect yet again! I thought it may have something to do with the 3rd parameter of $this->validate() but nope, thank you! :)Spandau
P
13

I think you are looking for required_if:

The field under validation must be present if the anotherfield field is equal to any value.

So, the validation rule would be:

$this->validate($request, [
    'name'        =>  'required|max:255',
    'url'         =>  'required_if:route,""',
    'route'       =>  'required_if:url,""',
    'parent_items'=>  'sometimes|required|integer'
]);
Pictor answered 21/1, 2016 at 5:6 Comment(3)
Unfortunately this doesn't help, validation still passes when both fields are filledSpandau
@Spandau Just a thought: Why don't you use jQuery ? For instance, if a user is filling url field, then add readonly attribute to route field and vice versa. In that way, they will NOT be able to fill the data if one of the field is filled in.Pictor
Well they can just remove that attribute and it will still go through server side, jQuery is not a solution to a server side problemSpandau
W
5

I think the easiest way would be creation your own validation rule. It could looks like.

Validator::extend('empty_if', function($attribute, $value, $parameters, Illuminate\Validation\Validator $validator) {

    $fields = $validator->getData(); //data passed to your validator

    foreach($parameters as $param) {
        $excludeValue = array_get($fields, $param, false);

        if($excludeValue) { //if exclude value is present validation not passed
            return false;
        }
    }

    return true;
});

And use it

    $this->validate($request, [
    'name'  =>  'required|max:255',
    'url'   =>  'empty_if:route|url',
    'route' =>  'empty_if:url|route',
    'parent_items'=>  'sometimes|required|integer'
]);

P.S. Don't forget to register this in your provider.

Edit

Add custom message

1) Add message 2) Add replacer

Validator::replacer('empty_if', function($message, $attribute, $rule, $parameters){
    $replace = [$attribute, $parameters[0]];
    //message is: The field :attribute cannot be filled if :other is also filled
    return  str_replace([':attribute', ':other'], $replace, $message);
});
Wharfinger answered 21/1, 2016 at 9:19 Comment(3)
Perfect! I realized this morning that required_if isn't what I needed at all and is in fact to check if another field is empty, appreciate your helpSpandau
There is a slight problem with the rule regarding messages, due to it being a custom validation rule it doesn't do the same replaces in the messages, and if I just set it as a static message then it will appear twice due to both fields using the same role, would you be able to extend your answer to include the message? For example The field :attribute cannot be filled if :other is also filled"? The closest I have tracked down the method is in \vendor\laravel\framework\src\Illuminate\Validation\Validator.php line 1680Spandau
perfect yet again! I thought it may have something to do with the 3rd parameter of $this->validate() but nope, thank you! :)Spandau

© 2022 - 2024 — McMap. All rights reserved.