Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

uhajzeraj's avatar

Validate a field only if another one passes validation

I have a form request that needs to validate 2 fields. However, I need to validate one of the fields only if the other one passes validation. I came up with something like this:

public function rules()
{
    return [
        'token' => ['bail', 'required', 'uuid', new ValidExchange],
    ];
}

public function withValidator($validator)
{
    $validator->after(function ($validator) {
        // This forces the validator to evaluate the rules defined in the rules() method above.
        if ($validator->failed()) return;

        // We only want to validate this field if the `token` rules were successful.
        // The `ValidExchange` injects the `product_id` into the request object, making it
        // available here, while IT IS NOT in the rules() method.
        // The `product_id` is known to us only after the `ValidExchange` rule is evaluated!
        Validator::make($this->input(), [
            'product_id' => ['required', new NoUnusedCards(auth()->id())]
        ])->validate();
    });
}

As the comment in the code says, the ValidExchange rule performs an API call to some external service, which returns an id that can be used to find a row in the database locally, from which we can get the product_id and inject it into the request object as follows:

request()->merge(['product_id' => $card->product_id]);

, as it too needs to be validated.

This works but doesn't feel right. Is there a cleaner way to do this or another approach that I might be missing? Thank you :)

0 likes
8 replies
Snapey's avatar

restrict validation to checking the inputs (form or api) and check the product_id in the controller or otherwise.?

uhajzeraj's avatar

@snapey Validating the product_id in the controller does work, but I feel that it kind of defeats the purpose of having a form request class. I would like the entire validation process to be done in the form request, and only if it succeeds, continue in the controller.

fylzero's avatar

@uhajzeraj I don't really care where this lives. You could argue that both ways as to whether this is truly validation or logic.

That said. What part of how you're doing this "doesn't feel right"?

The answer to ugliness is comments and refactoring. Rather than just say, this doesn't seem right... try to answer why it doesn't seem right. If you can do that it will help assist answering the real question.

I personally would just ship it as you have it and comment/document it.

uhajzeraj's avatar

@fylzero It doesn't feel right as I am doubtful if this case should be handled like this. Not entirely understanding what's going on in the withValidator method kind of adds to that feeling.

uhajzeraj's avatar

@willvincent it must always be present if the token validation rules pass. With sometimes it won't validate, as it is not in the request object yet.

Top-Master's avatar

Since Laravel 5,6 we can try:

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 in array right after the fields it is checking.

See also related StackOverflow post

Please or to participate in this conversation.