3

I'm trying to create a validator that require at least one of three input.

I tried this

protected function validateFundingSource (): array
{
    return request()->validate([
       'title'       => 'required',
       'description' => 'required',
       'national'       => 'nullable',
       'province'       => Rule::requiredIf(!request('national')),
       'url'            => [
           'required_without_all:phone,email',
           'active_url'
       ],
       'phone'          => [
           'required_without_all:url,email|regex:/^(\+\s?)?1?\-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}(?: *#(\d+))?\s*$/im'
       ],
       'email' => [
           'required_without_all:url,phone|email:rfc,dns'
       ],
       'categories' => 'exists:categories,id'
   ]);
}

But it was was forcing only the first field (url). So I tried with Complex Conditional Validation.

protected function validateFundingSource ()
{

    $v = Validator::make(request()->all(), [
            'title'       => 'required',
            'description' => 'required',
            'national'       => 'nullable',
            'categories'     => 'exists:categories,id',
    ]);

    $v->sometimes('province', 'required', function ($input) {
        return ($input->national === null) ;
    });

    $v->sometimes('url', 'required|active_url', function ($input) {
        return (($input->phone === null) && ($input->email === null));
    });

    $v->sometimes('phone', 'required|regex:/^(\+\s?)?1?\-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}(?: *#(\d+))?\s*$/im', function ($input) {
        return (($input->url === null) && ($input->email === null));
    });

    $v->sometimes('email', 'required|email:rfc,dns', function ($input) {
        return (($input->url === null) && ($input->phone === null));
    });

    return $v;
}

But still no luck... Now it's never required I can submit all three empty field and it's working...

Any clues to help me please ?

Thank you !

1
  • Hello do you solve you problem now? Commented Mar 11, 2020 at 1:38

2 Answers 2

2

You code is working fine. you just forget to check if validate pass or not. because when you use Validator::make you need to manually check it. for request()->validate laravel will do it for you. inside your validateFundingSource () function just check it pass validate or not before return like this:

private function validateFundingSource () {
        $v = Validator::make(request()->all(), [
                'title'       => 'required',
                'description' => 'required',
                'national'       => 'nullable',
                'categories'     => 'exists:categories,id',
        ]);

        $v->sometimes('province', 'required', function ($input) {
            return ($input->national === null) ;
        });

        $v->sometimes('url', 'required|active_url', function ($input) {
            return (($input->phone === null) && ($input->email === null));
        });

        $v->sometimes('phone', 'required|regex:/^(\+\s?)?1?\-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}(?: *#(\d+))?\s*$/im', function ($input) {
            return (($input->url === null) && ($input->email === null));
        });

        $v->sometimes('email', 'required|email:rfc,dns', function ($input) {
            return (($input->url === null) && ($input->phone === null));
        });

        // check if validae failed
        if($v->fails()) {
            dd('fail', $v); // do something when it failed
        }
    }

also sorry for my bad English & hope it help

Sign up to request clarification or add additional context in comments.

2 Comments

Oooooh ! That's why 🤦‍♂️ ! I end up using form request validation, but I will keep that in mind for my future project I was not aware of that ! You're english may not be perfect, but mine is not perfect either 🤐 I'm french, but it still fantastic that we can talk on a common language ! 😆 Thanks for your time, and continue to answer ppl with that attitude, I really like that, it's being a long time since I posted because answer was sometime really harsh, and I get to fantastic answer ! Thank you
glad it hear. cheer
2

If you're looking for "at least one of" url, phone, or email then you want to use required_without. This rule means the field is required when any of the specified fields are missing; required_without_all means it's required when all of the specified fields are missing.

You are also confusing rule syntax, you must use either array or pipe-delimited string syntax, not both at once.

You may want to improve your phone number regex as well; + -. (000-111.9999 #8 is not a great phone number, but would pass your validation. I'd suggest sanitizing your value to remove everything except digits and a leading +, then using a better pattern on what's left.

And, it's just a cosmetic change but you can replace Rule::requiredIf(!request('national')), with a simple required_if rule like the others.

Changing to a form request validation, this would look like:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreFundingsource extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Prepare the data for validation.
     *
     * @return void
     */
    protected function prepareForValidation()
    {
        $phone = preg_replace("/[^0-9]/", "", $this->phone);
        if (strpos($this->phone, "+") === 0) {
            $phone = "+$phone";
        }
        $this->merge(["phone"=>$phone]);
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
           'title'       => ['required'],
           'description' => ['required'],
           'national'    => ['nullable'],
           'province'    => ['required_if,national,'],
           'categories'  => ['exists:categories,id']
           'url'         => [
               'required_without:phone,email',
               'active_url'
           ],
           'phone'       => [
               'required_without:url,email',
               'regex:/^\+?1?[2-9][0-9]{5,14}$/'
           ],
           'email'       => [
               'required_without:url,phone',
               'email:rfc,dns'
           ],
       ];
    }
}

3 Comments

That's what I ended up doing a form request validation ! Thank you! Just so you know, this required_if will not work, since I want it required if national is null. Will definitively test your regex to see if it fit my needs for Canadian phone number. Thanks again for your time.
Oh, also, array and pipe has I understood are two different approach, pipe is for OR and array for AND relation.
The second parameter to required_if is the value. Note the trailing comma in my code, meaning no value. A pipe-delimited string is just another way of specifying multiple rules. "required|email" is the same as ["required", "email"], and you can use either one. You were using ["required|email"] which is not correct.

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.