Haris
Haris

Reputation: 368

Laravel validation rule for either of two fields required but both should not be present

Is there a Laravel validation rule for when either of two fields are required but both should not be present at the same time.

Eg. Mobile number and email, either of them should be present but not both.

Upvotes: 2

Views: 4851

Answers (3)

antja0
antja0

Reputation: 180

You can also achieve this by combining required_without and prohibited_unless.

For example:

    [
        'email' => 'required_without:name|prohibited_unless:name,""'
        'name' => 'required_without:email|prohibited_unless:email,""'
    ]

Note: prohibited_unless is fairly new, available since Laravel 8

Upvotes: 1

J. Robinson
J. Robinson

Reputation: 929

I've updated this code a little bit to ensure that things are not counted if they are null. A request can have a parameter, but that parameter be null, and the rule will still fire as invalid even though one of the two fields provided has nothing in it.

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class OneOf implements Rule
{
    public $oneOf = [];
    public $request = null;
    public $message = "";

    /**
     * Create a new rule instance.
     *
     * @return void
     */
    public function __construct(Request $request, array $oneOf, string $message = "")
    {
        $this->oneOf = $oneOf;
        $this->request = $request;
        $this->message = $message;
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param string $attribute
     * @param mixed $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        $count = 0;
        foreach ($this->oneOf as $param) {
            if($this->request->has($param) && $this->request->$param != null){
                $count++;
            }
        }
        return count($this->oneOf) && ($count === 1) ? true : false;

    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        $i = 1;
        $length = count($this->oneOf);
        $fieldNames = null;
        foreach($this->oneOf as $fieldName) {
            if($i < $length) {
                $fieldNames .= Str::replace('_', ' ', Str::title($fieldName)) . ', ';
            } else {
                $fieldNames .= Str::replace('_', ' ', Str::title($fieldName));
            }
            $i++;
        }
        return  strlen(trim($this->message)) ? $this->message : "Only one of these fields should be used: $fieldNames.";
    }
}

Upvotes: 0

steven7mwesigwa
steven7mwesigwa

Reputation: 6700

Unfortunately, I couldn't find one.

To meet your needs, below are the steps I took.

  1. Laravel has a validation rule for the case of making one request param required if the others aren't present.

required_without:foo,bar,...

The field under validation must be present and not empty only when any of the other specified fields are not present.

  1. For the case of achieving the, not both case, I had to come up with a custom validation rule.

STEPS

2a. Open your terminal and run the command to generate a custom validation rule. php artisan make:rule OneOf

Here OneOf is my validation rule name. Name it anything you want. (use PascalCase).

2b. Open the generated file add your logic.

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use Illuminate\Http\Request;

class OneOf implements Rule
{
    public $oneOf = [];
    public $request = null;
    public $message = "";

    /**
     * Create a new rule instance.
     *
     * @return void
     */
    public function __construct(Request $request, array $oneOf, string $message = "")
    {
    $this->oneOf = $oneOf;
    $this->request = $request;
    $this->message = $message;
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param string $attribute
     * @param mixed $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
    $count = 0;
    foreach ($this->oneOf as $param) {
        if($this->request->has($param)){
        $count++;
        }
    }
    return count($this->oneOf) && ($count === 1) ? true : false;

    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
    $json_encodedList = json_encode($this->oneOf);

    return  strlen(trim($this->message)) ? $this->message : "Please insert one of $json_encodedList.";
    }


}

2c. In your controller, make use of the created custom rule.

<?php

namespace App\Http\Controllers\Employee;

use App\Http\Controllers\Controller;
use App\Rules\OneOf;
use Illuminate\Http\Request;


/**
 * Class Employee
 * @package App\Http\Controllers\Employee
 */
class Employee extends Controller
{

    public function store(Request $request)
    {

    $request->validate([
        "email" => ["required_without:phone_number", new OneOf($request, ["email", "phone_number"])],

        "phone_number" => ["required_without:email", new OneOf($request, ["email", "phone_number"])],

    ]);
    
    // ...Custom project specific logic here

    return response()->json([]);
    }
}

You can supply a custom message if you wish.

You can also apply it to more than two request params.

Upvotes: 2

Related Questions