2

I have a problem, how do you add / remove a rule if another rule passed/fails?

public function rules()
{
    $rules = [
        'description' => 'required|unique:orders,description',
        'user_id' => 'required|unique:users,id',
        'supplier_id' => 'required',
        'warehouse_id' => 'required|unique:warehouse,id',
        'warehouse_qty' => 'required'


    ];
    
    return $rules;
}

For example, if the "warehouse_id" rule fails, it would not be necessary to evaluate the "warehouse_qty" field's rules (since the warehouse id is not found in the db).

Or if the "user_id" field's rule succeeds, new rules should be activated, like for "user_name" and "user_phone" fields.

Thanks!

4
  • Something like bail? Commented Jul 3, 2021 at 8:11
  • 1
    Have you checked laravel.com/docs/8.x/validation#conditionally-adding-rules Commented Jul 3, 2021 at 8:17
  • You will probably have to make a custom function for this and do your own logic. Commented Jul 3, 2021 at 8:25
  • Sorry, you were actually asking about depending one field's validation on another field's result, see my edit. Commented Jul 3, 2021 at 10:14

2 Answers 2

3

To validate a field only if another passes validation, there are multiple solutions.

Approach #2 (recommended)

Since Laravel 5.3.23; Simply move any field validation that depends on another field's rules, into Controller's withValidator(...) method, like:

use Illuminate\Support\Facades\Validator;

// ...

public function withValidator($validator)
{
    $validator->after(function ($validator) {
        // Skip if any previous field was invalid.
        if ($validator->failed()) return;

        // Actual validation (for fields depending on previous).
        Validator::make($this->input(), [
            'warehouse_qty' => ['required']
        ])->validate();
    });
}

This has the advantage of simply using existing rules, as you would normally (instead of rewriting the "required" rule's source-code, which the below approach does).

See also related Laracast post.

Note that if rules() method is not defined, withValidator(...) is never called (and calling $this->input() may result in function not defined error).

Approach #1 (Old answer)

Since Laravel 5.6 and later we can do below:

use Illuminate\Support\Facades\Validator;

// ...

public function rules()
{
    $result = [
        // ...
        'warehouse_id' => 'required|unique:warehouse,id',
    ];

    if (Validator::make($this->input(), $result)->passes()) {
        $result['warehouse_qty'] = [
            function ($attribute, $value, $fail) {
                if (empty($value)) {
                    $fail('The '.$attribute.' is required.');
                }
            },
        ];
    }

    return $result;
}

Note that for above to work, the fields order is important, and the closure should be added to array right after the fields it is depending on.

-------===-------

While above are custom approaches (for asked matter), doing that for rules of the same field is built-in Laravel.

  1. Adding a rule if another rule passed is easy:
    • The rules are executed from left to right.
    • Use pipe (the | character) to separate rules.
    • And, as long as rules at left pass, the later rules are added and/or executed.
  2. Removing a rule if another rule fails can be achieved by pipe as well.

    Simply because any rule after the failed one is ignored.

  3. But the reverse of above two statements is not possible:
    • We can not add and/or execute next-rule if previous fails.
    • We can not remove next-rule if previous passed.
    • At least, not without custom function.
Sign up to request clarification or add additional context in comments.

Comments

0

Thanks to all who gave their solutions to my problem, especially Top-Master, since his explanation showed me another point of view.

I was finally able to solve my problem, and this is my solution. If anyone can improve the code, it is welcome.

public function rules()
{   

    $rules = [
        'description' => 'required|unique:orders,description',
        'user_id' => 'required|unique:users,id',
        'supplier_id' => 'required'
    ];
  
    $rules = $this->validateItems($rules);
    
    return $rules;
}

public function validateItems($rules){


        $rules["warehouse_id"] = ['required', 
                                  'unique:warehouse,id'];
        
        //Here I validate if the rules of the "warehouse_id" field fail or not.
        $validator = Validator::make($this->input('warehouse_id'), [
            "id" => ['required', 
                     'unique:warehouse,id']
        ]);

        //If the validation doesn't fail, the "warehouse_qty" field is added to $rule                                 
        if(!($validator->fails())){
            $rules["warehouse_qty"] = ['required'];
        }
        
    }
    return $rules;
}

2 Comments

See my Approach #2 which like your answer has the advantage of directly using rules (like required) without re-writing them (but without your answers disadvantages).
Disadvantages: you are copying and running warehouse_id field's rules (which is not done by my Approach #2), also, Laravel will redo it (making your code little slower).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.