Reputation: 368
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
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
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
Reputation: 6700
Unfortunately, I couldn't find one.
To meet your needs, below are the steps I took.
The field under validation must be present and not empty only when any of the other specified fields are not present.
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